Formulierung des Problems
Der C # -Code muss in Rust-Code übersetzt werden. Genauer gesagt ist ein solches Übersetzungsverfahren erforderlich (die Entwicklung wird in C # fortgesetzt), damit Sie jederzeit einen funktionierenden Code in Rust erhalten können. Ich habe dieses Problem für Java, Python, JavaScript und PHP gelöst, indem ich einen Konverter von C # in diese Sprachen geschrieben habe. Das Konzept einer solchen Konvertierung wurde vor einigen Jahren im UniSharping- Artikel beschrieben . Ich habe diesen Konverter entwickelt, um den Code meines Pullenti SDK-Projekts (linguistische Textanalyse) zu übersetzen. Und ich dachte: Warum nicht Rust probieren? Ja, ich habe verschiedene Antworten gehört, dass die Sprache ungewöhnlich ist usw., aber dies ist kein Folterversuch ... Außerdem hat einer der Kunden eine Gruppe von Programmierern, die begeistert darin schreiben.
Ich muss sofort sagen, dass es nicht vollständig geklappt hat, wie bei anderen Sprachen - es gab nicht genug Kraft. Vielleicht komme ich auf dieses Problem zurück. Eineinhalb Monate lang haben wir mit mir und der Sprache gekämpft. Wir haben es geschafft, den Konverter so weit zu bringen, dass der morphologische Block in Rust zu übersetzen begann und sogar zu kompilieren (und damit zu arbeiten) begann. Während dieser Zeit konnte das Morphologiemodul natürlich von Grund auf neu geschrieben werden, aber dahinter standen etwa 500 weitere C # -Klassen, die fast 10 Jahre lang erstellt und debuggt wurden, und es ist nicht so einfach, sie neu zu schreiben. In diesem Artikel möchte ich meine Eindrücke von der Rust-Sprache teilen und die Techniken beschreiben, die ich zum Konvertieren verwendet habe.
Eindruck der Rust-Sprache
Sie sagen, dass der Meister nicht nach einfachen Wegen sucht. Dies gilt vollständig für Rust, da vieles, was in anderen Sprachen einfach und vertraut ist, komplex wird, während der Komplex nicht einfach wird. Sie scheinen sich in einer anderen Welt mit einer auf den ersten Blick absurden Logik zu befinden, die nach Beherrschung der Grundkonzepte alles andere als sofort verständlich wird. Es spielt keine Rolle, was Sie bisher geschrieben haben: C ++, Java, Python usw., aber wenn sich herausstellt, dass Sie nach dem Hinzufügen eines Objekts zur Liste nicht verwenden können:, it = new ...(); list.add(it); it.val = ...aber Sie können it = new ...(); it.val = ...; list.add(it);es so machen:, es ist entmutigend. Um Querverweise zwischen Objekten der Foo-Klasse zu implementieren, müssen Sie die Konstruktion verwenden Option<Rc<RefCell<Foo>>>und auf das val-Feld dieser Klasse zugreifen 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 — , , , - ! !