文章目录

Rust 借用:& 引用与生命周期标注

发布于 2026-04-17 16:13:24 · 浏览 9 次 · 评论 0 条

Rust 借用:& 引用与生命周期标注

Rust 的所有权系统是其核心特性之一,其中 借用检查器 确保内存安全而不需要垃圾回收器。理解引用(&)和生命周期是掌握 Rust 的关键。

理解引用

在 Rust 中,传递 数据所有权通常意味着移动数据,这会导致原始变量失效。使用 引用可以避免这种情况。

let x = 5;
let y = &x; // y 是对 x 的引用

引用使用 & 符号创建,它允许你借用数据而不获取所有权。引用分为可变引用 (&mut) 和不可变引用 (&)。

创建 不可变引用:

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()
}

注意s1 仍然有效,因为它没有被移动。

引用与可变性

默认情况下,引用不可变。声明 可变引用需要使用 &mut

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

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

遵循 Rust 的规则:同一时间你只能有一个对特定数据的可变引用,或者任意数量的不可变引用,但不能同时有可变引用和不可变引用。

生命周期介绍

生命周期是 Rust 引用最重要的概念之一。定义 生命周期为引用的有效范围。理解 生命周期可以帮助你避免悬垂引用问题。

观察 这个会导致编译错误的代码:

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

编译器会报错,因为它无法确定返回的引用会存活多久。

添加 生命周期注解:

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

这里 'a 是一个生命周期参数,它告诉编译器返回的引用至少与输入的引用一样长。


生命周期规则

记住 三条主要规则:

  1. 每个引用 都有一个生命周期
  2. 函数或方法 的参数生命周期必须与返回值生命周期相关联
  3. 编译器 会使用这些规则确定代码是否有效

结构体中的生命周期

定义 包含引用的结构体时,必须为每个引用指定生命周期:

struct Book<'a> {
    title: &'a str,
    author: &'a str,
}

fn main() {
    let title = String::from("The Rust Programming Language");
    let author = String::from("Steve Klabnik");

    let book = Book {
        title: &title,
        author: &author,
    };

    println!("{} by {}", book.title, book.author);
}

方法定义中的生命周期

带有引用的结构体方法添加生命周期:

impl<'a> Book<'a> {
    fn summary(&self) -> &'a str {
        format!("{} by {}", self.title, self.author)
    }
}

静态生命周期

使用 'static 生命周期表示引用会一直存活到程序结束:

let s: &'static str = "I have a static lifetime.";

字符串字面量拥有 'static 生命周期,因为它们存储在程序的只读内存中。


实际应用示例

让我们构建 一个更复杂的例子,展示生命周期的实际应用:

struct Article<'a> {
    title: &'a str,
    content: &'a str,
    author: &'a str,
}

impl<'a> Article<'a> {
    fn new(title: &'a str, content: &'a str, author: &'a str) -> Self {
        Article {
            title,
            content,
            author,
        }
    }

    fn get_summary(&self) -> String {
        format!("{} by {}", self.title, self.author)
    }
}

fn find_longest_article<'a>(articles: Vec<&'a Article>) -> Option<&'a Article> {
    articles.iter().max_by_key(|article| article.content.len())
}

fn main() {
    let title = String::from("Rust Ownership");
    let content = String::from("In Rust, each value has a variable that’s its owner...");
    let author = String::from("Rust Team");

    let article1 = Article::new(&title, &content, &author);

    let title2 = String::from("Borrowing in Rust");
    let content2 = String::from("Rust's borrow checker ensures memory safety...");
    let author2 = String::from("Rust Community");

    let article2 = Article::new(&title2, &content2, &author2);

    let articles = vec![&article1, &article2];

    match find_longest_article(articles) {
        Some(article) => println!("Longest article: {}", article.get_summary()),
        None => println!("No articles found"),
    }
}

注意 我们如何在 find_longest_article 函数中使用生命周期参数,以确保返回的文章引用不会比输入的切片活得长。


常见借用错误

悬垂引用

避免 创建悬垂引用,即引用指向已经被释放的数据:

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

解决 方法是返回拥有权的值:

fn no_dangle() -> String {
    let s = String::from("hello");
    s // 返回 s,所有权被转移
}

可变引用与不可变引用冲突

理解 为什么不能同时使用可变和不可变引用:

let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &mut s; // 错误:不能同时有可变和不可变引用

多个可变引用

注意 同一时间只能有一个可变引用:

let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // 错误:不能同时有两个可变引用

生命省略规则

了解 Rust 的生命省略规则,让你编写更简洁的代码:

  1. 输入参数 生命周期:每个引用参数都有其自己的生命周期
  2. 方法:第一个参数的生命周期名为 self,且省略不写
  3. 返回类型
    • 如果只有一个输入生命周期,返回类型默认使用它
    • 如果有多个输入生命周期,且其中一个是 &self&mut self,返回类型默认使用 self 的生命周期

应用 生命省略规则:

// 生命周期省略前的代码
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    &s[..]
}

// 生命省略后的等效代码
fn first_word<'a>(s: &'a str) -> &'a str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    &s[..]
}

通过掌握 Rust 的引用和生命周期,你可以编写出既安全又高效的代码。练习 这些概念,逐步掌握 Rust 的内存管理系统。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文