Go 和 Rust 的核心差异在于默认安全边界:Go 默认防止崩溃(如自动拷贝、内置Unicode处理),适合快速上线的I/O密集服务;Rust 默认防止歧义逻辑(如显式内存访问、所有权检查),适合长期运行、内存敏感的关键组件。

Go 和 Rust 不是“选哪个更好”的关系,而是“在什么场景下谁更不添麻烦”。如果你要写一个需要快速上线、团队里有大量中级开发者、并发逻辑以 I/O 等待为主的服务,Go 几乎不会卡你;但如果你要写一个长期运行、内存敏感、不能容忍数据竞争或未定义行为的组件(比如协议解析器、加密模块、嵌入式网关),Rust 的编译期检查会提前拦住大量 runtime 错误。
for 循环写法差异直接暴露设计哲学
Go 的 for range 是统一抽象:数组、切片、map、channel 都用同一套语法,for i, v := range items 中的 i 和 v 是拷贝值,安全但隐含开销;for _, v := range items 忽略索引时,v 仍是拷贝,无法原地修改原数据。
Rust 的 for 必须显式指定迭代器来源:
-
for x in arr→ 消耗arr,之后不能再用 -
for x in arr.iter()→ 借用只读引用,x类型是&T -
for x in arr.iter_mut()→ 可变借用,能改原值,但一次只能有一个可变引用 -
for (i, x) in arr.iter().enumerate()→ 才等价于 Go 的range索引+值
这不是语法糖多寡的问题,而是 Rust 强制你声明“我要怎么访问这块内存”——没声明清楚,编译就报错。Go 则默认帮你做最安全(也最保守)的拷贝。
字符串反转这种操作,Go 写起来顺手,Rust 却必须想清楚编码
Go 的 string 底层是只读字节序列,但 range 迭代的是 rune(UTF-8 解码后的 Unicode 码点)。所以反转字符串必须先转成 []rune,再交换,否则按字节翻会破坏 UTF-8 编码。
Rust 的 String 也是 UTF-8 编码,但没有内置 rune 概念。你要反转,得引入 unicode-segmentation crate,用 UnicodeSegmentation::graphemes() 拆分字形簇,再 collect + reverse —— 否则直接 chars().rev() 只能保证字符级反转,对 emoji 或带修饰符的字符仍可能出错。
也就是说:Go 把常见 Unicode 处理封装进语言原语;Rust 把选择权留给你,但你要自己承担拆分策略的责任。
闭包捕获变量时,Go 默认拷贝,Rust 默认借用
Go 中这个经典陷阱:
for _, b := range []int{1, 2, 3} {
go func() {
fmt.Println("job", b) // 全部输出 3
}()
}
原因是 b 是循环变量,每次迭代都复用同一块栈内存,所有 goroutine 最终读到的是最后一次赋值后的值。修复方式是传参:go func(b int) { ... }(b)。
Rust 中类似写法根本过不了编译:
let items = vec![1, 2, 3];
for b in &items {
std::thread::spawn(|| {
println!("job {}", b); // error: `b` does not live long enough
});
}
因为 b 是 &i32,生命周期只到本轮循环结束,而 spawn 要求闭包能 `'static`。你必须显式克隆或转为 owned 类型:let b = *b; std::thread::spawn(move || println!("job {}", b));。Rust 不让你“无意中写出错误”,但也要求你明确说出“我打算怎么移交所有权”。
真正容易被忽略的不是语法差异,而是两者对“默认安全边界”的定义不同:Go 默认保护你免于崩溃,Rust 默认保护你免于写出有歧义的逻辑。前者让你快,后者让你稳,但稳的前提是你愿意花时间跟编译器对话。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











