又一个 Rust 所有权教程
Source: Dev.to
在 Rust 中最重要的概念之一是所有权(ownership)和借用(borrowing)。大量文章专门讨论这个细分主题;本教程尝试通过示例来解释该概念。
所有权是一套规则,决定 Rust 程序如何管理内存。Rust 使用所有权系统并在编译期进行检查;如果违反任何规则,程序将无法编译,并且没有运行时开销。
对所有权的初体验
Java 版
public void own(String text) {}
public static void main(String[] args) {
var text = "my text";
own(text);
System.out.println(text);
}
Rust 翻译
fn own(_: String) {}
fn main() {
let text: String = String::from("my text"); // 1
own(text);
println!("{}", text);
}
编译器报错:
error[E0382]: borrow of moved value: `text`
--> src/main.rs:11:20
|
9 | let text: String = String::from("my text");
| ---- move occurs because `text` has type `String`, which does not implement the `Copy` trait
10| own(text);
| ---- value moved here
11| println!("{}", text);
| ^^^^ value borrowed here after move
...
help: consider cloning the value if the performance cost is acceptable
10| own(text.clone());
own 函数取得了 text 的所有权,因此之后不能再使用 text。
Clone 与 Copy
如果我们遵循编译器的建议:
fn own(text: String) {}
fn main() {
let text = String::from("my text");
own(text.clone()); // 深拷贝
println!("{}", text);
}
String 实现了 Clone,会创建一次深拷贝。这会使该值的内存使用翻倍。
当一个类型同时实现了 Copy 时,值会隐式复制,我们无需调用 clone():
#[derive(Debug, Copy, Clone)]
struct Dummy {}
fn own(_: Dummy) {}
fn main() {
let dummy = Dummy {};
own(dummy); // 自动复制
println!("{:?}", dummy); // 仍然可以使用
}
注意: 为大型结构体实现
Copy可能会消耗大量内存,因为每一次移动都会产生一次复制。
通过引用传递
传递值会移动所有权。若想让函数借用该值,需要使用 & 传递引用:
#[derive(Debug)]
struct Dummy {}
fn borrow(_: &Dummy) {}
fn main() {
let dummy = Dummy {};
borrow(&dummy); // 借用,而非移动
println!("{:?}", dummy); // 仍然可以使用
}
通过引用读取字段:
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn borrow(dummy: &Dummy) {
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
borrow(&dummy);
println!("{:?}", dummy); // Dummy { foo: "Foo" }
}
修改数据
在没有 mut 的情况下尝试修改
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar"); // 错误
}
编译错误:
error[E0594]: cannot assign to `dummy.foo`, as `dummy` is not declared as mutable
--> src/main.rs:8:5
|
8 | dummy.foo = String::from("Bar");
| ^^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
7 | let mut dummy = Dummy { foo: String::from("Foo") };
将变量声明为可变
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar");
}
通过值参数进行修改
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(mut dummy: Dummy) {
dummy.foo = String::from("Bar");
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
mutate(dummy);
}
通过可变引用进行修改
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(dummy: &mut Dummy) {
dummy.foo = String::from("Bar");
println!("Inside: {:?}", dummy.foo);
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
mutate(&mut dummy);
println!("Outside: {:?}", dummy.foo);
}
小结
| 目标 | 实现方式 |
|---|---|
| 获取所有权 | 按值传递 |
| 复制值 | 实现 Clone 并调用 clone() |
| 自动复制值 | 实现 Copy(隐含实现 Clone) |
| 借用(只读) | 传递引用 &T |
| 修改局部变量 | 将其声明为 mut |
| 通过引用修改 | 使用 &mut T 并将绑定声明为 mut |
最初发表于 A Java Geek(2025 年 12 月 7 日)