ExperiĂŞncia de conversĂŁo de cĂłdigo C # em cĂłdigo Rust

Formulação do problema



O código C # precisa ser traduzido em código Rust. Mais precisamente, esse procedimento de tradução é necessário (o desenvolvimento continua em C #) para que a qualquer momento você possa obter um código funcional em Rust. Resolvi esse problema para Java, Python, JavaScript e PHP escrevendo um conversor de C # para essas linguagens. O conceito de tal conversão foi delineado no artigo UniSharping alguns anos atrás. Eu estava desenvolvendo este conversor para traduzir o código do meu projeto Pullenti SDK (análise linguística de texto). E pensei: por que não experimentar o Rust? Sim, ouvi diferentes respostas de que a linguagem é incomum, etc., mas esta não é uma tentativa de tortura ... Além disso, um dos clientes tem um grupo de programadores escrevendo com entusiasmo.



Devo dizer desde já que não funcionou por completo, como para outras línguas, - não havia força suficiente. Talvez eu volte a este problema. Passamos um mês e meio lutando comigo e com o idioma, conseguimos trazer o conversor a tal ponto que o bloco morfológico passou a traduzir e até compilar (e portanto funcionar) em Rust. É claro que, durante esse tempo, o módulo de morfologia poderia ser escrito do zero, mas por trás dele havia cerca de 500 outras classes C #, criadas e depuradas por quase 10 anos, e não é tão fácil reescrevê-las. Neste artigo, quero compartilhar minhas impressões sobre a linguagem Rust, bem como descrever as técnicas que usei para converter.



ImpressĂŁo da linguagem Rust



Dizem que o mestre não procura caminhos fáceis. Isso se aplica totalmente ao Rust, já que muito do que é simples e familiar em outras linguagens se torna complexo, enquanto o complexo não se torna simples. Você parece se encontrar em outro mundo com uma lógica absurda à primeira vista, que se torna nada imediatamente compreensível depois de dominar os conceitos básicos. Não importa o que você escreveu até agora: C ++, Java, Python, etc., mas quando descobrir que depois de adicionar um objeto à lista, você não pode usar :, it = new ...(); list.add(it); it.val = ...mas pode it = new ...(); it.val = ...; list.add(it);fazer assim : , é desanimador. Ou, para implementar referências cruzadas entre objetos da classe Foo, você precisa usar a construção Option<Rc<RefCell<Foo>>>e, para acessar o campo val dessa classe, chamar foo.unwrap().borrow().val.



: , , . Rust , . ( Rust 20-). ? .



Rust — C# 2 . , , ( ). , , Rust C/C++ . . , Rust /C++, , ...



Rust , "" 50 , - , . 80- ( ), , . . - , trait- ( interface Java C#), - , . , , ? , Rust , .





Rust — (heap). — new/delete. \++, , . , delete . , . , , new. : Java, C#, Python, JavaScritp, PHP .



Rust , , , . , { ... let x = Foo {... }; ... }, . — - . , , (mut) , , . , , C# buf stream.Read(buf, 0, buf.Length) , buf mut-, buf . : int len = buf.Length; stream.Read(buf, 0, len);.



, C# Rust. , — .



C#



, SDK C# Java. , , . , , — C# , . . UniSharping. , . , C#, . , Java yield, C# — !

C# . Java DLL, Java . Python , , . JavaScript long ( byte, short, int, float, double, long- ), SDK C# long int, . PHP string utf-8 i- . , mb_, - . Rust , -.



C#, - : #if JAVA || PYTHON… #else… #endif — .



— . , , ? . Rust , .



, .





C#, , , — Rust , . for(...; ...; ...) while — . byte, int, float . , . .



T C# Rust : T ( ), &T ( ) &mut T ( ). , C# — C# , Rust , .



var obj = new T(); //    T
FuncNotModif(obj); //    
FuncModif(obj); //   
list.Add(obj); //    List<T>
var obj2 = obj; //      
var obj3 = obj; //      


Rust:



let obj = T { }; //    T (   )
func_not_modif(&obj); //   ,   obj   
func_modif(&mut obj); //    
list.push(&obj); //       Vec<&T>,   obj 
let obj2 : &T = &obj; //    
let obj3 : T = obj; //      obj3,  obj  obj2   


, Rust , : =, return &. , .



C# , : T, &T &mut T? , :



&T &mut T , ( ), , property { get; set; } &T, — T. C# /*&*/ /*&mut*/ . , List<T/*&*/>, , List<T/*&*/>/*&*/.



: , , , . , . — , . , .





Rust utf-8 ( PHP). , 2 . C#, Java . char 16 ( 8 , ++ 8 16), Rust. Unicode 32-, 64-? . — , 7- ASCII.



str[i]. ?



— (struct Rust), , string.



#[derive(Clone)]
pub struct NString {
    pub chars : Vec<char>,
    pub string : String,
    _is_null : bool
}
impl NString {
    pub fn from_string(s : &String) -> NString {
        NString { chars : s.chars().collect(), string : s.clone(), _is_null : false }
    }
    pub fn from_str(s : &str) -> NString {
        NString { chars : s.chars().collect(), string : s.to_string(), _is_null : false }
    }
    pub fn from_chars(s : &Vec<char>) -> NString {
        NString { chars : s.clone(), string : s.into_iter().collect(), _is_null : false }
    }
...
}


, chars, — String. C# , Rust. , Substring(int start, int len) :



    pub fn substring(&self, pos : i32, len : i32) -> NString {
        let length : i32 = if len <= 0 { self.chars.len() as i32 - pos } else { len };
        let sub = self.chars[pos as usize .. (pos + length) as usize].to_vec();
        NString::from_chars(&sub)
    }


- , &STR_HELLO STR_HELLO.clone() :



static STR_HELLO : Lazy<NString> = Lazy::new(|| { NString::from_str("Hello!") }); 
use once_cell::sync::Lazy;




, Rust , . , , C# , Vec HashMap . 3 : , &T T. array[] Rust , List.

Object



Rust null object, . , C# "" object "" — Rust . , .



object, object. , , , /*=*/ object.



            object/*=ObjValue*/ obj = "Hello";
            Console.WriteLine(obj);
            obj = 10;
            if (obj is int)
            {
                int ii = (int)obj;
                Console.WriteLine(ii);
            }
            obj = cnt.First; //   Item
            if(obj is Item)
                Console.WriteLine((obj as Item).Str);

#if RUST  //  C#   
        //RUST object_class
        class ObjValue
        {
            public string Str;
            public int Int;
            public Item/*&*/ Item;
        }
#endif


, object int, string Item, , Item — .



ObjValue, C#, .



        let mut obj : ObjValue = ObjValue::from_str_(STR_HELLO.clone());
        println!("{}", &obj.to_nstring());
        obj = ObjValue::from_int(10);
        if obj.is_class("i32") {
            let mut ii : i32 = obj.int;
            println!("{}", &NString::from_string(&ii.to_string()));
        }
        obj = ObjValue::from_item(Some(Rc::clone(cnt.borrow().get_first().as_ref().unwrap())));
        if obj.is_class("Item") {
            println!("{}", obj.item.as_ref().unwrap().borrow().get_str());
        }

pub struct ObjValue {
    pub str_ : NString, 
    pub int : i32, 
    pub item : Option<Rc<RefCell<dyn IItem>>>, 
    _typ : &'static str
}

impl ObjValue {
    pub fn from_str_(val : NString) -> ObjValue {
        ObjValue { str_ : val, int : 0, item : None, _typ : "NString" }
    }
    pub fn from_int(val : i32) -> ObjValue {
        ObjValue { str_ : NString::null(), int : val, item : None, _typ : "i32" }
    }
    pub fn from_item(val : Option<Rc<RefCell<dyn IItem>>>) -> ObjValue {
        ObjValue { str_ : NString::null(), int : 0, item : val, _typ : "Item" }
    }
    pub fn null() -> ObjValue {
        ObjValue { str_ : NString::null(), int : 0, item : None, _typ : "" }
    }
    pub fn is_null(&self) -> bool { self._typ.len() == 0 }
    pub fn is_class(&self, typ : &str) -> bool { self._typ == typ }
    pub fn to_nstring(&self) -> NString {
        if self._typ == "NString" { return self.str_.clone(); }
        if self._typ == "i32" { return NString::from_string(&self.int.to_string()); }
        if self._typ == "Item" { return NString::from_str("Option<Rc<RefCell<dyn IItem>>>"); }
        NString::null()
    }
}


, . ! , .

: obj = cnt.First Rust obj = ObjValue::from_item(Some(Rc::clone(cnt.borrow().get_first().as_ref().unwrap()))). , ? , ! , , .





C# Rust struct, — trait. , — . . C# , : . .



- - , .



. A, , trait, , get set ( property). struct B A (struct B { base : A, }), B trait A. A, self.base.x.

.



    //RUST RefCell
    class Item
    {
        public Item(int val) { Val = val; }
        public int Val { get; set; }
        public string Str;
        public Item/*&*/ Prev { get; set; }
        public Item/*&*/ Next { get; set; }
        public virtual void Inc() { Val += 1; }
    }
    //RUST RefCell
    class ItemChild : Item
    {
        public ItemChild(int val) : base(val) { }
        public override void Inc() { Val *= 2; }
    }


( ). trait.



pub trait IItem {
    fn get_val(&self) -> i32;
    fn set_val(&mut self, value : i32) -> i32;
    fn get_str(&self) -> &NString;
    fn set_str(&mut self, value : NString) -> &NString;
    fn get_prev(&self) -> &Option<Rc<RefCell<dyn IItem>>>;
    fn set_prev(&mut self, value : Option<Rc<RefCell<dyn IItem>>>) -> &Option<Rc<RefCell<dyn IItem>>>;
    fn get_next(&self) -> &Option<Rc<RefCell<dyn IItem>>>;
    fn set_next(&mut self, value : Option<Rc<RefCell<dyn IItem>>>) -> &Option<Rc<RefCell<dyn IItem>>>;
    fn inc(&mut self);
    fn get_base_class(&self) -> &dyn IItem;
    fn is_class(&self, name : &str) -> bool;
    fn as_item(&self) -> &dyn IItem;
    fn as_mut_item(&mut self) -> &mut dyn IItem;
}


.



pub struct Item {
    pub _val : i32, 
    pub m_str : NString, 
    pub _prev : Option<Rc<RefCell<dyn IItem>>>, 
    pub _next : Option<Rc<RefCell<dyn IItem>>>, 
}

impl IItem for Item {
    fn get_val(&self) -> i32 {
        return self._val;
    }
    fn set_val(&mut self, mut value : i32) -> i32 {
        self._val = value;
        return self._val;
    }
    fn get_prev(&self) -> &Option<Rc<RefCell<dyn IItem>>> {
        return &self._prev;
    }
    fn set_prev(&mut self, mut value : Option<Rc<RefCell<dyn IItem>>>) -> &Option<Rc<RefCell<dyn IItem>>> {
        self._prev = utils::clone_opt_ref(&value);
        return &self._prev;
    }
...
    fn inc(&mut self) {
        self.set_val(self.get_val() + 1);
    }
    fn as_item(&self) -> &dyn IItem { self }
    fn as_mut_item(&mut self) -> &mut dyn IItem { self }
    fn get_base_class(&self) -> &dyn IItem { self }
    fn is_class(&self, name : &str) -> bool { name == "Item" }
}

impl Item {
    pub fn new(mut __val : i32) -> Item {
        let mut self_result = Item {  _val : 0,  _prev : None,  _next : None,  m_str : NString::null() };
        self_result.set_val(__val);
        self_result
    }
}


:



pub struct ItemChild {
    pub base : Item, //   
}
impl IItem for ItemChild {
    fn get_val(&self) -> i32 {
        self.base.get_val()  //      base
    }
    fn set_val(&mut self, value : i32) -> i32 {
        self.base.set_val(value)
    }
    //   -     
    fn inc(&mut self) {
        self.base.set_val(self.get_val() * 2);
    }
    ....
}

impl ItemChild {
    pub fn new(mut __val : i32) -> ItemChild {
        ItemChild {  base : Item::new(__val) };
    }
}


Item ItemChild ITrait, inc() , trait — ! .





&T (lifetime), , , . , : struct A<'a> { ref : &'a Item, ... }. , 'a. . , , lifetime-hell, . , Rust !



: Option<Rc<RefCell<T>>>. . — , . , Option<Weak<RefCell<T>>>. " , , ! — , ..."





, , . SDK , 10% . , , . " " , , C# — Rust . .



, Rust , … , . Rust — , , , - ! !




All Articles