本人技术实力一般,所以自认为更能从大众程序员的视角对比一下 Rust 和其他语言,以及浅浅聊下为什么更喜欢 Rust 。
用过很多语言了,包括 C, C++, Go, JS, TS, C#。最近几个月一直在用 Rust 工作,用下来的整体体验就是:其他语言用着不爽的地方,在 Rust 这边总是能非常自然地解决。以下是详细版。
大家用 Rust 基本都是奔着这个来的,这方面确实是吊打 C/C++。C++ 的智能指针是在运行时保证安全的,跟 Rust 在编译期保证安全没法比。而且,在 Rust 里也可以用 unsafe
来手动管理内存,非常灵活。跟其他带 GC 的语言比就见仁见智了。
配套工具可以说是一应俱全,都是由官方提供,令我这种想开箱即用的程序员体验非常好。包管理器有 Cargo ,代码格式化工具有 rustfmt ,linter 有 clippy ,还有 VSCode 插件。C/C++ 就不说了,非常难评。Go 的工具链也不错,包管理、格式化工具之类的也都有(但是不能不吐槽早年间的奇葩设计 GOPATH
),感觉能打个平手。JS/TS 生态感觉会有些混乱,包管理器就有好几个了,然后 linter 不自带,虽然配置好了之后用起来也很舒服,但是自己配置会有些烦,不像 Rust 开箱即用。C# 就更复杂了,不过考虑到这是微软那一套东西,似乎就该是臃肿又难用。
Rust 采用返回 Result<T, E>
的方式,Go 采用返回多个值(其中有一个表示是否有错),其他都是 throw-try-catch 。
个人很不喜欢 throw-try-catch 的方式,首先 try block 自己是一个 scope ,我不得不在 try block 的外面先声明变量,然后再在 try block 里给变量初始化/赋值,很不喜欢这样子。其次就是,比如 JS/TS ,就不会强制要求定义函数时要同时定义该函数可能抛出的异常的类型,这就让我开发时感觉如履薄冰,我永远不知道会有什么神奇的异常被抛出。
Go 的 if err != nil
已经被很多人诟病过了,虽然我认为这样子做已经比 throw-try-catch 好一些了——因为强制程序员错误处理或者显式表明不想处理——但是在函数的返回值里,当出错时,表示错误的返回值有意义而其他返回值无意义;当顺利执行时,表示错误的返回值无意义而其他值有意义,这个设计对我来说实在是槽点太大了。
Rust 则让函数返回一个 Result<T, E>
的 enum value ,这就非常优雅,一个返回值承载了顺利和出错这两种情况,而且跟 Go 一样,这强制程序员处理错误或者显示表明不想处理。除此以外,假设函数 fun1
调用了 fun2
,fun1
的返回值类型为 Result<T1, E1>
, fun2
的返回值类型为 Result<T2, E2>
, 当 E2
类型能「转换」(Into
trait )成 E1
类型或者 E2
跟 E1
相同时,若 fun1
想要直接返回 fun2
返回的错误时,有语法糖,使得代码可读性提高很多。
Rust 支持简单的用 macro_rules!
定义的宏,更复杂的宏(过程宏,procedural macro )可以用标准 Rust 语言实现,只需要写一个接收 token 输入,并且输出 token 的函数即可。这些宏在编译期间会被编译器执行并展开,然后再进行后续编译。
C/C++ 的宏只支持字符串处理,与 Rust 能在 token 级别处理形成鲜明对比。C++ 还支持 template ,不过这方面 Rust 更加强大(配合上 Rust 的强大的宏)。
TS 的 decorator 确实也算元编程,但是 decorator 的内容是在运行时执行的,损失了一点点运行效率。C# 印象中也是运行时(错了的话麻烦纠正我,谢谢)。
(不过 TS 的编译器类型推断确实很骚,能在编译时期用函数式编程的方法做归并排序…… 虽然还是被 Rust 吊打,毕竟 Rust 的宏就是一个标准 Rust 函数,里面可以为所欲为)
Rust 除了宏,还有 trait 可以做元编程,不过暂时没觉得非常惊艳。(而且印象中 trait 和 async 的配合相对会局限一些,所以感觉更不值得吹嘘这一点)
Rust 里,各个 module 组成了树形结构,官方也确实提倡把 module 结构映射到操作系统的文件系统里,因此很清晰。每个 item (即 module, function, struct, enum, trait 等)都有可见性( public or private?),子 module 默认可以访问父 module 的 item 。其他情况下,module A 可访问 module B 的 item ,当且仅当 A 和 B 的最近公共祖先到 B 的路径上,每个 module 都是 public ,并且欲访问的 item 也是 public 的。
我很喜欢的一点就是,父 module 里的 private items ,在子 module 里是可以访问的,这就好像我有一个很大的功能模块,这个功能模块有若干子模块,我可以在这个功能模块里定义一些不想暴露给外界,但是子模块里又需要使用的东西。其他语言很难做到这样,他们如果想让子模块访问父模块的东西,就只好让父模块里的东西 public ,但是这样子就不仅仅只暴露给了子模块,还暴露给了外界。
C/C++ 里,可以透过头文件来控制可见性,也可以用 public
, protected
, private
来控制。但是由于不同功能模块之间没有语言级别支持的父子关系(namespace
有父子关系,但是父子关系没什么用?),就无法实现我喜欢 Rust 的那一点。不过 C++ 有友元,可以更精细的控制…… 但是我还是喜欢 Rust 这种很自然的默认设置。JS/TS 和 C# 也差不多。
Go 真的槽点太大了,靠 item 的首字母是否大写来决定可见性…… 个人反正是很讨厌。
C++ 的 int
, long
, long long
, short
分别是几个字节?跟具体的 CPU 相关,并不是定死的。
Go 里有 int8
, uint64
等类型,直接写明这个类型占几字节,可惜除此以外,Go 里还有 int
和 uint
,又是跟 CPU 相关的。Rust 里,每个整数类型都写明了占几字节,而且没有跟 CPU 相关的整数类型。这种强制程序员指明数据长度的设计,我很喜欢,这样子大大提高跨平台的可能性。同时,Rust 里也有 引用/指针 类型,这个确实是跟 CPU 相关的(也不得不跟 CPU 相关),但是至少 引用/指针 类型不是整数类型。
个人粗浅的理解:Rust 披着高级语言的外衣,实际上高层、底层的事情都能做,是一门严谨的、高效的、语法现代化的、安全的编程语言。个人用了一段时间,真的感觉很舒服。开发时最喜欢干的事情就是写宏(没错我就是宏孩儿)来解耦,写完了一个宏之后,用起来的感觉是真的爽,一行简单的代码,生成了一堆复杂的代码,很有成就感,代码维护成本也大大降低(不过宏维护起来确实也难一点)。
欢迎大家补充以及指正我的错误!!
1
querysecret 133 天前
哈哈,没人回复,前几天有一个 GO 的就 100 多楼,说明 Rust 不招人喜爱
|
2
ciaoSora OP @querysecret 我刚发出来没两分钟的帖子🤣 顺便问下 Go 的 100 多楼的贴在哪,我去对线
|
3
zsj1029 133 天前
支持一下,js 配合 bun 编译写后端挺爽,想试试 1m 内存运行的 rust ,学习一下
|
4
panlatent 133 天前
标题改成《 Rust 超过其他语言的原因》 发到编程语言的节点 :D
|
6
zw1196650986 133 天前
最近也在学 rust,但是这玩意不太好找工作,只能靠兴趣学习
|
7
ciaoSora OP @zw1196650986 区块链领域有很多用 Rust ,Solana 的智能合约最主流用 Rust 写,还有些钱包也用 Rust 写。互联网领域感觉主要是既有的框架过于成熟了,各个团队、公司肯定是懒得迁移,而且也不敢用新东西。我自己用 Rust 写过后端服务和前端页面,感觉刚开始会有一点不适应,写一会感觉就还好了~
|
8
ciaoSora OP @zw1196650986 哦,一些量化系统也有些开始用 Rust 了,可能 Rust 确实比 C++ 用起来安心一点吧
|
9
minami 133 天前
别的不谈,整体看下来感觉都是主观喜好,特别是一看 C++也用的不是很熟的样子。。。不过素质已经吊打大部分 Rust 教徒了,今天就不喷编程原神了
|
10
povsister 133 天前
因为 rust 还是相对小众一些,不懂的人来指指点点很容易反被喷,go 就不一样了,毕竟是强类型 Python (狗头)
自己目前用 rust 还是拿来写一些中间件或者资源/性能要求的小组件,业务/web 的话用起来还是没有 go 顺手 |
11
AllenTsui 133 天前
OP 再学习一下 zig ,听说 zig 的宏编程比 rust 惊艳。
|
12
hingle 133 天前 1
Rust 的 isize 和 usize 是固定大小?
|
14
virusdefender 133 天前
rust package mod 之类的概念感觉有点过于复杂了
|
15
cmdOptionKana 133 天前
写得很好,完全可以作为一篇优秀的博客文章啊
|
16
Pierro 133 天前
标题改成 java 这么烂为什么还有这么多人用 为什么不换成 rust
|
17
IvanLi127 133 天前
@querysecret 这下路过不得不回复一下了。Rust 万岁,人气+1
|
18
Bazingal 133 天前 1
C#的 Source Generator 是编译时元编程
|
19
csys 133 天前
在语言特性上,go 是不配和 rust 比的,比如你说的 Result<T, E>和 trait ,以及非常好用的模式匹配,rust 吸收了很多优秀的编程语言特性,拿 golang 比,go 简直就像是原始人手里的棒子
我的感受是 rust 的最大问题在于繁琐,不只是写起来繁琐,读起来也挺难受的 另外就是 rust 和 go 的通病,既不够 oo ,又不够函数式,go 就当它摆了,但是 rust 有这么先进高级的语言特性,结果只能写出和 go 一个模样的代码,实在是难以接受,有拿数控机床造砖块的感觉 |
20
wangweei 133 天前 via iPhone
C++也可以使用 uint32 之类的固定长度基本数据类型
|
21
wlingxiao 132 天前 via Android
这不学一手 Scala (狗头
|
22
yoiteshaw 132 天前
写得不错,rust 带给程序员的安全性,是很棒的。但是每次都要 unwrap 让我很烦,不够优雅。(可能是我掌握不到位
|
23
StarsunYzL 132 天前
工具链
C++除了没有靠谱的包管理器外哪里差了 错误处理 C++23 有 std::expected<T, E>,C++11 可以用微软 VC 团队成员开源的 tl::expected<T, E>实现 元编程 先把 C++的 template 玩明白了再夸其他的 代码结构和可见性 过于主观 内置数据类型 C++几百年前就有了固定长度的(u)int8_t/16/32/64 类型 |
24
NPC666 132 天前 via Android
其实 C++上手难度比 Rust 高多了,Rust 如果不和 C/C++的 binding 交互,只写 rust safe 的话,开发体验是非常棒的。
|
25
liuran 132 天前
还有 rust 放弃了 C++和其他过去的面向对象语言的 Class ,而是采取 trait ,这一点和 go 的鸭子类型类似,我都感觉是非常好的实现方式,从个人体验上来说,灵活性和使用体验都比 Class 好很多。
但是工作中是写 C++的,个人项目目前还没有什么好的要用 rust 写的项目,于是总是在入门 rust 之后忘掉,下次再入门再忘掉的循环中度过。。。 |
26
bunny189 115 天前 via iPhone
老哥,能问问你是怎么入门的吗?先看圣经还是其他资料呀?
|