Ownership, Borrowing & Lifetimes
Ownership and moves, references and borrowing, lifetimes, smart pointers (Box/Rc/Arc), and interior mutability with RefCell/Mutex.
Ownership and Moves
fn main() {
let s1 = String::from("hi");
let s2 = s1; // 移动:s1 失效(堆数据只有一个主人)
// println!("{}", s1); // ✗ borrow of moved value
println!("{}", s2);
// Copy 类型(栈上):i32、f64、bool、(i32, i32) 等直接复制
let a = 5;
let b = a; // a 仍可用
}
// 函数传参同样是移动(或借用)
fn takes(s: String) { /* s 在函数结束时 drop */ }
fn borrows(s: &String) { /* 只读,不获取所有权 */ }
fn gives() -> String { String::from("hi") } // 所有权交出
When the compile error 'does not live long enough' or 'borrow of moved value' appears, the fix is usually one of: borrow with &, clone if cheap, or restructure so ownership flows one direction.
Lifetimes
// 编译器需要证明返回的引用不会悬空
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
let s1 = String::from("long string");
let res; // res 的生命周期被 'a 约束
{
let s2 = String::from("x");
// res = longest(s1.as_str(), s2.as_str());
// ✗ s2 活不到 res 使用的地方
}
// println!("{}", res);
// 结构体持有引用必须标注
struct Parser<'a> {
src: &'a str,
}
let p = Parser { src: &s1 }; // p 不能比 s1 活得久
// 匿名生命周期 '_:签名里 'a 太吵时用来简化
fn first_word(s: &str) -> &str { s }
Lifetime elision handles the common cases (one input ref → same output ref); explicit annotations are needed when there are two or more input references or a struct holds references.
Smart Pointers
| 类型 | 语义 | 何时使用 |
|---|---|---|
| Box<T> | 独占堆分配 | 递归类型、大数据、trait 对象 |
| Rc<T> | 单线程引用计数 | 树/图结构,多个所有者 |
| Arc<T> | 原子引用计数 | 跨线程共享(线程安全版 Rc) |
| RefCell<T> | 运行时借用检查 | 单线程内部可变性 |
| Mutex<T> | 运行时互斥 | 跨线程内部可变性 |
| Cow<'a, T> | 写时克隆 | 可能修改也可能只读的输入 |
use std::sync::{Arc, Mutex};
// 多线程共享可变计数:Arc + Mutex
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(std::thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("{}", *counter.lock().unwrap()); // 10
// Box 构建 trait 对象(动态分发)
let animal: Box<dyn Animal> = Box::new(Dog);
Error Handling: Result and the ? Operator
use std::fs;
use std::io;
// ? 运算符:出错即早退
fn read_config(path: &str) -> io::Result<String> {
let raw = fs::read_to_string(path)?; // 失败则 return Err
Ok(raw.trim().to_string())
}
// 自定义错误:thiserror(库)或 anyhow(应用)
// #[derive(thiserror::Error, Debug)]
// pub enum ShopError { #[error("not found")] NotFound }
fn main() {
match read_config("app.toml") {
Ok(cfg) => println!("{}", cfg),
Err(e) => eprintln!("config error: {}", e),
}
}所有权与移动
fn main() {
let s1 = String::from("hi");
let s2 = s1; // 移动:s1 失效(堆数据只有一个主人)
// println!("{}", s1); // ✗ borrow of moved value
println!("{}", s2);
// Copy 类型(栈上):i32、f64、bool、(i32, i32) 等直接复制
let a = 5;
let b = a; // a 仍可用
}
// 函数传参同样是移动(或借用)
fn takes(s: String) { /* s 在函数结束时 drop */ }
fn borrows(s: &String) { /* 只读,不获取所有权 */ }
fn gives() -> String { String::from("hi") } // 所有权交出
遇到 'does not live long enough'、'borrow of moved value' 时,解法通常是三选一:加 & 借用、代价小就 clone、或把所有权的流向理成单向。
生命周期
// 编译器需要证明返回的引用不会悬空
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
let s1 = String::from("long string");
let res; // res 的生命周期被 'a 约束
{
let s2 = String::from("x");
// res = longest(s1.as_str(), s2.as_str());
// ✗ s2 活不到 res 使用的地方
}
// println!("{}", res);
// 结构体持有引用必须标注
struct Parser<'a> {
src: &'a str,
}
let p = Parser { src: &s1 }; // p 不能比 s1 活得久
// 匿名生命周期 '_:签名里 'a 太吵时用来简化
fn first_word(s: &str) -> &str { s }
生命周期省略规则覆盖了常见情形(单输入引用 → 同样的输出引用);两个及以上输入引用、或结构体持有引用时才需要显式标注。
智能指针
| 类型 | 语义 | 何时使用 |
|---|---|---|
| Box<T> | 独占堆分配 | 递归类型、大数据、trait 对象 |
| Rc<T> | 单线程引用计数 | 树/图结构,多个所有者 |
| Arc<T> | 原子引用计数 | 跨线程共享(线程安全版 Rc) |
| RefCell<T> | 运行时借用检查 | 单线程内部可变性 |
| Mutex<T> | 运行时互斥 | 跨线程内部可变性 |
| Cow<'a, T> | 写时克隆 | 可能修改也可能只读的输入 |
use std::sync::{Arc, Mutex};
// 多线程共享可变计数:Arc + Mutex
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(std::thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
}));
}
for h in handles { h.join().unwrap(); }
println!("{}", *counter.lock().unwrap()); // 10
// Box 构建 trait 对象(动态分发)
let animal: Box<dyn Animal> = Box::new(Dog);
错误处理:Result 与 ? 运算符
use std::fs;
use std::io;
// ? 运算符:出错即早退
fn read_config(path: &str) -> io::Result<String> {
let raw = fs::read_to_string(path)?; // 失败则 return Err
Ok(raw.trim().to_string())
}
// 自定义错误:thiserror(库)或 anyhow(应用)
// #[derive(thiserror::Error, Debug)]
// pub enum ShopError { #[error("not found")] NotFound }
fn main() {
match read_config("app.toml") {
Ok(cfg) => println!("{}", cfg),
Err(e) => eprintln!("config error: {}", e),
}
}