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 — , , , - ! !