当前位置: 首页 > news >正文

第5章 所有权系统

在这里插入图片描述

文章目录

  • 第5章 所有权系统
    • 5.1 所有权概念详解
      • 5.1.1 所有权的基本规则
      • 5.1.2 栈与堆的内存模型
      • 5.1.3 Copy和Move语义
      • 5.1.4 所有权与函数调用
    • 5.2 引用与借用
      • 5.2.1 不可变引用
      • 5.2.2 可变引用
      • 5.2.3 高级引用模式
    • 5.3 切片类型
      • 5.3.1 字符串切片
      • 5.3.2 数组切片
      • 5.3.3 切片与所有权系统
    • 5.4 所有权规则实战
      • 5.4.1 常见所有权问题与解决方案
      • 5.4.2 所有权模式与最佳实践
      • 5.4.3 性能优化与所有权

第5章 所有权系统

5.1 所有权概念详解

5.1.1 所有权的基本规则

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 将返回值移给 s1let s2 = String::from("hello"); // s2 进入作用域let s3 = takes_and_gives_back(s2); // s2 被移动到函数中,返回值移给 s3println!("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的栈和堆内存模型。

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语义是掌握所有权系统的关键。

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 traitdemonstrate_copy_types();// 4. 哪些类型是 Move 语义demonstrate_move_types();// 5. 自定义类型的 Copy 行为custom_type_copy_behavior();
}fn demonstrate_copy_types() {println!("\n=== Copy 类型示例 ===");// 所有整数类型都是 Copylet a: i8 = 1;let b: i16 = 2;let c: i32 = 3;let d: i64 = 4;let e: isize = 5;// 所有浮点数类型都是 Copylet f: f32 = 1.0;let g: f64 = 2.0;// 布尔类型是 Copylet h: bool = true;// 字符类型是 Copylet i: char = 'a';// 元组当所有元素都是 Copy 时也是 Copylet j: (i32, f64) = (1, 2.0);let k = j; // 拷贝println!("元组拷贝: j={:?}, k={:?}", j, k);// 数组当元素是 Copy 时也是 Copylet l: [i32; 3] = [1, 2, 3];let m = l; // 拷贝println!("数组拷贝: l={:?}, m={:?}", l, m);// 不可变引用是 Copylet 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. 检查类型是否实现了 Copycheck_copy_trait();
}fn check_copy_trait() {println!("\n=== 检查 Copy trait 实现 ===");// 使用编译时检查fn is_copy<T: Copy>() -> bool {true}// 这些类型实现了 Copyassert!(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代码的关键。

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"); // 返回新的Stringprintln!("链式调用结果: {}", 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中避免所有权转移的主要方式。

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 可变引用

可变引用允许修改借用的数据,但受到严格的规则限制。

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 高级引用模式

探索一些更复杂的引用使用模式和技巧。

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 letlet 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中处理字符串的常用方式。

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-8string_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 数组切片

数组切片是对数组或向量中一部分的引用,提供了灵活的数据访问方式。

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代码的关键。

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);// 在数据结构中使用 'staticstruct StaticHolder {data: &'static str,}let holder = StaticHolder { data: "静态数据" };println!("静态持有者: {}", holder.data);// 注意:不是所有字符串字面量都必须是 'static// 但在大多数情况下,字符串字面量确实有 'static 生命周期
}

5.4 所有权规则实战

5.4.1 常见所有权问题与解决方案

通过实际案例学习如何解决常见的所有权问题。

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 所有权模式与最佳实践

学习在实际项目中应用所有权的模式和最佳实践。

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 自动提供RAIIstruct 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>whereP: 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>whereP: 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 性能优化与所有权

了解如何利用所有权系统进行性能优化。

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(&nothing));// 包含零大小类型的结构体struct WithZST {data: i32,_marker: Nothing,}let with_zst = WithZST { data: 42, _marker: Nothing };println!("包含ZST的结构体大小: {} 字节", mem::size_of_val(&with_zst));
}

通过本章的全面学习,你已经深入掌握了Rust最独特和强大的特性——所有权系统。从基本的所有权规则到高级的借用模式,从简单的切片使用到复杂的内存优化技巧,你现在应该能够编写出既安全又高效的Rust代码。在下一章中,我们将探讨结构体和方法,学习如何组织数据和行为。

http://www.dtcms.com/a/568550.html

相关文章:

  • 从零开始学Flink:事件驱动
  • 机器学习实现逻辑回归-癌症分类预测
  • Kafka 从入门到精通完整指南
  • 常见二三维GIS数据分类及处理流程图
  • LLM结构化输出:约束解码、CFG和response_format
  • 做网站麻烦不文山网站建设求职简历
  • wordpress网站静态页面外国食品优秀设计网站
  • hybrid
  • C++中malloc、free和new、delete的区别
  • 计算机视觉:python车辆行人检测与跟踪系统 YOLO模型 SORT算法 PyQt5界面 目标检测+目标跟踪 深度学习 计算机✅
  • 提高肠氧饱和度测量精度的新技术评估
  • 【数据集+源码+文章】基于yolov8+streamlit的12种水果品质、成熟度检测系统
  • Camera参数(3A)
  • 【C++:搜索二叉树】二叉搜索树从理论到实战完全解读:原理、两种场景下的实现
  • 高性能网络编程实战:用Tokio构建自定义协议服务器
  • H265 vs AV1 vs H266帧内块拷贝差异
  • CSS 中 `data-status` 的使用详解
  • 舟山企业网站建设公司微信小程序麻将辅助免费
  • VMware替代 | 详解ZStack ZSphere产品化运维六大特性
  • 缓存击穿,缓存穿透,缓存雪崩的原因和解决方案(或者说使用缓存的过程中有没有遇到什么问题,怎么解决的)
  • 关于数据包分片总长度字段的计算和MF标志位的判断
  • 手机网站建站流程网站建设卩金手指科杰
  • BuildingAI 用户信息弹出页面PRD
  • ​Oracle RAC灾备环境UNDO表空间管理终极指南:解决备库修改难题与性能优化实战​
  • 《uni-app跨平台开发完全指南》- 02 - 项目结构与配置文件详解
  • 【数据分析】基于R语言的废水微生物抗性分析与负二项回归模型建模
  • 深圳专业网站公司注册查询网站
  • k8s --- resource 资源
  • 神经网络之反射变换
  • k8s——pod详解2