Rust,是由Mozilla开发的系统编程语言,2015年5月发布1.0版本。
它的目标是,成为一门"安全、并发、实用"的系统编程语言,既要有C/C++的性能,又要有内存安全和线程安全。Rust,通过所有权系统、借用检查器、生命周期等机制,在编译期,就保证了内存安全和线程安全,不需要垃圾回收,也不需要运行时开销。
我第一次接触Rust,是在三年前,那时候Rust刚发布1.0不久。我被它的内存安全、零成本抽象、所有权系统等特性吸引,觉得这是一门很有前途的语言,于是,开始学习和使用Rust。
三年来,我用Rust写了一些项目,有命令行工具,有网络服务,也有一些小的库。踩了很多坑,也有很多收获。从最开始,被借用检查器折磨得死去活来,到后来,慢慢理解了所有权系统,再到现在,能够用Rust,写出比较优雅的代码。这三年,是一个不断学习,不断进步的过程。
今天,我想分享一下,我用了三年Rust,才明白的一些道理。希望能给正在学习Rust,或者准备学习Rust的朋友,一些参考和启发。
一、道理一:Rust的学习曲线,确实很陡,但是,值得
刚学Rust的时候,我最大的感受就是:难。
Rust的语法,虽然和C/C++有点像,但是,它的核心概念,比如所有权、借用、生命周期、trait、模式匹配等,和其他语言,都不一样。特别是所有权系统和借用检查器,是Rust独有的,也是最难理解的。
我记得,最开始写Rust代码的时候,几乎每写几行,就会被编译器报错。而且,错误信息,还特别长,特别难懂。什么"cannot move out of borrowed content"、"lifetime of reference is too short"、"trait bound not satisfied",看得我一头雾水。
那时候,我经常写一个简单的功能,要编译好几十次,才能通过。有时候,一个生命周期的问题,能卡我好几个小时。我甚至一度怀疑,自己是不是太笨了,学不会Rust。
但是,坚持了一段时间之后,我慢慢发现,Rust的难,是有道理的。它的所有权系统,借用检查器,虽然严格,但是,它们是在帮你,避免内存错误,避免并发bug。这些错误,在C/C++中,可能要运行的时候,才能发现,而且,很难调试,很危险。但是,在Rust中,编译期,就能发现,就能避免。
而且,当你理解了所有权系统,理解了借用检查器之后,你会发现,Rust的代码,写起来,其实很有规律,很有美感。编译器,就像一个严格的老师,虽然严厉,但是,它能帮你,写出更安全、更健壮、更优雅的代码。
用了三年Rust,我才明白:Rust的学习曲线,确实很陡,特别是前几个月,会很痛苦。但是,这种痛苦,是值得的。当你跨过了那个门槛,你会发现,Rust,是一门非常优秀的语言,它能让你,写出既高性能,又安全可靠的代码。而且,学习Rust的过程,也能让你,对内存管理、并发编程、系统编程,有更深刻的理解。
所以,如果你正在学Rust,觉得很难,不要放弃,坚持下去。等你跨过了那个门槛,你会发现,一切都是值得的。
二、道理二:所有权系统,不是负担,而是Rust的灵魂
刚学Rust的时候,我觉得,所有权系统,是一个负担,是一个麻烦。它限制了我的自由,让我写代码的时候,束手束脚,不能像在C/C++中那样,随心所欲地操作指针和内存。
但是,用了三年Rust,我才明白:所有权系统,不是负担,而是Rust的灵魂,是Rust最核心、最有价值的设计。
所有权系统,简单来说,就是Rust中,每个值,都有一个唯一的所有者;当所有者离开作用域时,值就会被自动释放;值,可以被移动,也可以被借用,但是,同一时间,只能有一个可变借用,或者多个不可变借用。
这个系统,看起来简单,但是,它从根本上,解决了内存安全的问题。在C/C++中,内存错误,比如,空指针、野指针、悬垂指针、二次释放、内存泄漏,是最常见,也是最危险的bug。这些bug,很难发现,很难调试,而且,可能导致严重的安全漏洞。
但是,在Rust中,通过所有权系统和借用检查器,这些内存错误,在编译期,就被杜绝了。编译器,会检查每个值的所有权和借用,确保,不会出现悬垂指针,不会出现二次释放,不会出现数据竞争。你不需要手动管理内存,也不需要垃圾回收,就能写出内存安全的代码。
而且,所有权系统,不仅保证了内存安全,还保证了线程安全。Rust的类型系统,通过Send和Sync这两个trait,在编译期,就保证了,线程之间,不会出现数据竞争。你可以放心地写并发代码,不需要担心,会出现竞态条件。
用了三年Rust,我越来越觉得,所有权系统,是一个非常优雅、非常聪明的设计。它用一种简单、统一的规则,解决了内存安全和线程安全的问题,而且,没有运行时开销,不需要垃圾回收。这,就是Rust的魅力所在。
所以,学Rust,一定要深入理解所有权系统,理解借用,理解生命周期。这,是Rust的核心,也是Rust的灵魂。只有理解了这些,你才能真正掌握Rust,才能写出地道的Rust代码。
三、道理三:编译器,是你最好的朋友,不是敌人
刚学Rust的时候,我最讨厌的,就是Rust的编译器。它太严格了,太啰嗦了,几乎每写几行代码,就会报错,而且,错误信息,还特别长,特别难懂。那时候,我觉得,编译器,是我的敌人,是在故意刁难我。
但是,用了三年Rust,我才明白:编译器,是你最好的朋友,不是敌人。
Rust的编译器,是我用过的,最友好、最智能的编译器之一。它的错误信息,虽然长,但是,非常详细,非常有用。它不仅告诉你,哪里错了,还告诉你,为什么错了,应该怎么改。很多时候,它甚至会直接给出,修改建议,你照着改,就能通过编译。
比如,如果你忘记了引入一个trait,编译器会告诉你,"this method exists but the following trait bounds were not satisfied",然后,告诉你,需要引入哪个trait。如果你写错了变量名,编译器会告诉你,"cannot find value x in this scope",然后,会猜测,你是不是想写y,给你一个建议。
而且,Rust的编译器,还在不断进步。每一个版本,错误信息,都会更友好,更智能。比如,Rust 2018 edition,就改进了很多错误信息,让它们,更容易理解。
更重要的是,Rust的编译器,是在帮你,避免bug,避免错误。它严格的检查,虽然让你写代码的时候,有点痛苦,但是,它能保证,你的代码,编译通过之后,内存是安全的,线程是安全的,不会出现那些低级的、危险的bug。这,比运行的时候,才发现bug,要好得多。
用了三年Rust,我现在,已经习惯了,和编译器"对话"。我写代码的时候,会先写一个大概的版本,然后,看编译器的报错,根据报错,一步步修改,直到编译通过。这个过程,就像和一个经验丰富的老师,在交流,他会指出你的错误,告诉你怎么改,帮你,写出更安全、更健壮的代码。
所以,学Rust,不要害怕编译器,不要讨厌编译器。要学会,和编译器做朋友,学会,利用编译器的错误信息,来学习,来改进你的代码。编译器,是你最好的老师,最好的朋友。
四、道理四:不要试图,把其他语言的写法,搬到Rust中
刚学Rust的时候,我犯了一个错误,就是,试图把其他语言(比如C++、Python、Java)的写法,搬到Rust中。
比如,我习惯了,在C++中,用指针,用引用,随意地传递对象;习惯了,在Python中,用动态类型,用鸭子类型,灵活地操作对象;习惯了,在Java中,用面向对象,用继承,用接口。但是,当我把这些写法,搬到Rust中的时候,发现,处处碰壁,编译器,总是报错。
那时候,我觉得,Rust,太不灵活了,太限制人了。但是,后来,我慢慢明白,不是Rust不灵活,而是,我在用错误的方式,写Rust代码。Rust,有自己的编程范式,自己的设计哲学,自己的最佳实践。你不能,把其他语言的写法,生搬硬套到Rust中,那样,只会事倍功半。
比如,Rust,虽然支持面向对象,但是,它的面向对象,和Java/C++的面向对象,不一样。Rust,没有继承,只有组合,通过trait,来实现多态。如果你试图,在Rust中,写Java风格的继承体系,你会发现,非常别扭,非常难用。但是,如果你用Rust的方式,用组合,用trait,来设计你的代码,你会发现,非常优雅,非常灵活。
再比如,Rust,虽然支持指针(裸指针),但是,在安全的Rust中,你几乎不需要用裸指针。你应该,用引用,用借用,用智能指针(Box、Rc、Arc等),来管理内存。如果你试图,在Rust中,写C风格的指针操作,你会发现,处处都是unsafe,非常危险,也非常不Rust。
还有,Rust的错误处理,和其他语言,也不一样。Rust,没有异常,而是,用Result和Option,来处理错误和空值。如果你试图,在Rust中,用异常的方式,来处理错误,你会发现,非常别扭。但是,如果你用Rust的方式,用Result,用?运算符,来处理错误,你会发现,非常清晰,非常优雅。
用了三年Rust,我才明白:学Rust,要放下,其他语言的思维定式,要学会,用Rust的方式,来思考,来写代码。Rust,有自己的设计哲学,自己的编程范式,自己的最佳实践。只有理解了这些,用Rust的方式,写Rust代码,你才能,真正发挥Rust的优势,写出优雅、高效、安全的代码。
五、道理五:Rust的生态,还不够成熟,但是,在快速发展
Rust,虽然语言本身,已经比较成熟了,但是,它的生态,和C/C++、Java、Python等老牌语言相比,还不够成熟。
比如,库的数量,虽然增长很快,但是,和Python的PyPI,Java的Maven Central,相比,还是少很多。很多领域,还没有,特别成熟、特别流行的库。比如,GUI库,Rust的GUI库,虽然有几个(比如gtk-rs、imgui-rs、conrod等),但是,都还不够成熟,不够好用,和Qt、Electron等,相比,还有很大的差距。
再比如,工具链,虽然Rust的工具链(cargo、rustc、rustup等),已经非常好了,但是,IDE支持,还不够好。虽然,有JetBrains的Rust插件,有VS Code的Rust扩展,但是,和Java、C#的IDE支持,相比,还有差距。特别是,大型项目的代码分析、重构、调试等,还不够完善。
还有,学习资源,虽然,Rust的官方文档(The Rust Programming Language),写得非常好,但是,中文的学习资源,还比较少,比较零散。很多深入的主题,比如,生命周期高级用法、unsafe Rust、过程宏、编译器内部等,中文资料,还比较少。
但是,Rust的生态,在快速发展。每年,都有很多新的库,新的工具,新的学习资源,涌现出来。而且,越来越多的大公司,开始在生产环境中,使用Rust。比如,Mozilla,用Rust开发了Servo浏览器引擎;Dropbox,用Rust开发了文件同步引擎;Cloudflare,用Rust开发了边缘计算平台;PingCAP,用Rust开发了TiKV分布式存储。这些公司的使用,说明,Rust,已经足够成熟,可以在生产环境中使用了。
用了三年Rust,我才明白:Rust的生态,虽然还不够成熟,但是,在快速发展,而且,核心领域的库,已经比较成熟了。比如,命令行工具、网络服务、系统工具、嵌入式开发等领域,Rust的库,已经比较丰富,比较成熟了。如果你在这些领域,使用Rust,生态,已经不是大问题了。
而且,Rust,有一个非常好的特性,就是,可以很方便地,调用C语言的库。通过FFI,Rust可以调用,几乎所有的C语言库。这意味着,C语言的庞大生态,Rust都可以利用。这,大大弥补了,Rust自身生态的不足。
所以,如果你担心,Rust的生态不够成熟,不敢在生产环境中使用,我觉得,你可以先,在一些非核心的项目中,尝试使用Rust,比如,命令行工具,内部工具,小的服务等。等你熟悉了Rust,等Rust的生态,更成熟了,再逐步,推广到核心项目中。
六、道理六:Rust,不是银弹,不是所有场景,都适合用Rust
刚学Rust的时候,我觉得,Rust,太优秀了,简直是银弹,什么场景,都适合用Rust。我甚至想,以后,所有的项目,都用Rust来写。
但是,用了三年Rust,我才明白:Rust,不是银弹,不是所有场景,都适合用Rust。每门语言,都有自己的优势,自己的适用场景。Rust,虽然优秀,但是,也有自己的局限性。
Rust,适合的场景:
- 系统编程:操作系统、驱动程序、嵌入式系统等,需要高性能,需要内存安全的场景。
- 命令行工具:Rust,编译出的二进制,体积小,启动快,性能好,非常适合写命令行工具。比如,ripgrep、fd、bat等,都是用Rust写的,非常优秀。
- 网络服务:Rust,有很好的并发支持,性能高,内存安全,非常适合写高性能的网络服务。比如,用actix-web、tokio等框架,可以写出,非常高性能的Web服务。
- WebAssembly:Rust,是WebAssembly的首选语言之一。Rust编译出的Wasm,体积小,性能好,非常适合,在浏览器中,运行高性能的代码。
- 游戏引擎:Rust,性能高,内存安全,非常适合写游戏引擎。比如,Amethyst、Bevy等游戏引擎,都是用Rust写的。
Rust,不太适合的场景:
- 快速原型开发:Rust,编译慢,开发效率,相对较低。如果你需要,快速原型,快速迭代,Python、JavaScript等动态语言,可能更合适。
- 数据分析和机器学习:Rust,在数据分析和机器学习领域,生态,还不够成熟,库,还比较少。Python,有NumPy、Pandas、Scikit-learn、TensorFlow等,非常成熟的库,更适合这个领域。
- GUI应用:Rust的GUI库,还不够成熟,不够好用。如果你需要,写桌面GUI应用,C#(WPF/WinForms)、Java(Swing/JavaFX)、Electron等,可能更合适。
- 脚本和胶水代码:Rust,是编译型语言,需要编译,不适合写脚本和胶水代码。Python、Shell、JavaScript等,更适合。
- 团队不熟悉Rust:如果你的团队,都不熟悉Rust,那么,用Rust,开发成本,会很高,风险,也会很大。这时候,用团队熟悉的语言,可能更合适。
所以,选择语言,要根据项目的需求,团队的情况,综合考虑。不要因为,Rust优秀,就什么项目,都用Rust。选择,最适合项目的语言,才是最重要的。
用了三年Rust,我现在,会根据项目的需求,选择合适的语言。需要高性能,需要内存安全的系统项目,我会用Rust;需要快速原型,需要数据分析的项目,我会用Python;需要Web前端的项目,我会用JavaScript/TypeScript。每门语言,都有自己的位置,都有自己的价值。
七、写在最后
Rust,是一门非常优秀的系统编程语言。它,既有C/C++的性能,又有内存安全和线程安全,还有优雅的语法,完善的工具链。它,是一门,让人越用越喜欢的语言。
但是,Rust,也不是完美的。它的学习曲线,很陡;它的生态,还不够成熟;它,不是所有场景,都适合。所以,学习Rust,使用Rust,要有一个正确的心态。
用了三年Rust,我最大的收获,不仅仅是,学会了一门语言,更是,对内存管理、并发编程、系统编程,有了更深刻的理解。Rust的所有权系统,借用检查器,让我,对内存,对并发,有了全新的认识。这些认识,即使我不用Rust,用其他语言,也能受益。
而且,学习Rust的过程,也是一个,不断挑战自己,不断突破自己的过程。从最开始,被借用检查器折磨,到后来,理解了所有权系统,再到现在,能够写出优雅的Rust代码。这个过程,虽然痛苦,但是,非常有成就感。
最后,用一句话来结束这篇文章:"Rust,是一门,值得花时间,去学习,去掌握的语言。它,不仅能让你,写出更安全、更高效的代码,还能让你,对编程,有更深刻的理解。但是,它,不是银弹,要根据场景,合理使用。"
希望这篇文章,能给正在学习Rust,或者准备学习Rust的朋友,一些参考和启发。如果你有不同的观点,或者有什么问题,欢迎在评论区留言,我们一起交流。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录