C#コードをRustコードに変換した経験

問題の定式化



C#コードはRustコードに変換する必要があります。より正確には、いつでもRustで機能するコードを取得できるように、このような変換手順が必要です(開発はC#で続行されます)。 C#からこれらの言語へのコンバーターを作成することにより、Java、Python、JavaScript、およびPHPのこの問題を解決しました。このような変換の概念は、数年前のUniSharpingの記事で概説されています。Pullenti SDKプロジェクトのコードを翻訳するためにこのコンバーターを開発していました(言語テキスト分析)。そして私は思った:なぜRustを試してみませんか?はい、言語が変わっているなどとさまざまな回答を聞いていますが、これは拷問の試みではありません...さらに、顧客の1人は熱心に書いているプログラマーのグループを持っています。



他の言語に関しては、これを完全に行うことは不可能であったとすぐに言わなければなりません-十分な力がありませんでした。多分私はこの問題に戻るでしょう。1か月半は私自身と言語との戦いに費やされ、私たちはコンバーターを、形態学的ブロックがRustで翻訳し、さらにはコンパイル(したがって動作)し始めるところまで到達させることができました。もちろん、この間、形態モジュールは最初から作成できましたが、その背後には約500個のC#クラスがあり、ほぼ10年間作成およびデバッグされており、それらを書き換えるのはそれほど簡単ではありません。この記事では、Rust言語についての私の印象を共有し、変換に使用した手法について説明したいと思います。



さびた言葉の印象



マスターは簡単な方法を探していないと言われています。これはRustにも完全に当てはまります。なぜなら、他の言語で単純で馴染みのあるものの多くは複雑になりますが、複雑は単純にはならないからです。あなたは一見ばかげた論理で別の世界にいるように見えます。それは基本的な概念を習得した後すぐに理解できるとはほど遠いものになります。これまでに何を書いたかは関係ありません:C ++、Java、Pythonなどですが、リストにオブジェクトを追加した後、:を使用できないことが判明した場合、次のように使用it = new ...(); list.add(it); it.val = ...できit = new ...(); it.val = ...; list.add(it);ます:、それは落胆します。または、Fooクラスのオブジェクト間の相互参照を実装するには、構文を使用し、Option<Rc<RefCell<Foo>>>このクラスのvalフィールドにアクセスするには、を呼び出す必要があります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