何时使用Rust,何时使用Go
一开始,Go和Rust之间就有明显的区别。Go更专注于构建Web API和小型服务,这些服务可以无限扩展,特别是在 Goroutines 的强大功能下。Rust也可以实现后者,但从开发人员体验的角度来看,事情要困难得多。
Rust可以很好地处理大量数据和其他CPU密集型操作,例如执行算法。这是Rust对Go最大的优势;要求高性能的项目通常更适合Rust。
在本教程中,我们将比较Go和Rust,评估每种编程语言的性能、并发性、内存管理和整体开发人员体验。我们还将介绍这些元素的概述,以帮助您为您的项目选择正确的语言。
性能
Go最初由Google的工程师设计,于2009年向公众推出。它的创建是为了提供C++的替代方案,更容易学习和编写代码,并为在多核CPU上运行进行了优化。
从那时起,Go语言对于那些希望利用该语言提供的并发性优势的开发人员来说是一个很好的选择。该语言提供了Goroutines,使您能够将函数作为子进程运行。
Go的一大优势是你可以轻松地使用Goroutines。简单地将 go 语法添加到函数中,就可以使它作为子进程运行。Go的并发模型允许您跨多个CPU核心部署工作负载,使其成为一种非常高效的语言:
package main
import (
"fmt"
"time"
)
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct")
go f("goroutine")
time.Sleep(time.Second)
fmt.Println("done")
}
Rust从一开始就被设计成高性能-事实上,这就是Rust网站上是“为什么要使用Rust?”的答案!Rust处理内存管理的方式意味着它不像Go语言需要使用垃圾收集器,并且引用的使用可以让对象很容易地传递,而不需要复制。
Rust与Go基准测试
基准测试很容易被操控并且很难被解释。Benchmarks Game通过允许每种语言使用多个程序来处理这个问题,并比较它们运行所需的时间以及内存使用情况和代码复杂度,以更好地了解语言之间的权衡。
对于所有测试过的算法,最优化的Rust代码比最优化的Go代码至少快30%,而且在很多情况下速度要快得多;对于二叉树基准测试,优化最多的Rust代码比优化最多的Go代码快12倍!在许多情况下,即使是没有优化的Rust代码也比最优化的Go代码更快。
下面是一些优化最多的Rust和Go代码的示例:
可扩展性
这两种语言都擅长扩展,以利用多个CPU并行处理数据。在Go语言中,你可以使用Goroutine来处理每一个数据,然后使用 WaitGroup 来等待它们全部完成。在Rust中,rayon
是一个非常方便的库,使用它可以很容易地并行迭代容器。
并发
如上所述,Go支持并发。例如,假设您正在运行一个处理API请求的Web服务器。您可以使用Go语言的Goroutines将每个请求作为一个子进程运行,通过将任务卸载到所有可用的CPU内核来最大限度地提高效率。
Goroutines 是 Go 内置函数的一部分,而Rust通过原生 async/await语法来支持并发。因此,当涉及到并发性时,开发者体验优势就转向Go。然而,Rust在保证内存安全方面做得更好。
下面是Rust的简化线程示例:
use std::thread;
use std::time::Duration;
fn main() {
// 1. create a new thread
for i in 1..10 {
thread::spawn(|| {
println!("thread: number {}!", i);
thread::sleep(Duration::from_millis(100));
});
}
println!("hi from the main thread!");
}
对于开发者来说,并发性一直是一个棘手的问题。在不影响开发者体验的情况下保证内存安全的并发性并不是一件容易的事。然而,这种对安全的极端关注导致了可证明正确的并发的产生。
Rust尝试了所有权的概念,通过防止未经请求的资源访问,以防止内存安全漏洞。Rust提供了四种不同的并发范例来帮助您避免常见的内存安全陷阱。我们将在下面的章节中详细介绍两种常见的范例:通道 (Channel) 和锁 (Lock)
渠道
通道有助于将消息从一个线程传输到另一个线程。虽然这个概念也存在于Go语言中,但Rust允许您将指针从一个线程转移到另一个线程,以避免资源竞争。通过传递指针,Rust可以对通道强制线程隔离。再次,Rust在并发模型方面表现出了对内存安全的痴迷。
锁
只有当锁定处于状态时才能访问数据。Rust依赖于锁定数据而不是条件的原则,这种原则经常出现在Java等编程语言中。
有关所有权概念和所有并发范例的更多细节,请查看“Fearless Concurrency with Rust”。
内存安全
早期的所有权概念是Rust的主要卖点之一。Rust将类型安全提升到了一个新的层次,这对于实现内存安全并发也很重要。
根据Bitbucket博客,“Rust非常严格和迂腐的编译器检查你使用的每个变量和你引用的每个内存地址。它避免了可能的数据竞争条件,并通知您未定义的行为。
这意味着您不会因为Rust对内存安全的极度痴迷而导致缓冲区溢出或竞争条件。然而,这也有其缺点。例如,在编写代码时,您必须高度了解内存分配原则。要始终保持记忆安全并不容易。
开发者体验
首先,让我们看看与每种语言相关的学习曲线。Go的设计考虑到了简单。开发人员经常称之为“无聊”的语言,也就是说,Go语言有限的内置特性使得Go语言易于采用。
此外,Go语言提供了一种作为C++简单替代的方案,隐藏了内存安全和内存分配等方面。Rust采取了另一种方法,迫使您考虑内存安全等概念。所有权的概念和传递指针的能力使得Rust成为一个不那么吸引人的学习选择。当你不断考虑内存安全问题时,你的工作效率会降低,代码也会变得更加复杂。
与Go相比,Rust的学习曲线相当陡峭。然而,值得一提的是,Go语言比Python和JavaScript等更动态的语言有更陡峭的学习曲线。
开发周期
对于现代软件开发人员来说,能够快速迭代是非常重要的,能够让多个人在同一个项目上工作也是如此。Go和Rust以不同的方式实现这些目标。
Go语言编写和理解非常简单,这使得开发人员很容易理解彼此的代码并对其进行扩展。然而,在Go代码中,你必须非常小心地检查错误,避免 nil 访问;编译器在这里没有提供太多帮助,所以隐式地理解哪些变量可能是 nil ,哪些变量保证不是 nil 。
Rust代码编写和编译起来比较棘手;开发人员必须对引用和生存期等有很好的理解才能成功。然而,Rust编译器在捕捉这些问题方面做得很好。(提供非常有用的错误信息-在最近的一项调查中,90%的Rust开发人员认可了这些错误信息!) 因此,虽然“一旦你的代码编译,它是正确的!”对于这两种语言都不成立,但是Rust更接近这一标准,这让其他开发人员在迭代现有代码时更有信心。
产品特点
这两种语言都有各种各样的功能。正如我们在上面看到的,Go语言内置了对许多有用的并发机制的支持,主要是Goroutines和通道。该语言支持接口,并于2022年3月发布的Go v1.18支持泛型。但是,Go语言不支持继承、方法或运算符重载或断言。因为Go是在Google开发的,所以Go对HTTP和其他Web API有很好的支持也就不足为奇了,而且还有一个庞大的Go软件包生态系统。
Rust语言比Go语言的功能更丰富一些;它支持traits(接口的更复杂版本)、泛型、宏和丰富的内置类型,用于可空类型和错误,以及便于错误处理的 ? 操作符。从Rust调用C/C++代码也比从Go调用更容易。Rust还有一个大型的生态系统。
何时使用Go
Go语言适用于各种各样的用例,使其成为Node.js创建Web API的绝佳替代品。正如洛里斯Cro所指出的,“Go的并发模型非常适合必须处理多个独立请求的服务器端应用程序”。这就是Go提供Goroutines的原因。
更重要的是,Go内置了对HTTP Web协议的支持。您可以使用内置的HTTP支持快速设计一个小型API,并将其作为微服务运行。因此,Go非常适合微服务架构,并满足API开发人员的需求。
简而言之,如果您重视开发速度,并且更喜欢语法简单而非性能,Go语言是一个很好的选择。最重要的是,Go语言提供了更好的代码可读性,这是大型开发团队的一个重要标准。
在以下情况下选择“Go”:
- 你关心的是简单性和可读性
- 您需要一个简单的语法来快速编写代码
- 您希望使用一种更灵活、支持Web开发的语言
何时使用Rust
当性能很重要时,例如当您处理大量数据时,Rust是一个很好的选择。此外,Rust让您可以细粒度地控制线程的行为以及线程之间资源的共享。
另一方面,Rust具有陡峭的学习曲线,并由于内存安全的额外复杂性而减慢了开发速度。这不一定是劣势; Rust还保证了在编译器检查每个数据指针时不会遇到内存安全错误。对于复杂的系统,这种保证可以派上用场。
在以下情况下选择Rust:
- 你在乎性能
- 您希望对线程进行细粒度控制
- 您重视内存安全而非简单性
Go vs. Rust:我诚实地认为
让我们从强调相似之处开始。Go和Rust都是开源的,旨在支持微服务架构和并行计算环境。两者都通过并发优化可用CPU内核的利用率。
但说到底,哪种语言最好?
有很多方法可以解决这个问题。我建议你考虑一下你想要构建什么类型的应用程序。Go语言在创建Web应用程序和API方面很好,这些应用程序和API利用了其内置的并发特性,同时支持微服务架构。
您也可以使用Rust来开发Web API,但它的设计并没有考虑到这个用例。Rust对内存安全的关注增加了复杂性和开发时间,特别是对于一个相当简单的Web API。然而,您对代码的更大控制允许您编写更优化、内存效率更高且性能更好的代码。
尽可能简单地说,Go与Rust的争论实际上是一个简单与安全的问题。