引用与借用 #

一、引用概述 #

引用允许你使用值但不获取其所有权。创建引用的行为称为借用

1.1 基本引用 #

rust
fn main() {
    let s1 = String::from("hello");
    
    let len = calculate_length(&s1);  // 传递引用
    
    println!("'{}' 的长度是 {}", s1, len);  // s1 仍然有效
}

fn calculate_length(s: &String) -> usize {
    s.len()
}  // s 离开作用域,但因为它只是引用,不会释放数据

1.2 借用的含义 #

  • 借用:临时使用值,不获取所有权
  • 引用:指向数据的指针
  • 借用结束后,数据仍然属于原所有者

二、不可变引用 #

2.1 创建不可变引用 #

使用 & 创建不可变引用:

rust
fn main() {
    let s = String::from("hello");
    
    let r1 = &s;  // 不可变引用
    let r2 = &s;  // 另一个不可变引用
    
    println!("{} {}", r1, r2);
    println!("原值: {}", s);
}

2.2 多个不可变引用 #

可以同时存在多个不可变引用:

rust
fn main() {
    let s = String::from("hello");
    
    let r1 = &s;
    let r2 = &s;
    let r3 = &s;
    
    println!("{} {} {}", r1, r2, r3);
}

2.3 引用的作用域 #

引用的作用域从声明开始,到最后一次使用结束:

rust
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;
    println!("r1: {}", r1);  // r1 最后一次使用
    
    // r1 作用域结束
    
    let r2 = &mut s;  // 正确:r1 已不再使用
    println!("r2: {}", r2);
}

三、可变引用 #

3.1 创建可变引用 #

使用 &mut 创建可变引用:

rust
fn main() {
    let mut s = String::from("hello");
    
    change(&mut s);
    
    println!("{}", s);
}

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

3.2 可变引用的限制 #

同一时刻只能有一个可变引用:

rust
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &mut s;
    // let r2 = &mut s;  // 错误:不能同时有两个可变引用
    
    println!("{}", r1);
}

3.3 可变引用与不可变引用 #

可变引用存在时,不能有不可变引用:

rust
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;
    let r2 = &s;
    // let r3 = &mut s;  // 错误:已有不可变引用
    
    println!("{} {}", r1, r2);
}

3.4 非重叠作用域 #

引用的作用域可以不重叠:

rust
fn main() {
    let mut s = String::from("hello");
    
    {
        let r1 = &mut s;
        println!("{}", r1);
    }  // r1 作用域结束
    
    let r2 = &mut s;  // 正确
    println!("{}", r2);
}

四、借用规则 #

4.1 核心规则 #

Rust的借用规则:

  1. 任意时刻,要么只能有一个可变引用,要么只能有多个不可变引用
  2. 引用必须始终有效(不能有悬垂引用)

4.2 规则图解 #

text
┌─────────────────────────────────────────┐
│           借用规则                       │
├─────────────────────────────────────────┤
│  可以:多个不可变引用                    │
│  ┌───┐ ┌───┐ ┌───┐                     │
│  │&T │ │&T │ │&T │  ──→ 数据           │
│  └───┘ └───┘ └───┘                     │
├─────────────────────────────────────────┤
│  可以:一个可变引用                      │
│  ┌───────┐                              │
│  │&mut T │  ──→ 数据                    │
│  └───────┘                              │
├─────────────────────────────────────────┤
│  不可以:可变引用 + 不可变引用           │
│  ┌───┐ ┌───────┐                        │
│  │&T │ │&mut T │  ──→ 数据  ✗ 错误      │
│  └───┘ └───────┘                        │
└─────────────────────────────────────────┘

4.3 为什么有这些规则 #

防止数据竞争,数据竞争发生在以下情况:

  1. 两个或多个指针同时访问同一数据
  2. 至少有一个指针在写入
  3. 没有同步机制

五、悬垂引用 #

5.1 Rust 防止悬垂引用 #

Rust编译器确保引用永远有效:

rust
fn main() {
    let reference_to_nothing = dangle();
    println!("{}", reference_to_nothing);
}

// 错误示例
// fn dangle() -> &String {
//     let s = String::from("hello");
//     &s  // 返回悬垂引用
// }  // s 在这里被释放

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

5.2 编译器错误信息 #

text
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:16
  |
1 | fn dangle() -> &String {
  |                ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value,
          but there is no value for it to be borrowed from

六、引用与函数 #

6.1 传递引用 #

rust
fn main() {
    let s = String::from("hello");
    
    print_length(&s);
    println!("s 仍然有效: {}", s);
}

fn print_length(s: &String) {
    println!("长度: {}", s.len());
}

6.2 返回引用 #

返回引用需要确保引用的数据有效:

rust
fn main() {
    let s1 = String::from("hello");
    let s2 = String::from("world");
    
    let longer = longest(&s1, &s2);
    println!("更长的字符串: {}", longer);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

6.3 修改借用的值 #

rust
fn main() {
    let mut s = String::from("hello");
    
    append_world(&mut s);
    println!("{}", s);
}

fn append_world(s: &mut String) {
    s.push_str(", world");
}

七、解引用 #

7.1 解引用运算符 #

使用 * 解引用:

rust
fn main() {
    let x = 5;
    let y = &x;
    
    assert_eq!(5, x);
    assert_eq!(5, *y);
    
    println!("x = {}, *y = {}", x, *y);
}

7.2 自动解引用 #

Rust会自动解引用:

rust
fn main() {
    let s = String::from("hello");
    let r = &s;
    
    // 自动解引用
    println!("长度: {}", r.len());  // 等价于 (*r).len()
    println!("内容: {}", r);        // 自动解引用
}

7.3 可变解引用 #

rust
fn main() {
    let mut x = 5;
    let y = &mut x;
    
    *y += 1;  // 解引用并修改
    
    println!("x = {}", x);
}

八、引用与切片 #

8.1 字符串切片 #

rust
fn main() {
    let s = String::from("hello world");
    
    let hello = &s[0..5];
    let world = &s[6..11];
    
    println!("{} {}", hello, world);
}

8.2 数组切片 #

rust
fn main() {
    let arr = [1, 2, 3, 4, 5];
    
    let slice = &arr[1..3];
    
    println!("{:?}", slice);
}

九、实践示例 #

9.1 查找字符串 #

rust
fn main() {
    let text = String::from("hello world");
    
    let word = first_word(&text);
    
    println!("第一个单词: {}", word);
}

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

9.2 修改结构体 #

rust
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    fn scale(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }
}

fn main() {
    let mut rect = Rectangle {
        width: 10,
        height: 20,
    };
    
    println!("面积: {}", rect.area());
    
    rect.scale(2);
    
    println!("缩放后面积: {}", rect.area());
}

9.3 安全的数据访问 #

rust
fn main() {
    let mut data = vec![1, 2, 3, 4, 5];
    
    // 安全地读取和修改
    let first = &data[0];
    println!("第一个元素: {}", first);
    
    // first 作用域结束
    
    data.push(6);  // 正确:没有活跃的引用
    
    println!("数据: {:?}", data);
}

十、常见错误 #

10.1 同时持有可变和不可变引用 #

rust
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;
    let r2 = &s;
    // s.push_str(" world");  // 错误:已有不可变引用
    
    println!("{} {}", r1, r2);
}

10.2 返回局部变量的引用 #

rust
// 错误
// fn get_string() -> &String {
//     let s = String::from("hello");
//     &s  // 返回局部变量的引用
// }

// 正确
fn get_string() -> String {
    String::from("hello")
}

10.3 在迭代时修改集合 #

rust
fn main() {
    let mut v = vec![1, 2, 3, 4, 5];
    
    // 错误:不能在迭代时修改
    // for i in &v {
    //     v.push(*i + 1);
    // }
    
    // 正确:先收集,后修改
    let to_add: Vec<i32> = v.iter().map(|&x| x + 1).collect();
    v.extend(to_add);
    
    println!("{:?}", v);
}

十一、总结 #

本章学习了:

  • 引用与借用的概念
  • 不可变引用 &T
  • 可变引用 &mut T
  • 借用规则
  • 防止悬垂引用
  • 解引用操作
  • 引用与切片

引用和借用让Rust在不牺牲安全性的前提下实现高效的内存访问。下一章,我们将学习生命周期。

最后更新:2026-03-27