引用、借用与生命周期
引用和借用
引用(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); }
引用规则总结
- 同一个作用域中,一个值可以有多个不可变引用(
&T) - 同一个作用域中,一个值只能有一个可变引用(
&mut T) - 可变引用和不可变引用不能共存
- 引用必须始终有效(不会出现悬垂引用)
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 = "我存在于整个程序运行期间";
本章小结
- 引用允许访问值而不转移所有权
- 同一时间要么多个不可变引用,要么一个可变引用
- 切片是对集合部分元素的引用
- 生命周期注解确保引用不会悬垂
&'static表示整个程序都有效的引用