Rust 的所有权规则读起来像限制,实际目标却很务实:在编译期确定一块内存由谁释放、谁可以读取、谁可以修改,从而避免悬空指针和数据竞争。
先看一次移动:
fn print_name(name: String) {
println!("{name}");
}
fn main() {
let name = String::from("Ada");
print_name(name);
// println!("{name}"); // name 已经被移动
}
String 管理堆内存。传入函数后,所有权移动给参数,函数结束时内存被释放。若函数只需读取内容,更合适的签名是借用:
fn print_name(name: &str) {
println!("{name}");
}
fn main() {
let name = String::from("Ada");
print_name(&name);
println!("still here: {name}");
}
需要修改时,使用可变借用:
fn normalize(value: &mut String) {
value.make_ascii_lowercase();
}
fn main() {
let mut language = String::from("RUST");
normalize(&mut language);
println!("{language}");
}
同一作用域里可以有多个不可变借用,或者一个可变借用,但不能让二者在使用期重叠。这个规则保证“有人修改时,没有其他读者看到半成品”。
设计函数接口时,我会先问:函数是否需要取得并长期保存这个值?如果是,接收所有权;如果只读,优先接收引用;如果要原地修改,接收可变引用。多数所有权问题,在函数签名表达清楚意图后都会简单很多。