«

简单讨论一下 Go、Rust、Zig 这三个不推崇 OOP 思想的语言

时间:2026-1-24 00:10     作者:独元殇     分类: 开发相关


[toc]

Go 和 Rust 的区别是,前者极简主义,后者极繁主义。Zig 的话,可能有潜力,但目前最新版本是 0.15.2 ,额,还不成熟,不建议高资产投入的生产级环境使用,但它很有创意,如果你不喜欢 oop ,我很推荐它。

很巧,这三种,价值观上,都不推荐使用繁长的 OOP 面向对象思想(尤其是 Zig,致力于剔除这个思想,试图更简单)。而且 Go 和 Rust 都避开了类继承,且可以按照交互对象的方式构建程序。而 Zig 则是数据导向设计的语言。(数据导向 就是更关心数据,在内存里怎么存,怎么排,是数组,还是什么什么。区别于 OOP)

任何语言,能突破冷启动,变得流行起来,都一定是解决了某领域的痛点。所以,这些语言,一定都有各自的优点。在合适的场景,用合适的工具。

每个语言,他们的差异,都指向了「为什么它们选择这一种解决方案」。

很多时候,一些编程语言的区别是,X 语言适合写 web 应用,原因很简单,X 的语言的作者的价值观就是很喜欢互联网。

而很多时候,我们说很多语言的差异,是,比如 X 语言更适合写 web 应用,是因为 X 语言有 a、b、c 这些特性.... 这种显然就片面很多。

Go 语言

这个 go 语言,语法非常简单,和 C 一样优雅,但又不像 C 一样,go 有垃圾回收机制和真正的运行时环境。但是 GO 的语法简单到,我觉得一张 A4 纸就基本写完了,特性少而精,你能完全记住这个语言的语法。

很长时间里,Go 都因为缺少【泛型】而被遭到很多吐槽,一直被吐槽了了12 年,直到 Go 1.18 才加入..... 可见作者的谨慎,对「简单」的那种追求。

当然,各种其他语言里面的语法糖,带标签的联合理性、各种花里胡哨的错误处理,Go 里面都没有。

然后呢,最终的结果,就是你要写很多的「预制代码」来实现其他语言里一行 API 就搞定的东西。但是,好处是,易读、代码结果稳定!其实也不是什么缺点。

这归功于作者 Rob Pike 的价值观。他当年在美利坚谷歌,受够了 C++ 的项目编译、各种同事之间在 C++ 里犯的错误,然后就很想在 C++ 复杂的地方给转化为简洁的样子。然后就设计了 Go 这种面向所有程序员,能满足 90% 的使用场景的编程语言。阅读起来好读,写起来也轻松一点。

这个语言语法非常简单,在多人协作之间我觉得优势更大。(相较而言,C++ 就是另一个极端。太容易犯错了。)

Rust 语言

和 Go 语言相比,就是极繁主义。

外网对其的评价是 zero-cost abstractions(零成本抽象).

零成本抽象: 你用更高级、更好读的方式去写代码,但编译器会把这些抽象完全“消掉”,生成的机器码和你手写底层实现几乎一样快、一样省。也就是说,抽象不该因为“好看”就拖慢程序。

太高级了,太抽象了,因此难学。这个 Rust 里塞入了太多概念......

比如 github 的中这个评论:

https://github.com/rust-lang/rust/issues/68015

img

部分内容翻译如下:

Pin<&LocalType> 这个类型实现了 Deref,所以你可以把它当成只读引用来用,但它没有实现 DerefMut,也就是说你不能通过它拿到可变引用。Pin 和 & 都被标记成 #[fundamental],这让以后在类型系统层面为 Pin<&LocalType> 实现 DerefMut 成为可能。 这里的 LocalType 可以是一个具体结构体,比如 SomeLocalStruct,也可以是一个 trait 对象,比如 dyn LocalTrait。在这种情况下,你甚至可以把 Pin<Pin<&SomeLocalStruct>> 自动转换成 Pin<Pin<&dyn LocalTrait>>。是的,中间真的会套两层 Pin。 这样做的结果是,你可以在稳定版 Rust 里构造出一对“看起来像智能指针、又带点奇怪行为”的类型。具体来说,Pin<&SomeLocalStruct> 和 Pin<&dyn LocalTrait> 就扮演了这种角色,它们已经支持 CoerceUnsized,但在可变性和移动语义上有着非常特别的限制。

有时候,一个小细节,就能抖搂出一大堆的东西,相较 Go 来讲,复杂不少。

但是,Rust 的设计价值观是,【安全性】与【性能】。在 Rust 之前,安全与性能是矛盾的,你为了安全,必然会叽里呱啦哦捣鼓很多影响性能的措施,但 Rust 在这方面搞的不错。

当然,大家别误会,这里的安全,是指的【内存安全】,更细说,就是,不应该能够解引用无效指针、或者进行双重释放灯操作。(通俗的说,就是 不应该去用一块已经无效的内存,比如指针指向了不存在的地方,或者同一块内存被释放了两次,这些都会让程序行为变得不可控。)

不能说,在你机器这个时候能跑,换个机器换个环境时间就报错了,稳定性很差。

这个 Rust 的设计思想是【可预测】,对于任何运行中的程序,出现问题应当立即终止!不陷入无法预测的混沌地带,即【未定义行为】。Rust 的价值观认为,未定义行为是可怕的。(JavaScript 就是另一个极端,一切都模模糊糊,但是确实很简单)。

但是 JS 实现了安全性,但是牺牲了性能。Rust 则既能保证性能,又能保证安全性。

这个 Rust 的编译器很只能,在编译时能把几乎所有的【未定义行为】检查出来,并做预防措施,而且在运行时没有性能代价。

但付出的代价,就是需要非常繁多详细的语法系统、类型系统、新特性,以便能让编译器在运行时更清晰的理解你是想干啥。

只要你跟着 Rust 的规矩走,那 Rust 就能兑现做出更好的运行你的代码的承诺。这就让 Rust 里去使用第三方库很容易,也是为什么 Rust 的项目依赖项和 js 生态几乎一样多。

Zig 语言

这个用的不多,但它的价值观,和 Rust 、GO 又截然不同。

Go 的简单,是把计算机内部工作细节隐藏了,Rust 安全,是因为它的规矩细致繁多。但 Zig 则是自由。

在 Zig 里,你得自己分配每个字节,手动内存管理。比 C 的控制权还大。在 Rust 里,你搞一个全局能使用的变量很困难,但是在 Zig 里轻轻松松搞定。

而为了解决 Rust 要解决的那个 【未定义行为】,它选择在到达这种行为时,直接让程序崩溃。当然,至于检查未定义行为的性能方面,Zig 也提供了很多选项,禁用一部分检查。反正更务实一点吧。

Zig 就很叛逆,在语言语法上,有种无政府状态的感觉,我很看好 Zig,就看他们团队如何维护 Zig 了。不知道什么时候发布 Zig 1.0 版本哈哈。

标签: 原创 Go