Experiencia en la conversión de código C # a código Rust

Formulación del problema



El código C # debe traducirse al código Rust. Más precisamente, se requiere un procedimiento de traducción de este tipo (el desarrollo continúa en C #) para que en cualquier momento pueda obtener un código que funcione en Rust. Resolví este problema para Java, Python, JavaScript y PHP escribiendo un convertidor de C # a estos lenguajes. El concepto de tal conversión se describió en el artículo de UniSharping hace un par de años. Estaba desarrollando este convertidor para traducir el código de mi proyecto Pullenti SDK (análisis de texto lingüístico). Y pensé: ¿por qué no probar Rust? Sí, escuché diferentes respuestas de que el lenguaje es inusual, etc., pero esto no es un intento de tortura ... Además, uno de los clientes tiene un grupo de programadores que escriben con entusiasmo.



Debo decir de inmediato que no funcionó del todo, como para otros idiomas, no hubo suficiente fuerza. Quizás vuelva a este problema. Pasé un mes y medio peleando conmigo mismo y con el idioma, logramos llevar el convertidor al punto que el bloque morfológico comenzó a traducirse e incluso a compilar (y por ende a trabajar) en Rust. Por supuesto, durante este tiempo, el módulo de morfología podría escribirse desde cero, pero detrás de él había alrededor de 500 clases de C # más, creadas y depuradas durante casi 10 años, y no es tan fácil reescribirlas. En este artículo quiero compartir mis impresiones sobre el lenguaje Rust, así como describir las técnicas que utilicé para convertir.



Impresión del lenguaje Rust



Dicen que el maestro no busca caminos fáciles. Esto se aplica completamente a Rust, ya que mucho de lo simple y familiar en otros lenguajes se vuelve complejo, mientras que lo complejo no se vuelve simple. Pareces encontrarte en otro mundo con una lógica absurda a primera vista, que está lejos de ser inmediatamente comprensible después de dominar los conceptos básicos. No importa lo que hayas escrito hasta ahora: C ++, Java, Python, etc., pero cuando resulta que después de agregar un objeto a la lista, no puedes usar :, it = new ...(); list.add(it); it.val = ...pero puedes it = new ...(); it.val = ...; list.add(it);hacerlo así :, entonces es desalentador. O, para implementar referencias cruzadas entre objetos de la clase Foo, necesita usar la construcción Option<Rc<RefCell<Foo>>>y para acceder al campo val de esta clase, llame 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