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}");
}

同一作用域里可以有多个不可变借用,或者一个可变借用,但不能让二者在使用期重叠。这个规则保证“有人修改时,没有其他读者看到半成品”。

设计函数接口时,我会先问:函数是否需要取得并长期保存这个值?如果是,接收所有权;如果只读,优先接收引用;如果要原地修改,接收可变引用。多数所有权问题,在函数签名表达清楚意图后都会简单很多。