你不能在生锈中做的事情(而是做什么)

2021-05-15 19:17:47

作为Rust SubredDit的主持人,我经常发生关于开发人员试图转移各自语言范例的帖子才能生锈,以及混合结果和不同程度的成功。

在本指南中,我将描述开发人员在将其他语言范例转发时遇到的一些问题,并提出了一些替代解决方案,以帮助您在Rust的限制上工作。

可以说是来自面向对象语言的最遗憾的功能是继承。为什么不生锈让struct从另一个人继承?

你肯定会争辩说,即使在OO世界中,继承人也有一个糟糕的声誉,从业者通常有利于组合。但是,您还可以争辩说,允许类型以不同方式执行方法可能提高性能,因此对于这些特定实例而言是可取的。

界面动物{void tell(); void pet(); void饲料(食品食品);}类Cat {public void tell(){system.out.println(" meow"); public void pet(){system.out.println(" purr");公共空缺饲料(食品食品){system.out.println(" lick");此实现可能太乐观了... Class Lion扩展Cat {public void tell(){system.out.println("咆哮"); }}

特质动物{FN告诉(& self); FN PET(& mut self); Fn Feed(& mut自我,食品:食物);} struct cat; ich ic of tam act {fn tell(& self){println!(" meow"); FN PET(& mut self){println!(" purr"); Fn饲料(& mut自我,食物:食物){println!(" lick"); }}

STRUCT LION;锯割的狮子{FN告诉(& self){println!("咆哮"); } //错误:缺少方法宠物和饲料}

显然,最简单的方式是复制这些方法。是的,复制很糟糕。复杂性也是如此。如果您需要重复使用该代码,请创建一个免费的方法并从Cat和Lion ich ich ich呼叫。

但是等等,你可能会说,那些关于方程的多态性部分呢?这就是它变得复杂的地方。如果OO语言通常会给您动态调度,Rust会让您在静态和动态调度之间进行选择,并且都有其成本和福利。

//静态探索猫=猫; cat.tell();让狮子=狮子; lion.tell(); //通过enumenum anyanimal {cat(猫),狮子(狮子),狮子(狮子),狮子(狮子),鼠标为Anyanimal`留下了Readerlet Animage的练习= [Anyanimal :: Cat(Cat),Anyanimal ::狮子(狮子)];对于动物的动物。Its(){Animal.tell();} //通过"脂肪"指针包括vtablecelet动物= [&猫作为& dyn动物,&狮子& dyn动物];对于动物的动物。ters(){amall.tell();}

注意与垃圾收集的语言不同,每个变量必须在编译时具有单个混凝土类型。此外,对于枚举案例,委派特征的实施是乏味的,但大使等板条箱可以提供帮助。

最后,可以为实现许多其他特征的所有类来实现一个特征,但它需要专业化,这是现在的夜间功能(尽管有一个可用的解决方法,但如果您不提供宏观箱想要写出所有需要的样板)。特征可能很好地彼此继承,尽管它们只规定行为,而不是数据。

来自C ++来源的许多人员将首先想要实现“简单”的双链列表,但快速了解它实际上远非简单。那是因为Rust想要清楚所有权,因此双重链接列表需要相当复杂的指针与参考。

好吧,他们会在注意到这一点失败时,他们将添加选项和框。但是一旦他们试图实现插入,它们就会出现不愉快的惊喜:

ichsl lt; t> MyLinkedList< t> {fn插入(& mut self,value:t){let next_node = self.next_node.take(); self.next_node = some(box :: new(mylinkedlist {value,previous_node:some(box :: new(* self)),//哎哟next_node,})))); }}

当然,借款检查器不会允许这个。价值的所有权完全混乱。框拥有它包含的数据,因此列表中的每个节点都将由列表中的上一个节点和下一个节点拥有。生锈只允许每个数据允许一个所有者,因此这将至少需要RC或ARC工作。但即使这也很快就会变得麻烦,而不是提及参考数量的开销。

幸运的是,您无需编写双链接的列表,因为标准库已包含一个(std :: collections :: linkedlist)。此外,与简单的VEC相比,这将使您提供良好的性能,因此您可能希望相应地衡量。

如果您真的想编写双链接的列表,您可以引用“使用完全太多链接的列表学习生锈”,这可能会帮助您两个写链接的列表,并在此过程中学习很多关于不安全的Rust。

(抛开:单独的清单绝对是盒子的盒子。实际上,Rust编译器包含一个实施。)

同样主要适用于图形结构,尽管您可能需要对处理图数据结构的依赖性。 Petgraph是目前最受欢迎的,提供数据结构和多个图形算法。

当面对自我引用类型的概念时,要问,“谁拥有这个?”再次,这是借用检查者通常不满的所有权故事的皱纹。

当您拥有所有权关系并希望在一个结构中存储所有拥有和拥有的对象时,您将遇到此问题。尝试这个天真,你会有一个糟糕的时间,试图获得工作的生命。

我们只能猜测许多Rustaceans转向不安全的代码,这是微妙的,真的很容易出错。当然,使用普通指针而不是参考将移除您的终身担忧,因为指针携带一生。但是,这是占据手动管理寿命的责任。

幸运的是,有一些箱子采取解决方案并呈现一个安全的界面,例如租赁和_FORT_SELF_CELL板条箱。

来自C和/或C ++的人们 - 或者少于动态语言 - 有时习惯于在整个代码中创建和修改全球状态。例如,一个Redditor咆哮着“这是完全安全的,而且生锈不会让你这样做。”

#include< iostream> int i = 1; int main(){std :: cout

静态I:u32 = 1; fn main(){print!(" {}}" i);我= 2; //<错误:无法变异全球州打印!(" {}}" i);}

许多rustaceans会告诉你,你只是不需要那个国家成为全球性的。当然,在这样一个简单的例子中,这是真的。但对于许多使用情况,您真的需要全局变形状态 - 例如,在某些嵌入式应用程序中。

当然,使用不安全的方式做到这一点。但是在您到达之前,根据您的用例,您可能只想使用互斥锁,而是真正确定。或者,如果只需要突变一次进行初始化,则oncecell或lazy_static将整齐地解决问题。

与此同说,特别是在嵌入式世界中,每个字节计数和资源通常被映射到存储器中,具有可变静态通常是首选解决方案。所以,如果你真的必须这样做,那将是这样的:

static mut data_race_counter:u32 = 1; fn main(){print!(" {}}" data_race_counter); //我索蒙尼发誓,我没有好,也是单线的。不安全{data_race_counter = 2;打印!(" {}",data_race_counter);}

再次,除非你真的需要,否则你不应该这样做。如果你需要询问这是一个好主意,答案是否定的。

这失败,因为数组从未初始化。然后我们尝试将值分配给它,但没有告诉编译器,它甚至不会为我们写在堆栈上的地方。生锈是挑剔的;它将数组与其内容区分开来。此外,它需要在我们读取它们之前初始化。

通过初始化let array = [0usize; 512];我们以双重初始化的成本解决了这个问题,这可能会或可能不会被优化 - 或者,根据类型,甚至可能是不可能的。请参阅“不安全的生锈:如何以及何时(不)使用它”解决方案。

调试生锈应用可能是困难的,特别是当用户遇到难以重现的问题时。如果您有兴趣监控和跟踪生锈应用的性能,自动浮出误差,以及跟踪慢速网络请求和负载时间,请尝试Logrocket。 Logrocket就像Web应用程序的DVR,字面上录制了生锈应用程序上发生的一切。而不是猜测为什么问题发生,而是可以聚合和报告您的应用程序在发生问题时的状态。 Logrocket还监视您的应用程序的性能,报告客户端CPU负载,客户端内存用法等度量标准。