最近我意识到,与其说我在使用”适合工作的正确工具”,不如说我在使用”工作中的工具”。这让我开始思考一个问题:我们应该如何选择编程语言?
编程语言在太多维度上存在差异,很容易陷入”各有权衡”这种正确但无聊且无用的结论。当然有权衡,重要的问题是:这种语言为什么选择了这种特定的权衡组合?
这个问题很有趣,因为我不想像买加湿器一样根据功能列表来选择语言。我关心的是构建软件,我关心我的工具。在做出权衡时,语言表达了一套价值观。我想找出哪些价值观与我产生共鸣。
过去几个月,我深入体验了三种语言:Go、Rust和Zig。让我分享一下对它们价值观和执行力的思考。
Go:企业协作的极简主义
Go以其极简主义而著称,被描述为”现代C”。虽然Go有垃圾回收和真正的运行时,但它像C一样,你可以把整个语言装进脑子里。
你能把整个语言装进脑子里,是因为Go的功能很少。很长时间里,Go因为没有泛型而臭名昭著。这在Go 1.18中终于改变了,但那是在人们乞求添加泛型12年之后。其他现代语言常见的功能,如标记联合或错误处理的语法糖,至今没有添加到Go中。
Go开发团队对添加语言功能有很高的门槛。结果是一种语言,迫使你写大量样板代码来实现在其他语言中可以更简洁表达的逻辑。但结果也是一种随时间稳定且易于阅读的语言。
举个例子,考虑Go的slice类型。Rust和Zig都有slice类型,但这些只是胖指针。在Go中,slice是指向内存中连续序列的胖指针,但slice也可以增长,这意味着它包含了Rust的Vec<T>类型和Zig的ArrayList的功能。而且,由于Go为你管理内存,Go会决定你的slice的后备内存是在栈上还是堆上;在Rust或Zig中,你必须更仔细地考虑内存的位置。
Go的起源故事基本上是这样的:Rob Pike厌倦了等待C++项目编译,也厌倦了Google的其他程序员在这些C++项目中犯错误。因此,Go在C++巴洛克的地方保持简单。这是一种为编程大众设计的语言,旨在满足90%的用例,同时易于理解,即使(也许特别是)在编写并发代码时。
Go的价值观:企业协作高于个人表达。Go是极简主义服务于企业协作。这不是贬低——在企业环境中构建软件有其自身的挑战,Go为此提供了解决方案。
Rust:安全与性能的最大主义
Go是极简主义的,Rust是最大主义的。Rust经常关联的标语是”零成本抽象”。我会修改为:”零成本抽象,而且很多!”
Rust以难学著称。我同意Jamie Brandon的观点,让Rust困难的不是生命周期,而是塞进语言中的概念数量。看看这个GitHub评论就能完美说明Rust的概念密度:
类型
Pin<&LocalType>实现了Deref<Target = LocalType>但没有实现DerefMut。类型Pin和&是#[fundamental]的,所以Pin<&LocalType>的impl DerefMut是可能的…
当然,Rust并不是为了最大主义而最大主义。Rust是一种复杂的语言,因为它试图实现两个有些矛盾的目标——安全和性能。
性能目标不言自明。”安全”的含义不太清楚。”安全”意味着”内存安全”,你不应该能够解引用无效指针,或进行双重释放等。但它也意味着更多。”安全”程序避免所有未定义行为(有时称为”UB”)。
什么是可怕的UB?我认为理解它的最好方式是记住,对于任何运行的程序,都有比死亡更糟糕的命运。如果你的程序出了问题,立即终止实际上很好!因为如果错误没有被捕获,替代方案是你的程序进入不可预测的暮光地带,其行为可能由下一个数据竞争中哪个线程获胜或特定内存地址碰巧有什么垃圾来决定。
Rust试图通过在编译时检查来防止UB,而不付出任何运行时性能代价。Rust编译器很聪明,但不是全知的。为了能够检查你的代码,它必须理解你的代码在运行时会做什么。因此,Rust有一个表达性的类型系统和一系列特征,允许你向编译器表达在另一种语言中只是代码明显运行时行为的东西。
Rust的价值观:系统级控制与高级安全保证的结合。这使得Rust很难,因为你不能只是做事情!你必须找出Rust对这件事的称呼——找到你需要的特征或其他东西——然后按照Rust期望的方式实现它。
Zig:数据导向的无政府主义
在三种语言中,Zig是最新和最不成熟的。截至撰写本文时,Zig只有0.14版本。它的标准库几乎没有文档,学习如何使用它的最好方法是直接查阅源代码。
虽然我不知道这是否属实,但我喜欢把Zig看作对Go和Rust的反应。Go简单是因为它掩盖了计算机实际工作方式的细节。Rust安全是因为它强迫你跳过它的许多圈套。Zig会让你自由!在Zig中,你控制宇宙,没有人能告诉你该做什么。
在Go和Rust中,在堆上分配对象就像从函数返回结构体指针一样简单。分配是隐式的。在Zig中,你显式地分配每个字节。(Zig有手动内存管理。)你在这里比在C中有更多控制:要分配字节,你必须在特定类型的分配器上调用alloc(),这意味着你必须为你的用例决定最佳的分配器实现。
在Rust中,创建可变全局变量如此困难,以至于有长篇论坛讨论如何做到这一点。在Zig中,你可以直接创建一个,没问题。
未定义行为在Zig中仍然重要。Zig称之为”非法行为”。它试图在运行时检测它,并在发生时崩溃程序。对于那些可能担心这些检查的性能成本的人,Zig提供了四种不同的”发布模式”,你可以在构建程序时选择。在其中一些模式中,检查被禁用。
Zig与其他两种语言的另一个区别是Zig与面向对象编程的关系。OOP已经失宠一段时间了,Go和Rust都避开了类继承。但Go和Rust对其他面向对象编程习语有足够的支持,如果你想的话,你仍然可以将你的程序构造为交互对象的图。Zig有方法,但没有私有结构字段,也没有实现运行时多态性(又名动态分派)的语言功能。据我所知,这些排除是有意的;Zig是数据导向设计的语言。
Zig的价值观:程序员的绝对控制权与数据导向思维。
对创业者的启示
作为创业者,我们在技术选型时往往过于关注功能特性,而忽略了语言背后的价值观。但价值观决定了团队的工作方式和产品的最终形态。
选择Go的场景
- 团队规模较大,需要快速上手
- 业务逻辑复杂但不需要极致性能
- 重视代码的可维护性和团队协作
- 构建微服务和云原生应用
选择Rust的场景
- 对性能和安全有极高要求
- 构建系统级软件或基础设施
- 团队有足够的学习投入时间
- 需要处理复杂的并发场景
选择Zig的场景
- 需要对系统有完全控制
- 性能是第一优先级
- 团队偏好数据导向的设计思维
- 愿意承担早期技术的风险
技术选择的哲学思考
2026年,我们面临着前所未有的技术选择。每种工具都有其存在的理由,关键是理解它们的价值观是否与你的目标一致。
Go说:”让我们一起高效地构建可靠的软件。”
Rust说:”让我们构建既快速又安全的软件,即使这很困难。”
Zig说:”让我们完全控制我们的软件,回到本质。”
没有对错,只有适合与否。作为创业者,我们需要的不仅仅是技术工具,更是与我们价值观一致的合作伙伴。
选择编程语言,就是选择一种思维方式。选择一种思维方式,就是选择一种未来。
这篇文章受到了Sinclair Target关于Go、Rust、Zig比较的深度思考启发。在技术快速发展的时代,理解工具背后的哲学比掌握具体语法更重要。