Rust 所有权、借用与生命周期实战入门
wxk1991 Lv5

Rust 所有权、借用与生命周期实战入门

Rust 最特别的地方不是语法,而是它把内存管理规则前移到了编译期。刚开始写 Rust 时,很多人会觉得编译器很严格,但真正理解所有权之后,会发现它是在帮你提前挡住很多运行时问题。


一、所有权是什么

Rust 中每个值都有一个所有者,同一时间只能有一个所有者。当所有者离开作用域,值就会被自动释放。

1
2
3
4
fn main() {
let name = String::from("rust");
println!("{}", name);
}

name 离开 main 作用域后,对应的堆内存会被释放,不需要手动 free,也不需要 GC。


二、移动语义

对于 String 这类拥有堆内存的数据,赋值会发生移动:

1
2
3
let a = String::from("hello");
let b = a;
// println!("{}", a); // 这里会编译失败

a 的所有权已经移动给 b,所以不能继续使用 a。这避免了两个变量同时释放同一块内存的问题。


三、借用解决传参问题

如果只是读取数据,不必转移所有权,可以使用引用:

1
2
3
4
5
6
7
8
9
fn print_name(name: &String) {
println!("{}", name);
}

fn main() {
let name = String::from("rust");
print_name(&name);
println!("{}", name);
}

这叫不可变借用。函数可以读取 name,但不会拿走它。


四、可变借用的限制

可变借用允许修改数据,但同一时间只能存在一个可变引用:

1
2
3
let mut count = 1;
let r = &mut count;
*r += 1;

这个限制看起来麻烦,本质上是在防止数据竞争。尤其在并发环境里,它非常有价值。


五、生命周期什么时候需要写

多数情况下,生命周期可以由编译器自动推断。只有当函数返回引用,并且编译器无法判断引用来自哪里时,才需要显式标注:

1
2
3
fn longest<'a>(left: &'a str, right: &'a str) -> &'a str {
if left.len() > right.len() { left } else { right }
}

'a 表示返回值的生命周期不会超过两个参数中较短的那个。


六、学习建议

不要一开始就死磕复杂生命周期。先掌握三个判断:

  • 数据是谁创建的
  • 函数是否需要拥有它
  • 是读取、修改,还是返回引用

当你能回答这三个问题,Rust 的所有权模型就会从“编译器刁难我”变成“编译器替我守门”。