Rust是一种现代系统级编程语言,在设计时考虑了安全性,此外,它提供零成本抽象、泛型、功能特性等等。但是通常对于开发者来说,Rust上手门槛较难,而它究竟难在了哪里?此外,它还具备哪些优势以及缺点?接下来,本文将从语言、工具、库等多个角度一一探索Rust中的奥秘。
作者 | Brenden Matthews
译者 | 姜松浩
责编 | 屠敏
出品 | CSDN(ID:CSDNNews)
以下为译文:
这篇文章是关于我通过解决Twitch上的每个CtCI问题来学习Rust的经验(https://github.com/brndnmtthws/cracking-the-coding-interview-rust),这是一个未完成的项目。
最近,我开始努力学习Rust,在此我想分享一些心得体会。
近日,我在Rust中编写了一些小程序,并且在阅读了一半的“Programming Rust”书籍之后发现,我是真的不了解Rust。基于此,我想出一个很好的方法来了解这门语言,那就是尝试去解决“Cracking the Coding Interview”一书中的所有189个问题。我不仅会用Rust解决它们,而且还决定在Twitch上直播。对于提供技术讲座,或者在观众面前编码,我并不陌生,但尝试学习一种编程语言,并解释我所做的——让全世界都看到——对我来说是一种新鲜的东西。
事情开始有点不顺利:技术问题、流问题、工具问题,而且我一开始很难理解内存范式。我试着这样做,同时也对人们解释我做了什么,这是个…棘手的问题。
我花了大约8个小时来实现一个链接列表:我记录了的两个4小时的流,试图弄清楚如何正确使用Rc,RefCell和Box。有一段时间我觉得我只是在敲击键盘尝试随机组合,直到出现问题。令人惊奇的是,人们都在观看。我一定是做对了。
离线看书一段时间后(按照一本非常有用的书“Learning Rust With Entirely Too Many Linked Lists”),我开始理解了这些概念。完成我的链接链表实现后,事情变得容易了许多。
我现在已经读到了那本书的第4章,我觉得我已经迈出了Rust的第一步。Rust一旦编译起来就会让人感觉自然、高效,并且非常令人满意。Rust是强类型的,并提供优秀的编译器消息:如果你学会了使用编译器,除非代码存在逻辑缺陷否则你的代码就能得以运行。
Rust的一个可爱特性是编译器有很大的帮助。例如,C ++代码的编译器消息非常难以理解。虽然Clang已经在其错误消息方面取得了巨大进步,但Rust的编译器则是另一个更有帮助的工具。
我将总结一些我到目前为止的发现。这是基于我最初的反应,我承认我对Rust的专业知识很匮乏,但是对于其他人来说,看看他们的学习经历与我的相比较,可能仍然会觉得很有趣。我遗憾地承认我没有彻底研究下面的每个问题,所以我可能有过时的或不准确的信息。
语言:好的方面
首先,感谢Rust团队以及为项目做出贡献的每个人。这是我有史以来我所拥有的最有趣的编程语言学习经历之一。我不知道Rust是否会像其他一些语言一样吸引开发人员的注意力,但我认为它将继续存在。详细情况如下:
Rust代码相当容易阅读,并且不会受到诸如C++、Scala之类的语言难以解析的语法的影响。它似乎拥有我所期望的功能,而挑战仅仅在于确定调用哪个函数。
像具有map(),filter(),find()等功能特性是一种乐趣。定义高阶函数并将闭包传递给它们是轻而易举的。它不会像Ruby这样的语言一样很容易的进行函数式的编程,但它却很接近。事实上,对于一种与C / C ++相当的语言而言,它是那么容易实现,这多么令人兴奋。
Rust会强迫你仔细思考内存分配,因为你别无选择。到最后,这就意味着混乱的代码难以编写,而良好的代码却很容易编写。这些抽象也会直接映射到编写安全的并发代码。
Rust的零成本抽象的方式,会使得不会增加开销的情况下编写好的代码变得容易。这样的特性提供了没有性能损失的现代编程抽象。
Rust代码是安全的(前提是你不使用unsafe关键字,或者调用不安全的C库)
Rust的Result和Option提供了一种通过返回可能值或可能包含值的变量的函数处理的好方法。 而C,C ++甚至Java中的常见模式是函数在没有任何返回时返回空指针。在大多数情况下,当这种返回空指针的处理方式的发生意外情况时,会导致一些函数遇到麻烦。
语言:坏的方面
我发现有时需要unwrap(),as_ref()和borrow()这些有点冗长。我希望有一些语法糖来减少我必须以不同模式将这些调用链接在一起的次数。我发现自己经常编写类似于tooption.as_ref().unwrap().borrow()的代码,这让我感觉很烦。
为了能够在合理的时间内编译代码,编译器需要做一些权衡。因此,在某些情况下,rustc无法推断类型,或者需要一些人工帮助才能编译代码。对我来说,我发现有时很难弄清楚编译器需要什么,以及为什么它不能为我弄清楚。
有些事情偶尔会觉得过于冗长。例如,在str和String之间进行转换,或者将引用而不是值传递给函数,这似乎是编译器可以为我解决的事情。我确信这会有一个很好的理由解释为什么它是这样的,但偶尔也会感觉rustc太正确了。
需要处理每个函数的每个结果都很是好的;这意味着程序员必须考虑每个函数调用发生了什么。有时候感觉很乏味。运算符?可以减少一些冗长,但是没有很好的通用方法来处理故障类型。创造像failure和error-chain这样会使处理故障更容易,但是你仍然需要为可能发生的每种类型的错误明确定义一个案例。
语言:丑的方面
宏:wtf?与其他语言相比,Rust宏感觉就像是左转弯。公平地说,我还没能深入理解它们,但是它们感觉不太合适,就像某种奇怪的栓接附件,这种附件是在语言被设计出来之后受到Perl的启发才出现的。我最终会花一些时间来理解它们,但现在我想避免它们。就像躲避瘟疫一样。
工具:好的方面
Rust提供了不错的工具,并进行了IDE集成就像VSCode集成了RLS。 RLS支持动态链接、代码完成、语法检查和格式设置。
Cargo是Rust强大的包管理器:如果你尝试过Rust,你将会对它非常的熟悉。在大多数情况下,能够用Cargo进行工作是一个愉快的事。已经有大量的Cargo插件提供了像代码覆盖这样的附加功能。
Cargo也是一个构建系统,它可用于运行单元和集成测试。配置你的构建和依赖关系是一个简单的过程,因为它有一些声明性的TOML 语法。
Cargo与crates.io进行了集成,crates.io是开源Rust项目的权威来源。就像PyPi或RubyGems一样,你会发现几乎所有其他的Rust包都托管在crates.io上。
rustup是管理Rust安装的首选工具。你可以选择稳定版、测试版或夜间频道,并安装所有先前版本的特定版本。它还允许你安装clippy和rustfmt等组件。
如果你是像我这样的完美主义者,那么clippy是一个必不可少的工具。它将帮助你学习Rust解决问题方法,它可以捕获许多你可能不会注意到的常见错误。对我来说,当我知道解决问题的方法时,但我不知道正确的方法的时候,clippy很有帮助,。
rustfmt是Rust的固定代码格式的程序。在我看来,固定的格式才是最好的方式。当一切都遵循相同的标准时,就没有了关于代码格式的争论。
sccache是一个编译器缓存,它可以通过减少编译时间来加快速度。但是-请注意-sccache不能与RLS一起使用,所以你无法在IDE中使用它。
工具:坏的方面
好了,在我继续讨论Rust的问题之前,我们都应该承认这是一项正在推进中的工作。 虽然Rust工具已经发展得很快了,但我认为还有很长的路要走。下面我将重点介绍一些需要改进的事项:
编译感觉很慢。它不仅速度慢,而且我发现我经常需要重新编译包。我理解这种必要性,但有时仍然很烦人。 sccache虽然有一定帮助,但编译仍然感觉很慢。有一些方法可以缓解这种情况,例如使用cargo check 来取代cargo build。
RLS使用Racer完成代码,我发现它最多只能命中或错过(至少在VScode中是这样的)。我希望完成的函数通常不存在,不存在的函数显示为完成选项。我没有做彻底的分析,但这些建议似乎只有75%的时间是正确的。造成这种情况的原因可能仅仅是由于RLS的缓慢。
No REPL:这可能不公平,因为C ++都没有像样的REPL,但是现在很多语言都带有REPL。关于这一点,GitHub上有一个未解决的问题。一个好的REPL不是必需的,但会有所帮助。
工具:丑的方面
RLS缓慢,很厚重还有经常崩溃。对我来说,至少,我发现我经常需要在VSCode中重启RLS。 RLS是一个很棒的工具,但它确实让人觉得感觉它充其量只是一个测试版本。我发现自己不得不停下来并且等待RLS赶上来,这样我可以确保我没有编写错误的代码。有时我认为最好简单地禁用RLS,编写代码,然后尝试编译它,就像过去我在Vim中完成所有编码一样。基本上RLS就像变成了一个多余的拐杖,更多的是分心。
库:好的方面
Rust生态系统中出现了大量可用的库。似乎有一个热潮要冲出去实现所有Rust的库,并让你的名字在Rust历史中永存。你可以在crates.io或GitHub上找到你所期望的大部分内容。我常常惊讶于每次搜索都会出现2或3种不同的实现。
我使用的大多数库能够按预期工作,许多库甚至超出了预期。这些库是与替代方案,也就是那些不起作用的库的细微而重要的区别。
库:坏的方面
虽然有很多库,但我发现很多库都是不完整的,不成熟的或完全被遗弃的。 Rust社区似乎还处于早期阶段,但它每天都在不断改进。
有时会有太多选择。例如,我想使用日志库,我发现有很多选项可供选择。有很多选择很好,但对于这样的事情,我只想被告诉应该使用哪个。 Java生态系统有类似的问题,例如java.util.logging,log4j,logback,log4j2,slf4j和tinylog。到目前为止,我仍然不知道对我而言哪个Java日志库是正确的选择。对于Rust,我决定使用env_logger,仅仅因为它是列表中的第一个选项。
虽然没有Node.js生态系统那么糟糕,但每个库的依赖关系列表已经变得很长。我写了一个名为LabHub的小型GitHub机器人,我仍然对有多少依赖项感到惊讶(如果你想知道的话,可以有181个)。对我来说,这表明碎片和重复,这可以通过将一些广泛需要的功能慢慢地
库:丑的方面
我注意到,除了一个相对简单的应用程序的依赖项列表外,我还多次编译相同库的不同版本。我认为Cargo为了保持向后兼容性试正图变得聪明;它正在根据语义版本来猜测应该包含哪些库。令我担心的是,当你拥有一个依赖于某个其他库的古老的、破损的版本的库,同时这个库还有漏洞,这时会发生什么。我毫不怀疑Rust的作者已经考虑过这些问题了,但它仍然看起来很奇怪。公平地说,处理依赖项间的依赖关系是一个非常棘手的问题。值得庆幸的是,有一个Cargo的树工具可以帮助对这些东西进行排序,然后你可以强制一个依赖来升级它的依赖项。
最后的想法
Rust是一门很棒的语言。如果编程是你热衷的事情,那么就请试一试吧。我希望你能够喜欢它。
原文:https://hackernoon.com/programming-in-rust-the-good-the-bad-the-ugly-d06f8d8b773
本文为 CSDN 翻译,如需转载,请注明来源出处。作者独立观点,不代表 CSDN 立场。
热 文 推 荐
☞ 腾讯回应多闪弹窗事件;京东要求员工梳理亲戚同学关系;雷军董明珠十亿赌局胜负已定 | 极客头条
☞ GitHub 近两万 Star!深度学习 500 问带你入门人工智能!| 技术头条
☞大学生利用漏洞“骗走”京东110万, 中心化白条的漏洞, 区块链能否补得上?
☞1/10个iPhone Xs = 英伟达最便宜AI计算机,这是唯一的“核弹”?
☞曝光!月薪 5 万的程序员面试题:73% 人都做错,你敢试吗?
System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!\n");
cout << "点个在看吧!" << endl;
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在看吧!"
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。