Go vs Rust? Choose Go.

Matthias Endler

Go 还是 Rust?选 Go。

使用 Gopherize.me 设计的 Gopher。齿轮由 Freepik 设计。
来源:Gopher 由 Gopherize.me 设计。齿轮由 Freepik 设计。

这篇文章是我很久以前写的。在此期间,我对其中一些方面的看法发生了变化。

为了提供更加平衡的优缺点视角,我建议改为阅读这篇关于 Go 与 Rust 的比较文章。这篇文章是我与 Shuttle 🚀 合作撰写的。

Rust 还是 Go:实践比较

“Rust 还是 Go,我该选哪个?”这是我经常被问到的问题。两种语言似乎都在争夺同一批用户,而且它们似乎都是系统编程(systems programming)语言,所以一定存在一个明确的赢家,对吧?

Go:实用、务实、朴素

随时间变化的 Golang 学习曲线,一条直线。
随时间变化的 Golang 学习曲线,一条直线。

我不认为 Go 是一门优雅的语言。它最大的特性是简单容易上手。Go 甚至都不是一门系统编程语言。虽然它非常适合编写微服务(microservices)以及围绕后端基础设施构建的工具,但我不愿意用它来编写内核或内存分配器(memory allocator)。

但使用 Go,你可以很快地把事情做成
Go 是我使用过的生产力最高的语言之一。它的箴言是:今天就解决现实问题。

Rust 的强大保证是有代价的

随时间变化的 Rust 学习曲线,一段颠簸的旅程。
随时间变化的 Rust 学习曲线,一段颠簸的旅程。

相比之下,Rust 很难。我花了好几个月才算具备一定的生产力。你需要投入大量时间,才能看到任何收益。Rust 已经是一门强大的语言,而且每天都在变得更强。对我来说,它更像是务实的 Haskell,而不是更安全的 C

别误会:我喜欢 Rust,它也帮助我成为了更好的程序员。它确实是一门值得学习的优秀语言。真正的问题在于,它是否适合你的下一个大型项目。

问题是:如果你选择 Rust,通常是因为你需要这门语言所提供的保证:

  • 防范 Null 指针、竞态条件(race conditions)以及各种底层威胁的安全性。
  • 可预测的运行时行为(runtime behavior)(零成本抽象(zero-cost abstractions),且没有垃圾回收器)。
  • 对硬件的(几乎)完全控制(内存布局、处理器特性)。
  • 与其他语言实现无缝互操作。

如果你并不需要这些特性,Rust 可能不是下一个项目的理想选择。这是因为这些保证是有代价的:上手时间。你需要摒弃一些坏习惯,并学习新的概念。刚开始时,你很可能会频繁地与借用检查器(borrow checker)斗争

案例研究:通过试除法判断素数

假设你想检查一个数是否为素数。最简单的方法是检查这个数能否被任何比它小的自然数整除(没有余数)。如果不能,我们就找到了一个素数!这种方法称为试除法(trial division)

下面是用 Golang 实现的方法(感谢 Rosetta Code):

func IsPrime(n int) bool {
	if n < 0 {
		n = -n
	}
	switch {
	case n < 2:
		return false
	default:
		for i := 2; i < n; i++ {
			if n%i == 0 {
				return false
			}
		}
	}
	return true
}

下面是 Rust 中实现相同功能的代码:

pub fn is_prime(n: u64) -> bool {
    match n {
        0...1 => false,
        _ => {
            for d in 2..n {
                if n % d == 0 {
                    return false;
                }
            }
            true
        }
    }
}

乍一看,两种解决方案非常相似。但仔细观察,就能发现一些区别。

  • 在 Go 中,我们使用简单的 switch-case 语句。在 Rust 中,我们使用 match 语句,后者强大得多。
  • 在 Go 中,我们使用简单的 for 循环遍历从 2 到 n 的数字。在 Rust 中,我们使用范围表达式(2..n)。
  • 在 Go 中,我们使用两个 return 语句;在 Rust 中,我们有一个 return表达式。一般来说,Rust 中的大多数东西都是表达式,可以返回,也可以赋值给变量。有关表达式的更多信息,请参阅这里

在许多方面,Rust 比 Golang 更具函数式特征。你可以使用为 Range 实现的 any 方法重写上面的代码。

fn is_prime(n: u64) -> bool {
    match n {
        0...1 => false,
        _ => !(2..n).any(|d| n % d == 0),
    }
}

刚开始它可能看起来有些陌生,但过一段时间就会变得驾轻就熟。

当然,这只是一个快速示例。我建议你浏览一下 Rosetta Code 上的一些代码,以便更好地感受这两种语言。

案例研究:在文本文件中查找重复单词

如果你更喜欢直观的方式,这里有一段视频,展示了我分别用 Go 和 Rust 编写一个简单的并发程序(concurrent program),以比较这两种语言:

我更喜欢 Go 的一些方面

  • 编译速度快
  • 务实的问题解决方式
  • 适合典型 DevOps 任务的优秀生态系统
  • 开箱即用的标准库
  • IDE 支持
  • 简单的错误处理
  • 吉祥物 😉

我更喜欢 Rust 的一些方面

  • 安全性:没有空指针,没有数据竞争……
  • 细粒度的系统控制
  • 惊人的运行时速度(可与 C/C++ 媲美)
  • zero-cost abstractions
  • 棒且思想开放的社区
  • 使用 cargo 进行简单的包管理
  • 以 traits(特征)形式提供对 Generics(泛型)的支持
  • C 互操作和 FFI

结论

99% 的时候,Go 都“足够好”;而在那 1% 不够好的时候,你会知道。然后再看看 Rust,因为这两种语言互补性很好。如果你对实践型 Rust 咨询感兴趣,请从我的日历中选择一个日期,我们可以聊聊我能如何帮助你。

归根结底,Rust 和 Go 并不是真正的竞争对手

原文由 Matthias Endler 发布

本文章由 openai/gpt-5.6-luna 进行翻译