Skip to content

lifetimes.rs

文件信息

  • 📄 原文件:03_lifetimes.rs
  • 🔤 语言:rust

完整代码

rust
// ============================================================
//                      生命周期(Lifetimes)
// ============================================================
// 生命周期是 Rust 确保引用有效的机制
// 大多数情况下编译器可以自动推断(生命周期省略规则)
// 只有当编译器无法推断时才需要手动标注
//
// 【核心概念】生命周期标注不改变引用的实际生命周期,
//             它只是告诉编译器多个引用之间的关系

fn main() {
    println!("=== 生命周期 ===");

    // ----------------------------------------------------------
    // 1. 为什么需要生命周期
    // ----------------------------------------------------------
    // 当函数返回引用时,编译器需要知道返回的引用有效多久
    // 否则可能出现悬垂引用

    let string1 = String::from("长字符串");
    let result;
    {
        let string2 = String::from("短");
        result = longest(string1.as_str(), string2.as_str());
        println!("更长的: {}", result);
    }
    // 如果 result 引用了 string2,这里就会悬垂
    // 生命周期标注帮助编译器检查这种情况

    // ----------------------------------------------------------
    // 2. 生命周期标注语法
    // ----------------------------------------------------------
    // 使用 'a, 'b 等命名(以单引号开头)
    // 'a 读作"生命周期 a"
    // 标注在 & 后面:&'a str, &'a mut String

    let s1 = String::from("hello");
    let s2 = String::from("hi");
    let longer = longest(&s1, &s2);
    println!("较长: {}", longer);

    // ----------------------------------------------------------
    // 3. 生命周期省略规则(Elision Rules)
    // ----------------------------------------------------------
    // 编译器自动应用三条规则,能推断出就不需要手动标注:
    //
    // 规则1: 每个引用参数获得独立的生命周期
    //   fn foo(x: &str, y: &str) → fn foo<'a, 'b>(x: &'a str, y: &'b str)
    //
    // 规则2: 如果只有一个输入生命周期,输出生命周期等于它
    //   fn foo(x: &str) -> &str → fn foo<'a>(x: &'a str) -> &'a str
    //
    // 规则3: 如果有 &self 或 &mut self,输出生命周期等于 self
    //   fn method(&self, x: &str) -> &str → 输出生命周期 = self

    // 自动推断的例子(不需要手动标注):
    let s = String::from("hello world");
    let first = first_word(&s);  // 编译器知道返回值生命周期 = 参数
    println!("第一个词: {}", first);

    // ----------------------------------------------------------
    // 4. 结构体中的生命周期
    // ----------------------------------------------------------
    // 如果结构体持有引用,必须标注生命周期
    // 含义:结构体实例不能比其引用的数据活得更久

    let novel = String::from("在很久很久以前. 一个...");
    let first_sentence;
    {
        let i = novel.find('.').unwrap_or(novel.len());
        first_sentence = ImportantExcerpt {
            part: &novel[..i],
        };
    }
    println!("摘录: {}", first_sentence.part);

    // 结构体方法中的生命周期(通常自动推断)
    println!("通告: {}", first_sentence.announce_and_return("重要!"));

    // ----------------------------------------------------------
    // 5. 静态生命周期('static)
    // ----------------------------------------------------------
    // 'static 表示引用在整个程序运行期间都有效
    // 【常见】字符串字面量都是 'static
    // 【警告】不要随意使用 'static 来"解决"生命周期问题

    let s: &'static str = "我是静态生命周期";
    println!("{}", s);

    // 字符串字面量存储在程序的二进制文件中,所以是 'static
    // const 常量引用也是 'static

    // ----------------------------------------------------------
    // 6. 生命周期约束
    // ----------------------------------------------------------
    // 'a: 'b 表示 'a 至少和 'b 一样长

    let s1 = String::from("long");
    let result;
    {
        let s2 = String::from("hi");
        result = longest_with_announcement(&s1, &s2, "比较中...");
        println!("结果: {}", result);
    }

    // ----------------------------------------------------------
    // 7. 常见生命周期模式
    // ----------------------------------------------------------
    println!("\n=== 常见模式 ===");

    // 模式1: 返回输入的引用
    let data = vec![1, 2, 3, 4, 5];
    let first = first_element(&data);
    println!("第一个元素: {}", first);

    // 模式2: 多个生命周期参数
    let s1 = "hello";
    let s2 = "world";
    let pair = StringPair::new(s1, s2);
    println!("StringPair: {} + {}", pair.first, pair.second);

    // 模式3: 生命周期 + 泛型 + trait bound
    let items = vec![
        String::from("banana"),
        String::from("apple"),
        String::from("cherry"),
    ];
    let longest_item = longest_item(&items);
    println!("最长项: {}", longest_item);

    println!("\n=== 生命周期结束 ===");
}

// ----------------------------------------------------------
// 需要手动标注生命周期的函数
// ----------------------------------------------------------
// 'a 表示:返回值的生命周期 = 两个参数中较短的那个
// 编译器无法自动推断,因为有两个输入引用
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// ----------------------------------------------------------
// 编译器可以自动推断的函数(不需要标注)
// ----------------------------------------------------------
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            return &s[..i];
        }
    }
    s
}

// ----------------------------------------------------------
// 结构体中的生命周期
// ----------------------------------------------------------
// 含义:ImportantExcerpt 实例不能比 part 引用的数据活得更久
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    // 规则3 自动推断:返回值生命周期 = &self
    fn announce_and_return(&self, announcement: &str) -> &str {
        println!("请注意: {}", announcement);
        self.part
    }
}

// ----------------------------------------------------------
// 生命周期 + 泛型 + trait bound
// ----------------------------------------------------------
fn longest_with_announcement<'a, T: std::fmt::Display>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str {
    println!("公告: {}", ann);
    if x.len() > y.len() { x } else { y }
}

// ----------------------------------------------------------
// 返回集合元素的引用
// ----------------------------------------------------------
fn first_element(v: &Vec<i32>) -> &i32 {
    &v[0]
}

// ----------------------------------------------------------
// 多个生命周期参数
// ----------------------------------------------------------
struct StringPair<'a, 'b> {
    first: &'a str,
    second: &'b str,
}

impl<'a, 'b> StringPair<'a, 'b> {
    fn new(first: &'a str, second: &'b str) -> StringPair<'a, 'b> {
        StringPair { first, second }
    }
}

// ----------------------------------------------------------
// 泛型 + 生命周期
// ----------------------------------------------------------
fn longest_item<'a>(items: &'a Vec<String>) -> &'a str {
    let mut longest = &items[0] as &str;
    for item in items {
        if item.len() > longest.len() {
            longest = item;
        }
    }
    longest
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布