name: rust-ownership-system user-invocable: false description: 当使用Rust的所有权系统时使用,包括所有权规则、借用、生命周期和内存安全。当处理Rust内存管理时使用。 allowed-tools:
- Bash
- Read
Rust所有权系统
掌握Rust独特的所有权系统,通过编译时检查提供内存安全,无需垃圾回收。
所有权规则
三个基本所有权规则:
- Rust中的每个值都有一个变量作为其所有者
- 同一时间只能有一个所有者
- 当所有者离开作用域时,值被丢弃
fn ownership_basics() {
// s 拥有这个 String
let s = String::from("hello");
// 所有权移动到 s2
let s2 = s;
// 错误:s 不再拥有该值
// println!("{}", s);
println!("{}", s2); // 正常
} // s2 在这里被丢弃,内存释放
移动语义
所有权转移(移动):
fn move_semantics() {
let s1 = String::from("hello");
// 所有权移动到函数
takes_ownership(s1);
// 错误:s1 不再有效
// println!("{}", s1);
}
fn takes_ownership(s: String) {
println!("{}", s);
} // s 在这里被丢弃
// 从函数返回所有权
fn gives_ownership() -> String {
String::from("hello")
}
fn main() {
let s = gives_ownership();
println!("{}", s);
}
栈类型的Copy trait:
fn copy_types() {
// 实现Copy的类型被复制,而不是移动
let x = 5;
let y = x; // x 复制到 y
println!("x: {}, y: {}", x, y); // 两者都有效
// Copy类型:整数、浮点数、布尔值、字符、由Copy类型组成的元组
let tuple = (1, 2.5, true);
let tuple2 = tuple;
println!("{:?} {:?}", tuple, tuple2); // 两者都有效
}
借用
不可变借用(引用):
fn immutable_borrow() {
let s1 = String::from("hello");
// 借用 s1(不可变引用)
let len = calculate_length(&s1);
println!("Length of '{}' is {}", s1, len); // s1 仍然有效
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s 离开作用域,但不丢弃值
// 允许多个不可变借用
fn multiple_immutable_borrows() {
let s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("{}, {}, {}", r1, r2, r3); // 正常
}
可变借用:
fn mutable_borrow() {
let mut s = String::from("hello");
// 可变借用
change(&mut s);
println!("{}", s); // "hello, world"
}
fn change(s: &mut String) {
s.push_str(", world");
}
// 同一时间只允许一个可变借用
fn mutable_borrow_rules() {
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // 错误:不能可变借用两次
println!("{}", r1);
}
// 不能混合可变和不可变借用
fn no_mix_borrows() {
let mut s = String::from("hello");
let r1 = &s; // 不可变借用
let r2 = &s; // 另一个不可变借用
// let r3 = &mut s; // 错误:当不可变借用时不能可变借用
println!("{} {}", r1, r2);
}
非词法生命周期(NLL):
fn non_lexical_lifetimes() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} {}", r1, r2);
// r1 和 r2 在此点后不再使用
// 正常:不可变借用已结束
let r3 = &mut s;
println!("{}", r3);
}
生命周期
生命周期注解:
// 生命周期 'a 确保返回的引用与两个输入一样长
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string");
let string2 = String::from("short");
let result = longest(&string1, &string2);
println!("Longest: {}", result);
}
结构体中的生命周期:
// 结构体持有引用,需要生命周期注解
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("{}", excerpt.part);
}
生命周期省略规则:
// 编译器在这些情况下推断生命周期:
// 规则1:每个引用参数获得自己的生命周期
fn first_word(s: &str) -> &str {
// 扩展:fn first_word<'a>(s: &'a str) -> &'a str
s.split_whitespace().next().unwrap_or("")
}
// 规则2:如果有一个输入生命周期,分配给所有输出
fn foo(s: &str) -> &str {
s
}
// 规则3:如果有 &self 或 &mut self,其生命周期分配给输出
impl<'a> ImportantExcerpt<'a> {
fn get_part(&self) -> &str {
// 扩展:fn get_part<'a>(&'a self) -> &'a str
self.part
}
}
静态生命周期:
// 'static 意味着引用在整个程序期间存在
fn static_lifetime() -> &'static str {
"This string is stored in binary"
}
// 字符串字面量有 'static 生命周期
let s: &'static str = "hello world";
智能指针
Box用于堆分配:
fn box_pointer() {
// 在堆上分配值
let b = Box::new(5);
println!("b = {}", b);
} // b 在离开作用域时释放
// 递归类型需要 Box
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn recursive_type() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
Rc用于引用计数:
use std::rc::Rc;
fn rc_example() {
let a = Rc::new(5);
// 克隆 Rc 指针,增加计数
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("Reference count: {}", Rc::strong_count(&a)); // 3
// 所有所有者必须离开作用域后值才被丢弃
}
// 在图结构中共享数据
enum RcList {
Cons(i32, Rc<RcList>),
Nil,
}
use RcList::{Cons as RcCons, Nil as RcNil};
fn shared_ownership() {
let a = Rc::new(RcCons(5, Rc::new(RcCons(10, Rc::new(RcNil)))));
// b 和 c 都引用 a
let b = RcCons(3, Rc::clone(&a));
let c = RcCons(4, Rc::clone(&a));
}
RefCell用于内部可变性:
use std::cell::RefCell;
fn refcell_example() {
let value = RefCell::new(5);
// 可变借用
*value.borrow_mut() += 1;
// 不可变借用
println!("Value: {}", value.borrow());
}
// 结合 Rc 和 RefCell 用于共享可变数据
use std::rc::Rc;
use std::cell::RefCell;
fn rc_refcell() {
let value = Rc::new(RefCell::new(5));
let a = Rc::clone(&value);
let b = Rc::clone(&value);
*a.borrow_mut() += 10;
*b.borrow_mut() += 20;
println!("Value: {}", value.borrow()); // 35
}
所有权模式
获取所有权 vs 借用:
// 当需要消耗值时获取所有权
fn consume(s: String) {
println!("{}", s);
}
// 当只需要读取时借用
fn read(s: &String) {
println!("{}", s);
}
// 当需要修改时可变借用
fn modify(s: &mut String) {
s.push_str(" modified");
}
fn main() {
let mut s = String::from("hello");
read(&s); // 仍然拥有 s
modify(&mut s); // 仍然拥有 s
consume(s); // 不再拥有 s
}
构建器模式与所有权:
struct Config {
name: String,
value: i32,
}
struct ConfigBuilder {
name: Option<String>,
value: Option<i32>,
}
impl ConfigBuilder {
fn new() -> Self {
ConfigBuilder {
name: None,
value: None,
}
}
// 获取所有权并返回所有权
fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
fn value(mut self, value: i32) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Config {
Config {
name: self.name.unwrap_or_default(),
value: self.value.unwrap_or(0),
}
}
}
fn main() {
let config = ConfigBuilder::new()
.name(String::from("app"))
.value(42)
.build();
}
切片类型
字符串切片:
fn string_slices() {
let s = String::from("hello world");
// 切片引用字符串的一部分
let hello = &s[0..5];
let world = &s[6..11];
// 简写
let hello = &s[..5];
let world = &s[6..];
let whole = &s[..];
println!("{} {}", hello, world);
}
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 array_slices() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // &[i32]
assert_eq!(slice, &[2, 3]);
}
Clone vs Copy
理解Clone trait:
#[derive(Clone)]
struct Point {
x: f64,
y: f64,
}
fn clone_example() {
let p1 = Point { x: 1.0, y: 2.0 };
// 显式克隆(深拷贝)
let p2 = p1.clone();
// 两者都有效
println!("{} {}", p1.x, p2.x);
}
Copy trait限制:
// Copy 要求所有字段实现 Copy
#[derive(Copy, Clone)]
struct Coord {
x: i32,
y: i32,
}
// 不能为包含 String 字段的类型派生 Copy
// #[derive(Copy, Clone)] // 错误
struct Person {
name: String, // String 不实现 Copy
}
Drop Trait
使用Drop自定义清理:
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: {}", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created");
} // d 被丢弃,然后 c 被丢弃
手动drop:
fn manual_drop() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("Before drop");
drop(c); // 手动提前丢弃
println!("After drop");
}
何时使用此技能
使用 rust-ownership-system 当您需要:
- 理解Rust的内存管理模型
- 编写无需垃圾回收的内存安全代码
- 处理函数间的所有权转移
- 使用引用和借用
- 实现带有生命周期参数的结构体
- 使用智能指针(Box、Rc、RefCell)
- 调试借用检查器错误
- 选择所有权、借用和克隆
- 实现自定义Drop行为
- 安全地使用切片和引用
最佳实践
- 可能时优先借用而非所有权转移
- 默认使用不可变借用,仅在需要时使用可变借用
- 尽可能缩小借用范围
- 当编译器能推断生命周期时使用生命周期省略
- 根据用例选择合适的智能指针
- 在性能关键代码中避免使用RefCell
- 在函数签名中使用切片而非自有类型
- 仅在必要时克隆(它是显式和可见的)
- 为自定义清理逻辑实现Drop
- 让借用检查器错误指导您
常见陷阱
- 移动值后尝试使用它
- 同时创建多个可变借用
- 混合可变和不可变借用
- 返回局部变量的引用
- 对抗借用检查器而非理解它
- 过度使用clone()以避免所有权问题
- 不理解生命周期关系
- 使用Rc时的循环引用(使用Weak)
- 在运行时因RefCell借用违规而panic
- 错误使用 'static 生命周期