
文章目录
- [第5章 所有权系统](#第5章 所有权系统)
-
- [5.1 所有权概念详解](#5.1 所有权概念详解)
-
- [5.1.1 所有权的基本规则](#5.1.1 所有权的基本规则)
- [5.1.2 栈与堆的内存模型](#5.1.2 栈与堆的内存模型)
- [5.1.3 Copy和Move语义](#5.1.3 Copy和Move语义)
- [5.1.4 所有权与函数调用](#5.1.4 所有权与函数调用)
- [5.2 引用与借用](#5.2 引用与借用)
-
- [5.2.1 不可变引用](#5.2.1 不可变引用)
- [5.2.2 可变引用](#5.2.2 可变引用)
- [5.2.3 高级引用模式](#5.2.3 高级引用模式)
- [5.3 切片类型](#5.3 切片类型)
-
- [5.3.1 字符串切片](#5.3.1 字符串切片)
- [5.3.2 数组切片](#5.3.2 数组切片)
- [5.3.3 切片与所有权系统](#5.3.3 切片与所有权系统)
- [5.4 所有权规则实战](#5.4 所有权规则实战)
-
- [5.4.1 常见所有权问题与解决方案](#5.4.1 常见所有权问题与解决方案)
- [5.4.2 所有权模式与最佳实践](#5.4.2 所有权模式与最佳实践)
- [5.4.3 性能优化与所有权](#5.4.3 性能优化与所有权)
第5章 所有权系统
5.1 所有权概念详解
5.1.1 所有权的基本规则
Rust的所有权系统是其最独特的特性,也是保证内存安全的核心机制。让我们从基础开始,深入理解这一革命性的概念。
rust
fn ownership_basic_rules() {
println!("=== 所有权基本规则 ===");
// 规则1: 每个值都有一个所有者
let s1 = String::from("hello"); // s1 是这个字符串的所有者
println!("s1 = {}", s1);
// 规则2: 同一时间只能有一个所有者
let s2 = s1; // 所有权从 s1 移动到 s2
// println!("{}", s1); // 编译错误!s1 不再拥有数据
// 规则3: 当所有者离开作用域时,值将被丢弃
{
let temp = String::from("temporary");
println!("临时字符串: {}", temp);
} // temp 离开作用域,内存被释放
// println!("{}", temp); // 编译错误!temp 已不存在
// 演示所有权的转移
demonstrate_ownership_transfer();
}
fn demonstrate_ownership_transfer() {
println!("\n=== 所有权转移演示 ===");
// 移动语义 - 对于堆分配的数据
let heap_string = String::from("堆上的数据");
let moved_string = heap_string; // 所有权转移
// println!("{}", heap_string); // 错误:heap_string 不再有效
// 克隆 - 创建数据的完整副本
let original = String::from("原始数据");
let cloned = original.clone(); // 创建深拷贝
println!("original = {}, cloned = {}", original, cloned); // 两者都有效
// 拷贝语义 - 对于栈上的数据
let x = 5;
let y = x; // 拷贝值,不是移动
println!("x = {}, y = {}", x, y); // 两者都有效
// 函数调用中的所有权转移
let data = String::from("重要数据");
take_ownership(data);
// println!("{}", data); // 错误:所有权已转移到函数中
// 返回值转移所有权
let returned_data = give_ownership();
println!("返回的数据: {}", returned_data);
}
fn take_ownership(s: String) {
println!("在函数中获取所有权: {}", s);
} // s 离开作用域,内存被释放
fn give_ownership() -> String {
let s = String::from("新创建的数据");
s // 所有权转移给调用者
}
fn ownership_and_functions() {
println!("\n=== 函数与所有权 ===");
// 1. 参数传递转移所有权
let s = String::from("hello");
takes_ownership(s); // s 的所有权移动进函数
// s 在这里不再有效
let x = 5;
makes_copy(x); // x 应该移动进函数,但 i32 是 Copy 的,所以后面可继续使用 x
// 2. 返回值转移所有权
let s1 = gives_ownership(); // gives_ownership 将返回值移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到函数中,返回值移给 s3
println!("s1 = {}, s3 = {}", s1, s3);
// 3. 返回多个值的所有权
let (s4, len) = calculate_length(s1);
println!("'{}' 的长度是 {}", s4, len);
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string 离开作用域,drop 被调用,内存被释放
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
} // some_integer 离开作用域,没有特别的事情发生
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string // 返回 some_string 并移出给调用者
}
fn takes_and_gives_back(a_string: String) -> String {
a_string // 返回 a_string 并移出给调用者
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length) // 返回字符串和其长度
}
5.1.2 栈与堆的内存模型
要深入理解所有权,必须首先理解Rust的栈和堆内存模型。
rust
fn stack_vs_heap_memory() {
println!("=== 栈与堆内存模型 ===");
// 栈内存:固定大小,快速分配和释放
let x = 5; // i32,存储在栈上
let y = 3.14; // f64,存储在栈上
let z = true; // bool,存储在栈上
println!("栈变量: x={}, y={}, z={}", x, y, z);
// 堆内存:动态大小,通过指针访问
let heap_string = String::from("这是一个堆分配的字符串");
let heap_vector = vec![1, 2, 3, 4, 5]; // 向量在堆上分配
println!("堆数据: {}, {:?}", heap_string, heap_vector);
// 内存布局分析
analyze_memory_layout();
// 性能对比
performance_comparison();
}
fn analyze_memory_layout() {
println!("\n=== 内存布局分析 ===");
use std::mem;
// 栈上类型的大小
println!("栈类型大小:");
println!(" i32: {} 字节", mem::size_of::<i32>());
println!(" f64: {} 字节", mem::size_of::<f64>());
println!(" bool: {} 字节", mem::size_of::<bool>());
println!(" 引用: {} 字节", mem::size_of::<&String>());
// 堆分配的类型
let empty_string = String::new();
let small_string = String::from("hi");
let large_string = String::from("这是一个很长的字符串");
println!("\n字符串内存使用:");
println!(" 空字符串: {} 字节(栈上)", mem::size_of_val(&empty_string));
println!(" 小字符串: {} 字节(栈上)", mem::size_of_val(&small_string));
println!(" 大字符串: {} 字节(栈上)", mem::size_of_val(&large_string));
// 字符串实际数据在堆上
println!(" 字符串容量: {}, {}, {}",
empty_string.capacity(),
small_string.capacity(),
large_string.capacity());
// 向量的内存布局
let empty_vec: Vec<i32> = vec![];
let small_vec = vec![1, 2, 3];
let large_vec: Vec<i32> = (0..100).collect();
println!("\n向量内存使用:");
println!(" 空向量: {} 字节(栈上)", mem::size_of_val(&empty_vec));
println!(" 小向量: {} 字节(栈上)", mem::size_of_val(&small_vec));
println!(" 大向量: {} 字节(栈上)", mem::size_of_val(&large_vec));
println!(" 向量容量: {}, {}, {}",
empty_vec.capacity(),
small_vec.capacity(),
large_vec.capacity());
}
fn performance_comparison() {
println!("\n=== 栈与堆性能对比 ===");
use std::time::Instant;
// 栈分配性能测试
let start = Instant::now();
for _ in 0..1_000_000 {
let _x = 42; // 栈分配
let _y = 3.14; // 栈分配
let _z = [0u8; 64]; // 栈分配(小数组)
}
let stack_duration = start.elapsed();
// 堆分配性能测试
let start = Instant::now();
for _ in 0..1_000_000 {
let _x = String::from("hello"); // 堆分配
let _y = vec![0u8; 64]; // 堆分配
let _z = Box::new([0u8; 1024]); // 堆分配(大数组)
}
let heap_duration = start.elapsed();
println!("栈分配耗时: {:?}", stack_duration);
println!("堆分配耗时: {:?}", heap_duration);
println!("堆分配比栈分配慢 {:.1} 倍",
heap_duration.as_nanos() as f64 / stack_duration.as_nanos() as f64);
// 所有权转移的性能影响
ownership_transfer_performance();
}
fn ownership_transfer_performance() {
println!("\n=== 所有权转移性能 ===");
use std::time::Instant;
// 测试1: 移动小数据(实际上只是复制指针)
let small_data = String::from("small");
let start = Instant::now();
for _ in 0..1_000_000 {
let _moved = small_data.clone(); // 使用clone来模拟移动,因为不能重复移动同一个值
}
let small_move_duration = start.elapsed();
// 测试2: 移动大数据
let large_data = vec![0u8; 10_000];
let start = Instant::now();
for _ in 0..1_000_000 {
let _moved = large_data.clone(); // 使用clone模拟
}
let large_move_duration = start.elapsed();
println!("小数据移动耗时: {:?}", small_move_duration);
println!("大数据移动耗时: {:?}", large_move_duration);
println!("移动操作本质是廉价的指针复制");
}
5.1.3 Copy和Move语义
理解Copy和Move语义是掌握所有权系统的关键。
rust
fn copy_vs_move_semantics() {
println!("=== Copy 与 Move 语义 ===");
// 1. Copy 类型 - 栈上数据
let x = 5;
let y = x; // 拷贝值
println!("Copy语义: x={}, y={}", x, y); // 两者都可用
// 2. Move 类型 - 堆上数据
let s1 = String::from("hello");
let s2 = s1; // 移动所有权
// println!("{}", s1); // 错误!s1 不再有效
println!("Move语义: s2={}", s2);
// 3. 哪些类型实现了 Copy trait
demonstrate_copy_types();
// 4. 哪些类型是 Move 语义
demonstrate_move_types();
// 5. 自定义类型的 Copy 行为
custom_type_copy_behavior();
}
fn demonstrate_copy_types() {
println!("\n=== Copy 类型示例 ===");
// 所有整数类型都是 Copy
let a: i8 = 1;
let b: i16 = 2;
let c: i32 = 3;
let d: i64 = 4;
let e: isize = 5;
// 所有浮点数类型都是 Copy
let f: f32 = 1.0;
let g: f64 = 2.0;
// 布尔类型是 Copy
let h: bool = true;
// 字符类型是 Copy
let i: char = 'a';
// 元组当所有元素都是 Copy 时也是 Copy
let j: (i32, f64) = (1, 2.0);
let k = j; // 拷贝
println!("元组拷贝: j={:?}, k={:?}", j, k);
// 数组当元素是 Copy 时也是 Copy
let l: [i32; 3] = [1, 2, 3];
let m = l; // 拷贝
println!("数组拷贝: l={:?}, m={:?}", l, m);
// 不可变引用是 Copy
let n = &42;
let o = n; // 拷贝引用
println!("引用拷贝: n={}, o={}", n, o);
}
fn demonstrate_move_types() {
println!("\n=== Move 类型示例 ===");
// String 是 Move 类型
let s1 = String::from("hello");
let s2 = s1; // 移动
// println!("{}", s1); // 错误!
// Vec 是 Move 类型
let v1 = vec![1, 2, 3];
let v2 = v1; // 移动
// println!("{:?}", v1); // 错误!
// Box 是 Move 类型
let b1 = Box::new(5);
let b2 = b1; // 移动
// println!("{}", b1); // 错误!
// 包含 Move 类型的元组也是 Move 类型
let t1 = (String::from("hello"), 42);
let t2 = t1; // 移动
// println!("{:?}", t1); // 错误!
// 自定义结构体默认是 Move 类型
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
let p1 = Person { name: String::from("Alice"), age: 30 };
let p2 = p1; // 移动
// println!("{:?}", p1); // 错误!
}
fn custom_type_copy_behavior() {
println!("\n=== 自定义类型的 Copy 行为 ===");
// 1. 默认情况下,自定义类型是 Move 语义
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 移动(但实际只是栈上复制,因为没有堆数据)
// println!("{:?}", p1); // 错误!虽然都是栈数据,但默认是Move
// 2. 显式实现 Copy trait
#[derive(Debug, Clone, Copy)]
struct CopyPoint {
x: i32,
y: i32,
}
let cp1 = CopyPoint { x: 1, y: 2 };
let cp2 = cp1; // 拷贝
println!("Copy类型: cp1={:?}, cp2={:?}", cp1, cp2); // 两者都可用
// 3. 实现 Copy 的条件
println!("\n实现Copy的条件:");
println!(" - 类型的所有组件都实现了Copy");
println!(" - 类型不包含任何Drop实现");
println!(" - 类型本身是POD(普通旧数据)");
// 4. 不能实现 Copy 的情况
#[derive(Debug)]
struct NonCopyPoint {
x: i32,
name: String, // String 不是 Copy,所以整个结构体也不能是 Copy
}
let ncp1 = NonCopyPoint { x: 1, name: String::from("test") };
let ncp2 = ncp1; // 移动
// println!("{:?}", ncp1); // 错误!
// 5. 检查类型是否实现了 Copy
check_copy_trait();
}
fn check_copy_trait() {
println!("\n=== 检查 Copy trait 实现 ===");
// 使用编译时检查
fn is_copy<T: Copy>() -> bool {
true
}
// 这些类型实现了 Copy
assert!(is_copy::<i32>());
assert!(is_copy::<f64>());
assert!(is_copy::<bool>());
assert!(is_copy::<char>());
// 这些类型没有实现 Copy
// assert!(is_copy::<String>()); // 编译错误
// assert!(is_copy::<Vec<i32>>()); // 编译错误
println!("基本类型检查完成");
}
5.1.4 所有权与函数调用
理解函数调用中所有权的转移是编写正确Rust代码的关键。
rust
fn ownership_and_function_calls() {
println!("=== 所有权与函数调用 ===");
// 1. 参数传递的所有权转移
let s = String::from("hello");
takes_ownership_detailed(s); // s 的所有权移动到函数中
// println!("{}", s); // 错误!s 不再有效
// 2. 返回值的所有权转移
let s1 = gives_ownership_detailed();
let s2 = String::from("world");
let s3 = takes_and_gives_back_detailed(s2); // s2 被移动
println!("s1 = {}, s3 = {}", s1, s3);
// 3. 避免所有权转移的模式
avoid_ownership_transfer();
// 4. 链式函数调用中的所有权
chained_function_calls();
}
fn takes_ownership_detailed(some_string: String) {
println!("函数获取所有权: {}", some_string);
} // some_string 离开作用域,drop 被调用
fn gives_ownership_detailed() -> String {
let some_string = String::from("hello");
some_string // 所有权转移给调用者
}
fn takes_and_gives_back_detailed(a_string: String) -> String {
a_string // 所有权转移给调用者
}
fn avoid_ownership_transfer() {
println!("\n=== 避免所有权转移的模式 ===");
// 模式1: 使用引用(后面会详细讲解)
let s = String::from("hello");
calculate_length_without_take(&s);
println!("仍然拥有字符串: {}", s); // s 仍然有效
// 模式2: 返回元组包含原始数据
let s = String::from("hello");
let (s, len) = calculate_length_and_return(s);
println!("'{}' 的长度是 {}", s, len); // s 仍然有效
// 模式3: 使用克隆
let s1 = String::from("hello");
let len = calculate_length_with_clone(s1.clone());
println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效
// 模式4: 使用 Copy 类型
let x = 5;
use_integer(x);
println!("整数仍然可用: {}", x); // x 仍然可用
}
fn calculate_length_without_take(s: &String) -> usize {
s.len()
}
fn calculate_length_and_return(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
fn calculate_length_with_clone(s: String) -> usize {
s.len()
} // s 被丢弃,但原始数据不受影响,因为传入的是克隆
fn use_integer(x: i32) {
println!("使用整数: {}", x);
}
fn chained_function_calls() {
println!("\n=== 链式函数调用中的所有权 ===");
// 1. 基本的链式调用
let result = String::from("hello")
.clone() // 创建副本以避免移动原始数据
.to_uppercase() // 返回新的String
.replace("E", "3"); // 返回新的String
println!("链式调用结果: {}", result);
// 2. 构建器模式中的所有权
let user = UserBuilder::new()
.name("Alice".to_string())
.age(30)
.email("alice@example.com".to_string())
.build();
println!("构建的用户: {:?}", user);
// 3. 错误处理链中的所有权
let parse_result = "42"
.parse::<i32>()
.map(|n| n * 2)
.map(|n| n.to_string());
match parse_result {
Ok(s) => println!("解析结果: {}", s),
Err(e) => println!("解析错误: {}", e),
}
}
#[derive(Debug)]
struct User {
name: String,
age: u32,
email: String,
}
struct UserBuilder {
name: Option<String>,
age: Option<u32>,
email: Option<String>,
}
impl UserBuilder {
fn new() -> Self {
UserBuilder {
name: None,
age: None,
email: None,
}
}
fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
fn age(mut self, age: u32) -> Self {
self.age = Some(age);
self
}
fn email(mut self, email: String) -> Self {
self.email = Some(email);
self
}
fn build(self) -> User {
User {
name: self.name.expect("名称必须设置"),
age: self.age.expect("年龄必须设置"),
email: self.email.expect("邮箱必须设置"),
}
}
}
5.2 引用与借用
5.2.1 不可变引用
引用允许你使用值但不获取其所有权,这是Rust中避免所有权转移的主要方式。
rust
fn references_and_borrowing() {
println!("=== 引用与借用 ===");
// 1. 基本引用使用
let s1 = String::from("hello");
let len = calculate_length_with_ref(&s1);
println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效
// 2. 引用与直接使用的区别
compare_reference_vs_direct();
// 3. 多个不可变引用
multiple_immutable_references();
// 4. 引用的作用域
reference_scope();
}
fn calculate_length_with_ref(s: &String) -> usize {
s.len()
} // s 离开作用域,但由于它是引用,不会丢弃它指向的数据
fn compare_reference_vs_direct() {
println!("\n=== 引用与直接使用的对比 ===");
let s = String::from("hello");
// 使用引用 - 不获取所有权
let len1 = calculate_length_with_ref(&s);
println!("使用引用后字符串仍然可用: {}", s);
// 直接使用 - 获取所有权
// let len2 = calculate_length_direct(s); // 这会移动s的所有权
// println!("{}", s); // 错误!s 不再有效
// 解决方案:使用克隆
let len2 = calculate_length_direct(s.clone());
println!("使用克隆后字符串仍然可用: {}", s);
println!("长度1: {}, 长度2: {}", len1, len2);
}
fn calculate_length_direct(s: String) -> usize {
s.len()
}
fn multiple_immutable_references() {
println!("\n=== 多个不可变引用 ===");
let s = String::from("hello");
// 可以同时有多个不可变引用
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("r1 = {}, r2 = {}, r3 = {}", r1, r2, r3);
// 引用的引用
let rr1 = &r1;
let rr2 = &r2;
println!("rr1 = {}, rr2 = {}", rr1, rr2);
// 在复杂数据结构中使用引用
use_references_in_data_structures();
}
fn use_references_in_data_structures() {
println!("\n=== 在数据结构中使用引用 ===");
#[derive(Debug)]
struct Book {
title: String,
author: String,
year: u32,
}
let book = Book {
title: String::from("Rust编程"),
author: String::from("张三"),
year: 2023,
};
// 结构体字段的引用
let title_ref = &book.title;
let author_ref = &book.author;
println!("书名: {}, 作者: {}", title_ref, author_ref);
// 在向量中存储引用
let books = vec![
Book { title: String::from("Book1"), author: String::from("Author1"), year: 2020 },
Book { title: String::from("Book2"), author: String::from("Author2"), year: 2021 },
Book { title: String::from("Book3"), author: String::from("Author3"), year: 2022 },
];
let book_refs: Vec<&Book> = books.iter().collect();
for book_ref in book_refs {
println!("{:?}", book_ref);
}
}
fn reference_scope() {
println!("\n=== 引用的作用域 ===");
let s = String::from("hello");
{
let r1 = &s;
println!("内部作用域引用: {}", r1);
} // r1 离开作用域
let r2 = &s;
println!("外部作用域引用: {}", r2);
// 引用的生命周期不能超过被引用数据的生命周期
// let dangling_ref = dangle(); // 这会编译错误
let valid_ref = no_dangle();
println!("有效的引用: {}", valid_ref);
}
// 这个函数会产生悬垂引用,无法编译
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 错误!返回局部变量的引用
// } // s 离开作用域并被丢弃
fn no_dangle() -> String {
let s = String::from("hello");
s // 直接返回所有权
}
5.2.2 可变引用
可变引用允许修改借用的数据,但受到严格的规则限制。
rust
fn mutable_references() {
println!("=== 可变引用 ===");
// 1. 基本可变引用
let mut s = String::from("hello");
change_string(&mut s);
println!("修改后的字符串: {}", s);
// 2. 可变引用的限制
mutable_reference_restrictions();
// 3. 可变引用的作用域
mutable_reference_scope();
// 4. 数据竞争预防
data_race_prevention();
}
fn change_string(s: &mut String) {
s.push_str(", world!");
}
fn mutable_reference_restrictions() {
println!("\n=== 可变引用的限制 ===");
let mut s = String::from("hello");
// 规则1: 同一时间只能有一个可变引用
let r1 = &mut s;
// let r2 = &mut s; // 错误!不能同时有两个可变引用
println!("第一个可变引用: {}", r1);
// 在第一个引用离开作用域后,可以创建新的可变引用
{
let r2 = &mut s;
r2.push_str(" world");
} // r2 离开作用域
let r3 = &mut s;
r3.push_str("!");
println!("最终字符串: {}", r3);
// 规则2: 不能同时有可变引用和不可变引用
let mut data = String::from("data");
let immutable_ref = &data;
// let mutable_ref = &mut data; // 错误!不能同时存在可变和不可变引用
println!("不可变引用: {}", immutable_ref);
// 在不可变引用离开作用域后,可以创建可变引用
// immutable_ref 不再被使用
let mutable_ref = &mut data;
mutable_ref.push_str(" modified");
println!("修改后的数据: {}", mutable_ref);
}
fn mutable_reference_scope() {
println!("\n=== 可变引用的作用域 ===");
let mut s = String::from("hello");
// 可变引用的作用域从声明开始,到最后一次使用结束
let r1 = &mut s;
r1.push_str(" world"); // r1 在这里最后一次使用
// r1 的作用域在这里结束
let r2 = &mut s; // 现在可以创建新的可变引用
r2.push_str("!");
println!("最终结果: {}", r2);
// 非词法作用域生命周期(NLL)示例
nll_example();
}
fn nll_example() {
println!("\n=== 非词法作用域生命周期(NLL)===");
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// r1 和 r2 的作用域在这里结束
let r3 = &mut s; // 现在可以创建可变引用
r3.push_str(" world");
println!("{}", r3);
}
fn data_race_prevention() {
println!("\n=== 数据竞争预防 ===");
// Rust在编译时防止数据竞争的三种情况:
// 1. 两个或多个指针同时访问同一数据
// 2. 至少有一个指针被用来写入数据
// 3. 没有同步数据访问的机制
let mut data = vec![1, 2, 3];
// 安全的使用模式
{
let reference1 = &data[0];
let reference2 = &data[1];
println!("安全访问: {}, {}", reference1, reference2);
} // 引用离开作用域
// 现在可以安全地修改数据
let mutable_ref = &mut data;
mutable_ref.push(4);
println!("修改后的数据: {:?}", mutable_ref);
// 演示数据竞争的危险(在Rust中会被编译器阻止)
demonstrate_data_race_danger();
}
fn demonstrate_data_race_danger() {
println!("\n=== 数据竞争危险演示 ===");
// 这个代码无法编译,演示了Rust如何防止数据竞争
let mut data = vec![1, 2, 3];
let first = &data[0]; // 不可变借用
// 如果允许下面的代码,可能会导致数据竞争:
// data.push(4); // 这可能会重新分配内存,使first成为悬垂指针
// 但在Rust中,编译器会阻止这种情况
println!("第一个元素: {}", first);
// 只有在first不再使用后,才能修改data
// data.push(4); // 取消注释会导致编译错误
}
5.2.3 高级引用模式
探索一些更复杂的引用使用模式和技巧。
rust
fn advanced_reference_patterns() {
println!("=== 高级引用模式 ===");
// 1. 引用与模式匹配
references_and_pattern_matching();
// 2. 引用在数据结构中的使用
references_in_data_structures();
// 3. 引用与迭代器
references_and_iterators();
// 4. 生命周期省略规则
lifetime_elision_rules();
}
fn references_and_pattern_matching() {
println!("\n=== 引用与模式匹配 ===");
// 1. 匹配引用
let value = 42;
let reference = &value;
match reference {
&val => println!("匹配值: {}", val),
}
// 2. 在模式中解构引用
let point = (10, 20);
let point_ref = &point;
match point_ref {
&(x, y) => println!("点坐标: ({}, {})", x, y),
}
// 3. 匹配可变引用
let mut value = 42;
let mut_ref = &mut value;
match mut_ref {
&mut val => {
// val 是 i32,不是引用
println!("可变引用中的值: {}", val);
}
}
// 4. 引用与if let
let optional_value = Some(42);
if let Some(ref value) = optional_value {
println!("可选值中的引用: {}", value);
}
// 5. 在结构体模式匹配中使用引用
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
let person = Person {
name: String::from("Alice"),
age: 30,
};
match &person {
&Person { ref name, age } => {
println!("姓名: {}, 年龄: {}", name, age);
}
}
}
fn references_in_data_structures() {
println!("\n=== 数据结构中的引用 ===");
// 1. 包含引用的结构体
#[derive(Debug)]
struct BookView<'a> {
title: &'a str,
author: &'a str,
year: u32,
}
let book_title = String::from("Rust权威指南");
let book_author = String::from("Steve Klabnik");
let book_view = BookView {
title: &book_title,
author: &book_author,
year: 2018,
};
println!("图书视图: {:?}", book_view);
// 2. 枚举中的引用
#[derive(Debug)]
enum Message<'a> {
Text(&'a str),
Number(&'a i32),
Pair(&'a str, &'a i32),
}
let text = "hello";
let number = 42;
let msg1 = Message::Text(&text);
let msg2 = Message::Number(&number);
let msg3 = Message::Pair(&text, &number);
println!("消息1: {:?}", msg1);
println!("消息2: {:?}", msg2);
println!("消息3: {:?}", msg3);
// 3. 向量中的引用
let numbers = vec![1, 2, 3, 4, 5];
let number_refs: Vec<&i32> = numbers.iter().collect();
println!("数字引用: {:?}", number_refs);
// 4. 切片中的引用模式
let slice = &[1, 2, 3, 4, 5];
match slice {
[first, middle @ .., last] => {
println!("第一个: {}, 中间: {:?}, 最后一个: {}", first, middle, last);
}
}
}
fn references_and_iterators() {
println!("\n=== 引用与迭代器 ===");
let numbers = vec![1, 2, 3, 4, 5];
// 1. 迭代不可变引用
println!("不可变引用迭代:");
for number in &numbers {
println!("数字: {}", number);
}
// 2. 迭代可变引用
let mut mutable_numbers = vec![1, 2, 3, 4, 5];
println!("可变引用迭代:");
for number in &mut mutable_numbers {
*number *= 2; // 解引用并修改
println!("加倍后: {}", number);
}
// 3. 迭代器方法中的引用
let sum: i32 = numbers.iter().sum();
println!("总和: {}", sum);
let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
println!("加倍后: {:?}", doubled);
// 4. 过滤引用
let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
println!("偶数: {:?}", evens);
// 5. 引用与消费迭代器
let first_even = numbers.iter().find(|&&x| x % 2 == 0);
match first_even {
Some(&x) => println!("第一个偶数: {}", x),
None => println!("没有偶数"),
}
}
fn lifetime_elision_rules() {
println!("\n=== 生命周期省略规则 ===");
// Rust有三条生命周期省略规则,允许在常见情况下省略显式生命周期注解
// 规则1: 每个引用参数都有自己的生命周期参数
fn rule1_example(s: &str) -> &str {
s
}
// 规则2: 如果只有一个输入生命周期参数,它被赋予所有输出生命周期参数
fn rule2_example(s: &str) -> &str {
s
}
// 规则3: 如果有多个输入生命周期参数,但其中一个是&self或&mut self,那么self的生命周期被赋予所有输出生命周期参数
struct Example {
value: String,
}
impl Example {
fn rule3_example(&self, s: &str) -> &str {
if s.is_empty() {
&self.value
} else {
s
}
}
}
let example = Example { value: String::from("default") };
let result1 = example.rule3_example("");
let result2 = example.rule3_example("hello");
println!("规则3结果1: {}", result1);
println!("规则3结果2: {}", result2);
// 无法应用省略规则的情况
fn no_elision_example<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
let s1 = "hello";
let s2 = "world";
let result = no_elision_example(s1, s2);
println!("需要显式生命周期: {}", result);
}
5.3 切片类型
5.3.1 字符串切片
字符串切片是对String中一部分的引用,是Rust中处理字符串的常用方式。
rust
fn string_slices() {
println!("=== 字符串切片 ===");
// 1. 基本字符串切片
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
println!("原始字符串: {}", s);
println!("切片1: '{}'", hello);
println!("切片2: '{}'", world);
// 2. 切片语法糖
let s = String::from("hello");
let slice1 = &s[..2]; // 从开始到索引2(不含)
let slice2 = &s[2..]; // 从索引2到结束
let slice3 = &s[..]; // 整个字符串
println!("切片语法糖: '{}', '{}', '{}'", slice1, slice2, slice3);
// 3. 字符串字面量就是切片
let literal = "hello world"; // &str 类型
println!("字符串字面量: {}", literal);
// 4. 字符串切片作为函数参数
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
let text = String::from("hello world");
let word = first_word(&text);
println!("第一个单词: {}", word);
// 5. 字符串切片的不可变性
string_slice_immutability();
// 6. 字符串切片与UTF-8
string_slices_and_utf8();
}
fn string_slice_immutability() {
println!("\n=== 字符串切片的不可变性 ===");
let mut s = String::from("hello world");
// 创建不可变切片
let word = &s[0..5];
println!("切片: {}", word);
// 可以修改原始字符串
s.push_str("!"); // 这行可以正常工作
println!("修改后的字符串: {}", s);
// 但不能在存在不可变切片时创建可变引用
// let mutable_slice = &mut s[6..]; // 错误!
// 在切片不再使用后,可以创建可变引用
// word 不再被使用
let mutable_slice = &mut s[6..];
mutable_slice.make_ascii_uppercase();
println!("修改切片后: {}", s);
}
fn string_slices_and_utf8() {
println!("\n=== 字符串切片与UTF-8 ===");
let s = "hello 世界 🦀";
// 字符迭代
println!("字符:");
for (i, c) in s.chars().enumerate() {
println!(" 位置 {}: '{}'", i, c);
}
// 字节迭代
println!("字节:");
for (i, &byte) in s.as_bytes().iter().enumerate() {
println!(" 位置 {}: 0x{:02x}", i, byte);
}
// 有效的切片范围
let hello = &s[0..5];
println!("有效的切片: '{}'", hello);
// 无效的切片范围(会在运行时panic)
// let invalid = &s[0..6]; // 这会在6处分割字符
// 安全的切片方法
safe_string_slicing();
}
fn safe_string_slicing() {
println!("\n=== 安全的字符串切片 ===");
let s = "hello 世界 🦀";
// 使用字符边界进行安全切片
fn safe_slice(s: &str, start: usize, end: usize) -> Option<&str> {
// 找到字符边界
let mut char_indices = s.char_indices();
let start_byte = char_indices.nth(start).map(|(i, _)| i);
let end_byte = char_indices.nth(end - start - 1).map(|(i, _)| i);
match (start_byte, end_byte) {
(Some(start), Some(end)) => Some(&s[start..end]),
(Some(start), None) => Some(&s[start..]),
_ => None,
}
}
if let Some(slice) = safe_slice(s, 0, 5) {
println!("安全切片1: '{}'", slice);
}
if let Some(slice) = safe_slice(s, 6, 8) {
println!("安全切片2: '{}'", slice);
}
// 使用标准库的get方法
if let Some(slice) = s.get(0..5) {
println!("get方法切片: '{}'", slice);
}
// 处理可能失败的切片
match s.get(0..100) {
Some(slice) => println!("长切片: '{}'", slice),
None => println!("切片超出范围"),
}
}
5.3.2 数组切片
数组切片是对数组或向量中一部分的引用,提供了灵活的数据访问方式。
rust
fn array_slices() {
println!("=== 数组切片 ===");
// 1. 基本数组切片
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..4]; // 索引1到3的元素
println!("原始数组: {:?}", arr);
println!("切片: {:?}", slice);
// 2. 向量切片
let vec = vec![1, 2, 3, 4, 5];
let vec_slice = &vec[2..];
println!("原始向量: {:?}", vec);
println!("向量切片: {:?}", vec_slice);
// 3. 可变数组切片
let mut mut_arr = [1, 2, 3, 4, 5];
let mut_slice = &mut mut_arr[1..4];
// 修改切片元素
for elem in mut_slice.iter_mut() {
*elem *= 2;
}
println!("修改后的数组: {:?}", mut_arr);
// 4. 切片的方法和特性
slice_methods_and_traits();
// 5. 多维切片
multi_dimensional_slices();
}
fn slice_methods_and_traits() {
println!("\n=== 切片的方法和特性 ===");
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let slice = &data[..];
// 基本属性
println!("切片长度: {}", slice.len());
println!("是否为空: {}", slice.is_empty());
println!("第一个元素: {:?}", slice.first());
println!("最后一个元素: {:?}", slice.last());
// 元素访问
if let Some(&element) = slice.get(2) {
println!("索引2的元素: {}", element);
}
// 切片迭代
println!("迭代切片:");
for &element in slice {
print!("{} ", element);
}
println!();
// 切片分割
let (left, right) = slice.split_at(5);
println!("分割: {:?} | {:?}", left, right);
// 切片块
println!("分块:");
for chunk in slice.chunks(3) {
println!(" 块: {:?}", chunk);
}
// 切片窗口
println!("滑动窗口:");
for window in slice.windows(3) {
println!(" 窗口: {:?}", window);
}
// 切片搜索
if let Some(index) = slice.iter().position(|&x| x == 5) {
println!("找到5在位置: {}", index);
}
// 切片排序(需要可变切片)
let mut sortable = [3, 1, 4, 1, 5, 9, 2, 6];
let sort_slice = &mut sortable[..];
sort_slice.sort();
println!("排序后: {:?}", sort_slice);
}
fn multi_dimensional_slices() {
println!("\n=== 多维切片 ===");
// 1. 二维数组切片
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// 获取行切片
let row_slice = &matrix[1..];
println!("行切片: {:?}", row_slice);
// 获取特定行的切片
let second_row = &matrix[1];
println!("第二行: {:?}", second_row);
// 2. 向量的向量切片
let vec_of_vecs = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
let vec_slice = &vec_of_vecs[0..2];
println!("向量切片: {:?}", vec_slice);
// 3. 复杂切片操作
complex_slice_operations();
}
fn complex_slice_operations() {
println!("\n=== 复杂切片操作 ===");
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 1. 条件分割
let partitioned: Vec<&[i32]> = data
.chunks(3)
.collect();
println!("按大小分割: {:?}", partitioned);
// 2. 重叠窗口
println!("重叠窗口:");
for window in data.windows(3) {
println!(" {:?}", window);
}
// 3. 切片模式匹配
match &data[..] {
[first, second, .., last] => {
println!("模式匹配: 第一个={}, 第二个={}, 最后一个={}", first, second, last);
}
_ => println!("切片太短"),
}
// 4. 切片与迭代器组合
let sum_of_evens: i32 = data
.chunks(2)
.filter_map(|chunk| chunk.first())
.filter(|&&x| x % 2 == 0)
.sum();
println!("每块第一个偶数的和: {}", sum_of_evens);
// 5. 自定义切片算法
custom_slice_algorithms();
}
fn custom_slice_algorithms() {
println!("\n=== 自定义切片算法 ===");
// 1. 滑动窗口最大值
fn sliding_window_max(arr: &[i32], k: usize) -> Vec<i32> {
if k > arr.len() {
return Vec::new();
}
let mut result = Vec::new();
for window in arr.windows(k) {
if let Some(&max) = window.iter().max() {
result.push(max);
}
}
result
}
let data = [1, 3, -1, -3, 5, 3, 6, 7];
let maxes = sliding_window_max(&data, 3);
println!("滑动窗口最大值: {:?}", maxes);
// 2. 切片旋转
fn rotate_slice<T: Copy>(slice: &mut [T], k: usize) {
let n = slice.len();
if n == 0 { return; }
let k = k % n;
slice.reverse();
slice[..k].reverse();
slice[k..].reverse();
}
let mut rotatable = [1, 2, 3, 4, 5];
rotate_slice(&mut rotatable, 2);
println!("旋转后: {:?}", rotatable);
// 3. 切片分区
fn partition_slice(slice: &mut [i32], pivot: i32) -> usize {
let mut i = 0;
for j in 0..slice.len() {
if slice[j] < pivot {
slice.swap(i, j);
i += 1;
}
}
i
}
let mut to_partition = [3, 1, 4, 1, 5, 9, 2, 6];
let pivot_index = partition_slice(&mut to_partition, 5);
println!("分区后: {:?}, 分割点: {}", to_partition, pivot_index);
}
5.3.3 切片与所有权系统
理解切片如何与所有权系统交互是编写正确Rust代码的关键。
rust
fn slices_and_ownership() {
println!("=== 切片与所有权系统 ===");
// 1. 切片的所有权语义
slice_ownership_semantics();
// 2. 切片与借用检查器
slices_and_borrow_checker();
// 3. 返回切片
returning_slices();
// 4. 切片与生命周期
slices_and_lifetimes();
}
fn slice_ownership_semantics() {
println!("\n=== 切片的所有权语义 ===");
// 切片本身不拥有数据,它只是数据的视图
let s = String::from("hello world");
let slice = &s[0..5]; // 创建切片
println!("原始字符串: {}", s);
println!("切片: {}", slice);
// 切片不会影响原始数据的所有权
let s2 = s; // 移动所有权
// println!("{}", slice); // 错误!slice 引用已无效
// 数组切片也是类似的
let arr = [1, 2, 3, 4, 5];
let arr_slice = &arr[1..4];
println!("原始数组: {:?}", arr);
println!("数组切片: {:?}", arr_slice);
// 移动数组后,切片也会失效
let moved_arr = arr;
// println!("{:?}", arr_slice); // 错误!
}
fn slices_and_borrow_checker() {
println!("\n=== 切片与借用检查器 ===");
let mut data = vec![1, 2, 3, 4, 5];
// 创建不可变切片
let slice = &data[1..4];
println!("切片: {:?}", slice);
// 在存在不可变切片时,不能修改原始数据
// data.push(6); // 错误!不能同时存在可变和不可变借用
// 在切片不再使用后,可以修改数据
// slice 不再被使用
data.push(6);
println!("修改后的数据: {:?}", data);
// 可变切片与借用检查
let mut_slice = &mut data[2..];
for elem in mut_slice.iter_mut() {
*elem *= 2;
}
println!("通过可变切片修改后: {:?}", data);
}
fn returning_slices() {
println!("\n=== 返回切片 ===");
// 1. 返回字符串切片
fn get_first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
let text = String::from("hello world");
let word = get_first_word(&text);
println!("第一个单词: {}", word);
// 2. 返回数组切片
fn get_middle_slice(arr: &[i32]) -> &[i32] {
let mid = arr.len() / 2;
let start = mid.saturating_sub(1);
let end = (mid + 1).min(arr.len());
&arr[start..end]
}
let numbers = [1, 2, 3, 4, 5, 6];
let middle = get_middle_slice(&numbers);
println!("中间切片: {:?}", middle);
// 3. 返回结构体中的切片
struct StringWrapper {
data: String,
}
impl StringWrapper {
fn as_slice(&self) -> &str {
&self.data
}
fn get_substring(&self, start: usize, end: usize) -> &str {
&self.data[start..end]
}
}
let wrapper = StringWrapper { data: String::from("hello world") };
let slice1 = wrapper.as_slice();
let slice2 = wrapper.get_substring(0, 5);
println!("完整切片: {}", slice1);
println!("子字符串: {}", slice2);
}
fn slices_and_lifetimes() {
println!("\n=== 切片与生命周期 ===");
// 1. 显式生命周期注解
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
let s1 = String::from("hello");
let s2 = "world";
let result = longest(&s1, s2);
println!("较长的字符串: {}", result);
// 2. 结构体中的生命周期
struct StringSlice<'a> {
data: &'a str,
}
impl<'a> StringSlice<'a> {
fn new(s: &'a str) -> Self {
StringSlice { data: s }
}
fn get_data(&self) -> &'a str {
self.data
}
}
let original = String::from("hello world");
let slice_wrapper = StringSlice::new(&original);
println!("包装的切片: {}", slice_wrapper.get_data());
// 3. 复杂的生命周期场景
complex_lifetime_scenarios();
}
fn complex_lifetime_scenarios() {
println!("\n=== 复杂生命周期场景 ===");
// 1. 多个生命周期参数
fn multiple_lifetimes<'a, 'b>(s1: &'a str, s2: &'b str) -> &'a str {
println!("第二个字符串: {}", s2);
s1
}
let s1 = String::from("hello");
let result;
{
let s2 = String::from("world");
result = multiple_lifetimes(&s1, &s2);
println!("结果: {}", result);
}
// result 仍然有效,因为它只依赖于 s1 的生命周期
// 2. 生命周期与迭代器
struct IterWrapper<'a, T> {
data: &'a [T],
index: usize,
}
impl<'a, T> IterWrapper<'a, T> {
fn new(data: &'a [T]) -> Self {
IterWrapper { data, index: 0 }
}
}
impl<'a, T> Iterator for IterWrapper<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.data.len() {
let item = &self.data[self.index];
self.index += 1;
Some(item)
} else {
None
}
}
}
let numbers = [1, 2, 3, 4, 5];
let wrapper = IterWrapper::new(&numbers);
println!("自定义迭代器:");
for num in wrapper {
print!("{} ", num);
}
println!();
// 3. 静态生命周期
static_lifetime_examples();
}
fn static_lifetime_examples() {
println!("\n=== 静态生命周期 ===");
// 'static 生命周期表示数据在整个程序运行期间都有效
let static_string: &'static str = "这是一个静态字符串";
println!("静态字符串: {}", static_string);
// 函数返回 'static 生命周期
fn get_static_str() -> &'static str {
"hello world"
}
let s = get_static_str();
println!("函数返回的静态字符串: {}", s);
// 在数据结构中使用 'static
struct StaticHolder {
data: &'static str,
}
let holder = StaticHolder { data: "静态数据" };
println!("静态持有者: {}", holder.data);
// 注意:不是所有字符串字面量都必须是 'static
// 但在大多数情况下,字符串字面量确实有 'static 生命周期
}
5.4 所有权规则实战
5.4.1 常见所有权问题与解决方案
通过实际案例学习如何解决常见的所有权问题。
rust
fn common_ownership_problems() {
println!("=== 常见所有权问题与解决方案 ===");
// 问题1: 使用已移动的值
problem_use_after_move();
// 问题2: 多重可变借用
problem_multiple_mutable_borrows();
// 问题3: 悬垂引用
problem_dangling_references();
// 问题4: 迭代器无效化
problem_iterator_invalidation();
// 问题5: 自引用结构体
problem_self_referential_structs();
}
fn problem_use_after_move() {
println!("\n=== 问题1: 使用已移动的值 ===");
// 错误示例
let s1 = String::from("hello");
let s2 = s1; // 所有权转移
// println!("{}", s1); // 编译错误!s1 不再有效
// 解决方案1: 使用克隆
let s1 = String::from("hello");
let s2 = s1.clone(); // 创建副本
println!("解决方案1: s1={}, s2={}", s1, s2);
// 解决方案2: 使用引用
let s1 = String::from("hello");
let s2 = &s1; // 借用
println!("解决方案2: s1={}, s2={}", s1, s2);
// 解决方案3: 实现Copy trait(仅适用于简单类型)
#[derive(Copy, Clone, Debug)]
struct Point { x: i32, y: i32 }
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 拷贝,不是移动
println!("解决方案3: p1={:?}, p2={:?}", p1, p2);
}
fn problem_multiple_mutable_borrows() {
println!("\n=== 问题2: 多重可变借用 ===");
let mut data = vec![1, 2, 3];
// 错误示例
// let ref1 = &mut data;
// let ref2 = &mut data; // 编译错误!不能同时有两个可变引用
// 解决方案1: 使用作用域分隔
{
let ref1 = &mut data;
ref1.push(4);
} // ref1 离开作用域
let ref2 = &mut data;
ref2.push(5);
println!("解决方案1: {:?}", data);
// 解决方案2: 分割可变引用
let mut data = vec![1, 2, 3, 4, 5];
let (left, right) = data.split_at_mut(2);
left[0] = 10;
right[0] = 20;
println!("解决方案2: {:?}", data);
// 解决方案3: 内部可变性(后面会详细讲解)
use std::cell::RefCell;
let data = RefCell::new(vec![1, 2, 3]);
{
let mut ref1 = data.borrow_mut();
ref1.push(4);
}
{
let mut ref2 = data.borrow_mut();
ref2.push(5);
}
println!("解决方案3: {:?}", data.borrow());
}
fn problem_dangling_references() {
println!("\n=== 问题3: 悬垂引用 ===");
// 错误示例(无法编译)
// fn dangle() -> &String {
// let s = String::from("hello");
// &s // 错误!返回局部变量的引用
// } // s 离开作用域并被丢弃
// 解决方案1: 返回所有权
fn no_dangle() -> String {
let s = String::from("hello");
s // 直接返回所有权
}
let s = no_dangle();
println!("解决方案1: {}", s);
// 解决方案2: 使用静态生命周期
fn static_reference() -> &'static str {
"hello world" // 字符串字面量有 'static 生命周期
}
let s = static_reference();
println!("解决方案2: {}", s);
// 解决方案3: 接受引用参数并返回引用
fn get_slice<'a>(s: &'a str) -> &'a str {
&s[0..5]
}
let original = String::from("hello world");
let slice = get_slice(&original);
println!("解决方案3: {}", slice);
}
fn problem_iterator_invalidation() {
println!("\n=== 问题4: 迭代器无效化 ===");
let mut data = vec![1, 2, 3, 4, 5];
// 错误示例
// for item in &data {
// if *item == 3 {
// data.push(6); // 编译错误!不能同时存在可变和不可变借用
// }
// }
// 解决方案1: 收集需要修改的索引,然后单独修改
let mut indices_to_remove = Vec::new();
for (i, &item) in data.iter().enumerate() {
if item == 3 {
indices_to_remove.push(i);
}
}
for &index in indices_to_remove.iter().rev() {
data.remove(index);
}
data.push(6);
println!("解决方案1: {:?}", data);
// 解决方案2: 使用retain方法
let mut data = vec![1, 2, 3, 4, 5];
data.retain(|&x| x != 3);
data.push(6);
println!("解决方案2: {:?}", data);
// 解决方案3: 使用索引迭代
let mut data = vec![1, 2, 3, 4, 5];
let mut i = 0;
while i < data.len() {
if data[i] == 3 {
data.remove(i);
} else {
i += 1;
}
}
data.push(6);
println!("解决方案3: {:?}", data);
}
fn problem_self_referential_structs() {
println!("\n=== 问题5: 自引用结构体 ===");
// 错误示例(无法安全实现)
// struct SelfRef {
// data: String,
// reference: &String, // 错误!需要生命周期注解
// }
// 解决方案1: 使用索引代替引用
struct SelfRefWithIndex {
data: String,
reference_index: usize,
}
impl SelfRefWithIndex {
fn new(data: String) -> Self {
let reference_index = 0;
SelfRefWithIndex { data, reference_index }
}
fn get_reference(&self) -> &str {
&self.data[self.reference_index..]
}
}
let self_ref = SelfRefWithIndex::new(String::from("hello world"));
println!("解决方案1: {}", self_ref.get_reference());
// 解决方案2: 使用Pin(高级特性,用于固定数据位置)
use std::marker::PhantomPinned;
use std::pin::Pin;
struct SelfRefPinned {
data: String,
reference: *const String, // 原始指针
_pin: PhantomPinned, // 阻止移动
}
impl SelfRefPinned {
fn new(data: String) -> Pin<Box<Self>> {
let mut boxed = Box::pin(Self {
data,
reference: std::ptr::null(),
_pin: PhantomPinned,
});
let reference = &boxed.data as *const String;
unsafe {
let mut_ref = Pin::as_mut(&mut boxed);
Pin::get_unchecked_mut(mut_ref).reference = reference;
}
boxed
}
fn get_reference(self: Pin<&Self>) -> &str {
unsafe { &*self.reference }
}
}
let pinned = SelfRefPinned::new(String::from("hello world"));
println!("解决方案2: {}", pinned.get_reference());
// 解决方案3: 使用 arena 或引用计数
use std::rc::Rc;
struct SharedData {
data: Rc<String>,
}
impl SharedData {
fn new(data: String) -> Self {
SharedData { data: Rc::new(data) }
}
fn get_reference(&self) -> &str {
&self.data
}
}
let shared = SharedData::new(String::from("hello world"));
println!("解决方案3: {}", shared.get_reference());
}
5.4.2 所有权模式与最佳实践
学习在实际项目中应用所有权的模式和最佳实践。
rust
fn ownership_patterns_and_best_practices() {
println!("=== 所有权模式与最佳实践 ===");
// 模式1: 构建器模式
builder_pattern();
// 模式2: RAII (Resource Acquisition Is Initialization)
raii_pattern();
// 模式3: 迭代器适配器模式
iterator_adapter_pattern();
// 模式4: 新类型模式
newtype_pattern();
// 模式5: 内部可变性模式
interior_mutability_pattern();
}
fn builder_pattern() {
println!("\n=== 构建器模式 ===");
#[derive(Debug)]
struct ConnectionConfig {
host: String,
port: u16,
timeout: u32,
retries: u32,
}
struct ConnectionConfigBuilder {
host: Option<String>,
port: Option<u16>,
timeout: Option<u32>,
retries: Option<u32>,
}
impl ConnectionConfigBuilder {
fn new() -> Self {
ConnectionConfigBuilder {
host: None,
port: None,
timeout: None,
retries: None,
}
}
fn host(mut self, host: String) -> Self {
self.host = Some(host);
self
}
fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
fn timeout(mut self, timeout: u32) -> Self {
self.timeout = Some(timeout);
self
}
fn retries(mut self, retries: u32) -> Self {
self.retries = Some(retries);
self
}
fn build(self) -> Result<ConnectionConfig, String> {
Ok(ConnectionConfig {
host: self.host.ok_or("主机名必须设置")?,
port: self.port.ok_or("端口必须设置")?,
timeout: self.timeout.unwrap_or(30),
retries: self.retries.unwrap_or(3),
})
}
}
let config = ConnectionConfigBuilder::new()
.host("localhost".to_string())
.port(8080)
.timeout(60)
.build()
.unwrap();
println!("构建的配置: {:?}", config);
}
fn raii_pattern() {
println!("\n=== RAII 模式 ===");
// RAII: 资源获取即初始化
// 在Rust中,Drop trait 自动提供RAII
struct File {
filename: String,
}
impl File {
fn new(filename: &str) -> Result<Self, String> {
println!("打开文件: {}", filename);
Ok(File { filename: filename.to_string() })
}
}
impl Drop for File {
fn drop(&mut self) {
println!("关闭文件: {}", self.filename);
}
}
struct Transaction<'a> {
name: &'a str,
committed: bool,
}
impl<'a> Transaction<'a> {
fn new(name: &'a str) -> Self {
println!("开始事务: {}", name);
Transaction { name, committed: false }
}
fn commit(mut self) {
println!("提交事务: {}", self.name);
self.committed = true;
// self 在这里被丢弃,但因为我们移动了self,不会调用drop
}
}
impl<'a> Drop for Transaction<'a> {
fn drop(&mut self) {
if !self.committed {
println!("回滚事务: {}", self.name);
}
}
}
// 正常提交的事务
{
let transaction = Transaction::new("正常事务");
transaction.commit(); // 提交,不会回滚
}
// 未提交的事务(会自动回滚)
{
let _transaction = Transaction::new("未提交事务");
// 事务在离开作用域时自动回滚
}
// 文件RAII示例
{
let _file = File::new("test.txt").unwrap();
// 文件在离开作用域时自动关闭
}
}
fn iterator_adapter_pattern() {
println!("\n=== 迭代器适配器模式 ===");
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 使用迭代器适配器处理数据
let result: Vec<String> = numbers
.into_iter() // 获取所有权迭代器
.filter(|&x| x % 2 == 0) // 过滤偶数
.map(|x| x * 3) // 乘以3
.filter(|&x| x > 10) // 过滤大于10的数
.map(|x| x.to_string()) // 转换为字符串
.collect(); // 收集结果
println!("迭代器适配器结果: {:?}", result);
// 自定义迭代器适配器
struct TakeWhileInclusive<I, P> {
iter: I,
predicate: P,
done: bool,
}
impl<I, P> TakeWhileInclusive<I, P> {
fn new(iter: I, predicate: P) -> Self {
TakeWhileInclusive { iter, predicate, done: false }
}
}
impl<I: Iterator, P> Iterator for TakeWhileInclusive<I, P>
where
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
let item = self.iter.next()?;
if !(self.predicate)(&item) {
self.done = true;
}
Some(item)
}
}
trait TakeWhileInclusiveExt: Iterator + Sized {
fn take_while_inclusive<P>(self, predicate: P) -> TakeWhileInclusive<Self, P>
where
P: FnMut(&Self::Item) -> bool,
{
TakeWhileInclusive::new(self, predicate)
}
}
impl<I: Iterator> TakeWhileInclusiveExt for I {}
let numbers = vec![1, 2, 3, 4, 5, 1, 2, 3];
let result: Vec<i32> = numbers
.into_iter()
.take_while_inclusive(|&x| x < 4)
.collect();
println!("自定义适配器结果: {:?}", result);
}
fn newtype_pattern() {
println!("\n=== 新类型模式 ===");
// 新类型模式:用结构体包装现有类型以提供额外的类型安全
// 用户ID和产品ID本质都是u32,但应该是不同的类型
struct UserId(u32);
struct ProductId(u32);
impl UserId {
fn new(id: u32) -> Self {
UserId(id)
}
fn value(&self) -> u32 {
self.0
}
}
impl ProductId {
fn new(id: u32) -> Self {
ProductId(id)
}
fn value(&self) -> u32 {
self.0
}
}
fn get_user_name(user_id: UserId) -> String {
format!("用户{}", user_id.value())
}
fn get_product_name(product_id: ProductId) -> String {
format!("产品{}", product_id.value())
}
let user_id = UserId::new(123);
let product_id = ProductId::new(456);
println!("用户名: {}", get_user_name(user_id));
println!("产品名: {}", get_product_name(product_id));
// 下面的代码会导致编译错误,提供了类型安全
// get_user_name(product_id); // 错误!期望UserId,得到ProductId
// 另一个新类型示例:电子邮件地址
struct Email(String);
impl Email {
fn new(email: &str) -> Result<Self, String> {
if email.contains('@') {
Ok(Email(email.to_string()))
} else {
Err("无效的电子邮件地址".to_string())
}
}
fn value(&self) -> &str {
&self.0
}
}
let email = Email::new("user@example.com").unwrap();
println!("电子邮件: {}", email.value());
}
fn interior_mutability_pattern() {
println!("\n=== 内部可变性模式 ===");
// 内部可变性:允许在不可变引用的情况下修改数据
use std::cell::{RefCell, Cell};
use std::rc::Rc;
// 1. RefCell 示例
let counter = RefCell::new(0);
{
let mut count_ref = counter.borrow_mut();
*count_ref += 1;
} // count_ref 离开作用域,借用结束
{
let count_ref = counter.borrow();
println!("计数器值: {}", *count_ref);
}
// 2. Cell 示例(用于Copy类型)
let cell = Cell::new(42);
cell.set(100); // 不需要可变引用!
println!("Cell值: {}", cell.get());
// 3. 在结构体中使用内部可变性
struct Cache {
data: RefCell<Vec<String>>,
hit_count: Cell<u32>,
miss_count: Cell<u32>,
}
impl Cache {
fn new() -> Self {
Cache {
data: RefCell::new(Vec::new()),
hit_count: Cell::new(0),
miss_count: Cell::new(0),
}
}
fn get(&self, index: usize) -> Option<String> {
let data = self.data.borrow();
if index < data.len() {
self.hit_count.set(self.hit_count.get() + 1);
Some(data[index].clone())
} else {
self.miss_count.set(self.miss_count.get() + 1);
None
}
}
fn add(&self, item: String) {
let mut data = self.data.borrow_mut();
data.push(item);
}
fn stats(&self) -> (u32, u32) {
(self.hit_count.get(), self.miss_count.get())
}
}
let cache = Cache::new();
cache.add("item1".to_string());
cache.add("item2".to_string());
println!("获取item1: {:?}", cache.get(0));
println!("获取不存在的item: {:?}", cache.get(5));
println!("缓存统计: {:?}", cache.stats());
// 4. Rc + RefCell 用于共享所有权和内部可变性
let shared_data = Rc::new(RefCell::new(Vec::new()));
let shared1 = Rc::clone(&shared_data);
let shared2 = Rc::clone(&shared_data);
shared1.borrow_mut().push(1);
shared2.borrow_mut().push(2);
println!("共享数据: {:?}", shared_data.borrow());
}
5.4.3 性能优化与所有权
了解如何利用所有权系统进行性能优化。
rust
fn performance_optimization_with_ownership() {
println!("=== 性能优化与所有权 ===");
// 1. 避免不必要的克隆
avoid_unnecessary_clones();
// 2. 使用移动语义优化
optimize_with_move_semantics();
// 3. 零成本抽象
zero_cost_abstractions();
// 4. 内存布局优化
memory_layout_optimization();
}
fn avoid_unnecessary_clones() {
println!("\n=== 避免不必要的克隆 ===");
// 反模式:不必要的克隆
fn process_string_bad(s: String) -> String {
let result = s.clone(); // 不必要的克隆!
result.to_uppercase()
}
// 好的模式:使用引用
fn process_string_good(s: &str) -> String {
s.to_uppercase()
}
let original = String::from("hello");
let result1 = process_string_bad(original.clone());
let result2 = process_string_good(&original);
println!("结果1: {}", result1);
println!("结果2: {}", result2);
println!("原始字符串: {}", original);
// 在需要所有权时的优化
fn process_string_owned(mut s: String) -> String {
// 直接修改传入的字符串,避免分配新内存
s.make_ascii_uppercase();
s
}
let original = String::from("hello");
let result = process_string_owned(original);
println!("直接修改结果: {}", result);
}
fn optimize_with_move_semantics() {
println!("\n=== 使用移动语义优化 ===");
use std::time::Instant;
// 大型数据结构的移动性能
let large_data: Vec<u8> = (0..10_000_000).map(|x| (x % 256) as u8).collect();
let start = Instant::now();
let moved_data = large_data; // 移动,不是拷贝
let move_duration = start.elapsed();
println!("移动10MB数据耗时: {:?}", move_duration);
// 比较移动和克隆的性能
let large_data: Vec<u8> = (0..1_000_000).map(|x| (x % 256) as u8).collect();
let start = Instant::now();
let moved_data = large_data; // 移动
let move_duration = start.elapsed();
let large_data_clone: Vec<u8> = (0..1_000_000).map(|x| (x % 256) as u8).collect();
let start = Instant::now();
let cloned_data = large_data_clone.clone(); // 克隆
let clone_duration = start.elapsed();
println!("移动1MB数据耗时: {:?}", move_duration);
println!("克隆1MB数据耗时: {:?}", clone_duration);
println!("移动比克隆快 {:.1} 倍",
clone_duration.as_nanos() as f64 / move_duration.as_nanos() as f64);
// 使用移动语义优化函数返回值
move_semantics_in_functions();
}
fn move_semantics_in_functions() {
println!("\n=== 函数中的移动语义优化 ===");
// 返回大型数据结构时,移动语义可以避免拷贝
fn create_large_vector() -> Vec<i32> {
(0..1_000_000).collect()
}
let start = std::time::Instant::now();
let data = create_large_vector();
let duration = start.elapsed();
println!("创建并返回100万元素向量耗时: {:?}", duration);
// 链式方法调用中的移动优化
let result = (0..1000)
.collect::<Vec<i32>>() // 分配向量
.into_iter() // 转换为所有权迭代器
.filter(|&x| x % 2 == 0) // 过滤
.map(|x| x * 2) // 映射
.collect::<Vec<i32>>(); // 重新收集
println!("链式操作结果长度: {}", result.len());
}
fn zero_cost_abstractions() {
println!("\n=== 零成本抽象 ===");
// Rust的所有权系统在编译时检查,运行时零成本
// 1. 迭代器是零成本抽象的典型例子
let numbers = vec![1, 2, 3, 4, 5];
// 高级迭代器代码
let sum: i32 = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|&x| x * 2)
.sum();
// 编译为与手写循环同样高效的代码
let mut sum_manual = 0;
for &x in &numbers {
if x % 2 == 0 {
sum_manual += x * 2;
}
}
println!("迭代器求和: {}", sum);
println!("手动求和: {}", sum_manual);
// 2. Option和Result的零成本处理
fn safe_division(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
let result = safe_division(10.0, 2.0)
.map(|x| x * 2.0)
.and_then(|x| safe_division(x, 3.0))
.unwrap_or(0.0);
println!("安全除法链式结果: {}", result);
// 3. 模式匹配的零成本
let value = Some(42);
match value {
Some(x) => println!("有值: {}", x),
None => println!("无值"),
}
// 编译为高效的跳转表或条件判断
}
fn memory_layout_optimization() {
println!("\n=== 内存布局优化 ===");
use std::mem;
// 1. 栈分配 vs 堆分配
println!("内存使用分析:");
// 栈分配的结构体
#[derive(Debug)]
struct StackData {
a: i32,
b: i32,
c: i32,
}
// 堆分配的结构体
#[derive(Debug)]
struct HeapData {
data: Vec<i32>,
}
let stack_instance = StackData { a: 1, b: 2, c: 3 };
let heap_instance = HeapData { data: vec![1, 2, 3] };
println!("栈结构体大小: {} 字节", mem::size_of_val(&stack_instance));
println!("堆结构体大小: {} 字节", mem::size_of_val(&heap_instance));
println!("向量容量: {}", heap_instance.data.capacity());
// 2. 内存对齐优化
#[repr(C)]
struct Unoptimized {
a: u8, // 1字节
b: u32, // 4字节
c: u8, // 1字节
}
#[repr(C, packed)]
struct Packed {
a: u8,
b: u32,
c: u8,
}
let unopt = Unoptimized { a: 1, b: 2, c: 3 };
let packed = Packed { a: 1, b: 2, c: 3 };
println!("未优化结构体大小: {} 字节", mem::size_of_val(&unopt));
println!("打包结构体大小: {} 字节", mem::size_of_val(&packed));
// 3. 使用Box优化大类型
struct LargeType {
data: [u8; 1024], // 1KB数据
}
// 直接在栈上分配
let stack_large = LargeType { data: [0; 1024] };
// 在堆上分配
let heap_large = Box::new(LargeType { data: [0; 1024] });
println!("栈上大类型大小: {} 字节", mem::size_of_val(&stack_large));
println!("堆上大类型大小: {} 字节", mem::size_of_val(&heap_large));
// 4. 零大小类型优化
struct Nothing;
let nothing = Nothing;
println!("零大小类型大小: {} 字节", mem::size_of_val(¬hing));
// 包含零大小类型的结构体
struct WithZST {
data: i32,
_marker: Nothing,
}
let with_zst = WithZST { data: 42, _marker: Nothing };
println!("包含ZST的结构体大小: {} 字节", mem::size_of_val(&with_zst));
}
通过本章的全面学习,你已经深入掌握了Rust最独特和强大的特性------所有权系统。从基本的所有权规则到高级的借用模式,从简单的切片使用到复杂的内存优化技巧,你现在应该能够编写出既安全又高效的Rust代码。在下一章中,我们将探讨结构体和方法,学习如何组织数据和行为。