目录

引用、借用与生命周期

引用和借用

引用(Reference) 允许在不转移所有权的情况下访问值。创建引用称为借用(Borrowing)

不可变引用

fn main() {
    let s = String::from("hello");
 
    fn len(s: &String) -> usize {
        s.len() // 借用 s,不拥有所有权
    } // s 没有释放
 
    let length = len(&s);
    println!("'{}' 的长度是 {}", s, length); // s 仍然可用
}

规则:不可变引用可同时有多个。

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

可变引用

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

规则:同一时间只能有一个可变引用。

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

引用规则总结

fn main() {
    let mut s = String::from("hello");
 
    let r1 = &s; // OK
    let r2 = &s; // OK
    // let r3 = &mut s; // 编译错误:已有不可变引用
    println!("{}, {}", r1, r2);
    // r1 和 r2 不再使用
 
    let r3 = &mut s; // 现在 OK
    r3.push_str("!");
    println!("{}", r3);
}

字符串切片(Slice)

切片是对集合一部分的引用:

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

字符串切片类型 &str

fn main() {
    let s = String::from("hello world");
 
    // 取前5个字符
    let slice1 = &s[..5];   // 等同于 &s[0..5]
    let slice2 = &s[6..];   // 等同于 &s[6..len]
    let slice3 = &s[..];    // 整个字符串
 
    println!("{} {} {}", slice1, slice2, slice3);
}

数组切片

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

生命周期简介

生命周期(Lifetimes)确保引用始终有效。多数情况下编译器可以自动推断。

生命周期注解语法

// 'a 是生命周期参数,标明 x 和 y 必须活得一样长
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
 
fn main() {
    let s1 = String::from("长字符串");
    let s2 = String::from("短");
    let result = longest(&s1, &s2);
    println!("较长的: {}", result);
}

结构体中的生命周期

struct Excerpt<'a> {
    part: &'a str, // 结构体包含引用时必须标注生命周期
}
 
fn main() {
    let novel = String::from("很久很久以前...");
    let first = novel.split('.').next().expect("没有找到");
    let excerpt = Excerpt { part: first };
    println!("{}", excerpt.part);
}

静态生命周期

'static 生命周期在整个程序运行期间有效:

// 字符串字面量有 'static 生命周期
let s: &'static str = "我存在于整个程序运行期间";

本章小结