我尝试了当前流行的编程语言

科技   2024-12-16 23:25   北京  

导读:在这篇文章中,我将尝试并比较 GoLang、Zig 和 Rust。以及 Rust 为什么胜出。

我用 GoLang、Zig 和 Rust 编写了 3 个项目。这些项目足够大,可以让我们很好地了解语言基础知识、缺点以及语言、框架或生态系统是否存在问题。

Go语言


几个月前我开始构建开发工具,最初使用 GoLang。


第一个是基本的数据库管理和迁移实用程序(dbdaddy),我很喜欢使用 GoLang。


这是我第一次尝试使用 GoLang 构建比 Advent Of Code 和 10 亿行挑战问题更严肃的东西,但我只花了不到一周的时间就熟练掌握了它。


Zig

这段时间,我一直在业余时间研究 Zig。我听说 Redis 将其许可证更改为“技术上非开源”许可证。

我心想:在 zig 中构建一个新的开源 redis 有多难?

“ GbCache ”背后的基本思想是基于 LRU 的内存缓存,但实际的真相来源将保存在磁盘上,同时还具有特定键/值变化的通知以及分配给键的 TTL 等功能。

Zig 的明确性令人印象深刻,这使得它更加简单且更易于使用。

我从“如果明天要使用 HyperScale™,它能否处理负载?”的角度启动了 GbCache。

这种观点促使我排除了 GoLang,因为如果我无法非常确定地推断内存分配,我就无法完全控制速度。

从这个角度做事真的是一种很棒的学习经历,因为你会突然开始看到程序中所有可能崩溃、内存耗尽或成为瓶颈的点 -即代码中的热路径。

由于 Zig 的明确性,我可以准确地确定在何时何地分配内存。

但是... 归根结底,Zig 并不是一种内存安全的语言,而且我是一个技能问题如珠穆朗玛峰般巨大的人。

进入Rust

在过去的两年里,我曾三次尝试并愤怒地退出 Rust。

我的背景是 JavaScript,我们的 MacBook M69 Maxes 有用不尽的内存,我从来没有真正理解 Rust 为什么有这些规则,而从炒作的角度看待它可能不是一个好主意。

尽管我多次愤怒地退出,但 Rust 仍然在我脑海中浮现,它奇怪的语法、令人沮丧的内存规则和奇特的生命周期。

但是在写 Zig 的时候,我突然想到......我已经在追踪所有这些记忆规则和生命,但作为一种精神负担。

我开始注意到我的 Zig 代码中的模式和样板,尝试做 Rust 已经做的事情。

在为SFS构建 CLI 客户端(SFW 名称 - “简单文件存储”)时,我决定再次尝试 Rust。

我喜欢 Rust 的一点是基于所有权和借用权的内存心理模型。

尽管这种拥有和引用的记忆模型是虚构的,但它是一种方便推理的心理模型,并且对我的头脑造成的负担较小。

与 C 和 Zig 等语言在头脑中产生的经典心理记忆模型相反。必须在头脑中跟踪单个内存分配及其生命周期?不用了!

我们来做一番小总结:

Go语言


  • 快(就像你的 5 个 MAU 需要它一样哈哈哈)

  • 可靠的

  • 非常简单,语言永远不会成为你实现目标的障碍

  • 伟大的软件包生态系统


ZIG

  • 极速

  • 非常“傻”的简单


Rust

  • 极速

  • 可靠的

  • 不错的软件包生态系统


如果你是一名后端工程师,并且喜欢对自己的冲刺表现进行数字统计,那么最佳选择可能是GoLang。

“但是我的公司不使用GoLang——”
“离开公司,离开它,使用适合该工具的工作。离开这个公司。”

对我来说,GoLang 就像一场包办婚姻,你永远不会感到内心的匆忙,但它永远不会让你失望,它是你一生的伴侣,你可以把你的一生押注于它。

让我们快速看一下 Rust 中多线程安全的一个例子。

use std::thread;
fn main() { let mut counter = Box::new(0); // 32-bit integer allocated on the heap let mut handles = vec![];
for _ in 0..10 { let handle = thread::spawn(|| { *counter += 1; // trying to edit the value }); handles.push(handle); }
for handle in handles { handle.join().unwrap(); // WAITING FOR ALL THE THREADS TO COMPLETE }
println!("Result: {}", *counter);}

我们有一个堆分配的值,并且有 10 个线程试图独立修改它。

counter由于所有线程都会尝试同时修改变量,因此此代码访问变量是不安全的counter。

在其他语言中,类似的代码可以顺利编译并运行。

如果你以前在多线程环境中工作过,您的第一个想法可能是使用“互斥锁”来防止更新counter变量时出现竞争条件。

但由于人为错误,在大型代码库中很容易忽略这样的小事。

Rust 甚至不允许这个程序编译。

由于所有权和借用规则,编译器将检测到您counter在 for 循环第一次迭代中的第一个线程中进行可变借用...到目前为止还好...第二次迭代也想counter并行进行可变借用?不安全!引发编译时错误!!。

引用或“借用”其所有者的值,以进行变异/修改,这被称为“可变借用”

src/main.rs|8 col 36-38 error| cannot borrow `*counter` as mutable more than once at a time `*counter` was mutably borrowed here in the previous iteration of the loop

在这种情况下,其他语言根本不会给你任何错误。检查正确性是你的责任,但是 Rust 会阻止这种情况。

类似这样的结构在语言中广泛传播,可以帮助我们避免搬起石头砸自己的脚。

结语


我真的希望有一种语言可以解决这个问题,即使 GoLang 非常接近这个答案,但这确实取决于你的使用情况,最重要的是你的团队。


对于我来说,使用 Rust 是一种乐趣。

作者:聆听音乐的鱼

相关阅读:

21CTO
21CTO(21CTO.com),开发者的学习与服务平台。提供高品质文章、课程与训练营、招聘等产品。
 最新文章