引用与借用

23 阅读2分钟

引用与解引用

  • 引用是指向内存地址的指针类型。
  • 使用 & 符号创建引用,并用 * 进行解引用。

引用与解引用示例

fn main() {
    let x = 5;
    let y = &x;

    assert_eq!(5, x);
    assert_eq!(5, *y); // 使用解引用来访问 y 指向的值
}

不可变引用

  • 不可变引用允许读取但不修改数据。

不可变引用示例

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

可变引用

  • 可变引用允许修改引用的数据。
  • 同一作用域内,对特定数据只能存在一个可变引用。

可变引用示例

fn main() {
    let mut s = String::from("hello");

    change(&mut s);

    println!("{}", s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

可变引用限制

  • 可变引用与不可变引用不能同时存在。
  • 可变引用在同一作用域内只能有一个。

可变引用错误示例

fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &s; // 错误:不能在存在可变引用的同时创建不可变引用

    println!("{} and {}", r1, r2);
}

NLL(Non-Lexical Lifetimes)

  • Rust 编译器优化,允许在作用域结束前不再使用的引用结束。
  • 有助于解决引用作用域与变量作用域不一致的问题。

悬垂引用(Dangling References)

  • 悬垂引用指向的内存可能不存在或已被其他变量使用。
  • Rust 编译器确保引用永远不会是悬垂状态。

悬垂引用示例

fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");

    &s // 错误:返回悬垂引用
}

// 正确做法:返回 String 的所有权
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}

借用规则总结

  • 同一时刻,只能有一个可变引用或多个不可变引用。
  • 引用必须总是有效的。

总结

  • 引用与借用是 Rust 中管理内存的重要概念。
  • 不可变引用允许共享但不允许修改。
  • 可变引用允许修改但同一时间只能有一个。
  • NLL 优化了引用作用域,使其更加安全和灵活。
  • Rust 编译器防止悬垂引用,确保内存安全。