本章目标
通过本章学习,你将掌握: - Rust所有权系统的核心概念 - 所有权规则和内存管理 - 借用和引用机制 - 可变引用和不可变引用 - 切片类型的使用 - 生命周期的基本概念
4.1 所有权概念
什么是所有权?
所有权是Rust最独特的特性,它使Rust能够在不使用垃圾回收器的情况下保证内存安全。
所有权规则
- 每个值都有一个所有者(owner)
- 同一时间只能有一个所有者
- 当所有者离开作用域时,值会被丢弃
fn main() {
println!("🔐 所有权基础概念");
println!("==================");
// 规则1: 每个值都有一个所有者
let s1 = String::from("hello"); // s1是字符串的所有者
println!("s1: {}", s1);
// 规则2: 同一时间只能有一个所有者
let s2 = s1; // 所有权从s1转移到s2
println!("s2: {}", s2);
// println!("s1: {}", s1); // 错误!s1不再有效
// 规则3: 当所有者离开作用域时,值会被丢弃
{
let s3 = String::from("world"); // s3进入作用域
println!("s3: {}", s3);
} // s3离开作用域,内存被释放
// println!("s3: {}", s3); // 错误!s3已经不存在
println!("程序结束,s2的内存将被释放");
}
rustc ownership_basic.rs && ./ownership_basic
移动(Move)语义
fn main() {
println!("📦 移动语义示例");
println!("===============");
// 字符串移动
let s1 = String::from("hello");
let s2 = s1; // s1的值移动到s2
println!("s2: {}", s2);
// println!("s1: {}", s1); // 编译错误!
// 函数调用中的移动
let s3 = String::from("world");
take_ownership(s3); // s3的值移动到函数中
// println!("s3: {}", s3); // 编译错误!
// 函数返回值的移动
let s4 = give_ownership(); // 函数返回值移动到s4
println!("s4: {}", s4);
let s5 = String::from("rust");
let s6 = take_and_give_back(s5); // s5移动到函数,返回值移动到s6
println!("s6: {}", s6);
// println!("s5: {}", s5); // 编译错误!
}
fn take_ownership(some_string: String) {
println!("函数接收: {}", some_string);
} // some_string离开作用域,内存被释放
fn give_ownership() -> String {
let some_string = String::from("给你");
some_string // 返回值移动给调用者
}
fn take_and_give_back(a_string: String) -> String {
a_string // 直接返回参数
}
rustc move_semantics.rs && ./move_semantics
复制(Copy)语义
fn main() {
println!("📋 复制语义示例");
println!("===============");
// 基本类型实现了Copy trait
let x = 5;
let y = x; // x被复制到y,x仍然有效
println!("x: {}, y: {}", x, y); // 都可以使用
// 函数调用中的复制
let a = 10;
make_copy(a); // a被复制到函数中
println!("a仍然有效: {}", a); // a仍然可以使用
// 元组的复制(如果所有元素都实现Copy)
let tup1 = (1, 2, 3);
let tup2 = tup1; // 复制
println!("tup1: {:?}, tup2: {:?}", tup1, tup2);
// 数组的复制(如果元素实现Copy且长度已知)
let arr1 = [1, 2, 3, 4, 5];
let arr2 = arr1; // 复制
println!("arr1: {:?}, arr2: {:?}", arr1, arr2);
// 实现Copy的类型
demonstrate_copy_types();
}
fn make_copy(some_integer: i32) {
println!("函数接收的复制: {}", some_integer);
}
fn demonstrate_copy_types() {
println!("\n实现Copy trait的类型:");
// 所有整数类型
let i: i32 = 1;
let j = i;
println!("整数: i={}, j={}", i, j);
// 所有浮点类型
let f1: f64 = 3.14;
let f2 = f1;
println!("浮点: f1={}, f2={}", f1, f2);
// 布尔类型
let b1 = true;
let b2 = b1;
println!("布尔: b1={}, b2={}", b1, b2);
// 字符类型
let c1 = 'a';
let c2 = c1;
println!("字符: c1={}, c2={}", c1, c2);
// 不可变引用
let s = String::from("hello");
let r1 = &s;
let r2 = r1; // 引用被复制
println!("引用: r1={}, r2={}", r1, r2);
}
rustc copy_semantics.rs && ./copy_semantics
克隆(Clone)
fn main() {
println!("🔄 克隆示例");
println!("============");
// 深拷贝字符串
let s1 = String::from("hello");
let s2 = s1.clone(); // 显式克隆
println!("s1: {}, s2: {}", s1, s2); // 两个都可以使用
// 克隆向量
let vec1 = vec![1, 2, 3, 4, 5];
let vec2 = vec1.clone();
println!("vec1: {:?}", vec1);
println!("vec2: {:?}", vec2);
// 克隆复杂数据结构
let data1 = vec![String::from("a"), String::from("b")];
let data2 = data1.clone();
println!("data1: {:?}", data1);
println!("data2: {:?}", data2);
// 克隆的性能考虑
demonstrate_clone_cost();
}
fn demonstrate_clone_cost() {
println!("\n克隆的性能考虑:");
// 小数据的克隆
let small_vec = vec![1, 2, 3];
let cloned_small = small_vec.clone();
println!("小向量克隆: {:?} -> {:?}", small_vec, cloned_small);
// 大数据的克隆(注意性能)
let large_vec: Vec<i32> = (0..1000).collect();
println!("大向量长度: {}", large_vec.len());
// 克隆大数据时要谨慎
let cloned_large = large_vec.clone();
println!("克隆后长度: {}", cloned_large.len());
println!("提示: 克隆大数据结构可能很昂贵,考虑使用引用");
}
rustc clone_example.rs && ./clone_example
4.2 借用和引用
不可变引用
fn main() {
println!("📖 不可变引用示例");
println!("==================");
let s1 = String::from("hello");
// 创建引用
let len = calculate_length(&s1); // &s1创建对s1的引用
println!("字符串 '{}' 的长度是 {}", s1, len); // s1仍然有效
// 多个不可变引用
let r1 = &s1;
let r2 = &s1;
let r3 = &s1;
println!("r1: {}, r2: {}, r3: {}", r1, r2, r3);
// 引用的引用
let r4 = &r1;
println!("引用的引用: {}", r4);
// 不同类型的引用
let number = 42;
let number_ref = &number;
println!("数字引用: {}", number_ref);
let arr = [1, 2, 3, 4, 5];
let arr_ref = &arr;
println!("数组引用: {:?}", arr_ref);
// 引用作为函数参数
print_string(&s1);
print_length(&s1);
}
// 函数接收引用,不获取所有权
fn calculate_length(s: &String) -> usize {
s.len()
} // s离开作用域,但因为它是引用,所以不会释放内存
fn print_string(s: &String) {
println!("打印字符串: {}", s);
}
fn print_length(s: &String) {
println!("字符串长度: {}", s.len());
}
rustc immutable_references.rs && ./immutable_references
可变引用
fn main() {
println!("✏️ 可变引用示例");
println!("=================");
let mut s = String::from("hello");
// 创建可变引用
change(&mut s);
println!("修改后: {}", s);
// 可变引用的限制:同一时间只能有一个
let r1 = &mut s;
// let r2 = &mut s; // 错误!不能同时有两个可变引用
println!("可变引用: {}", r1);
// 可变引用和不可变引用不能同时存在
let mut s2 = String::from("world");
{
let r1 = &s2; // 不可变引用
let r2 = &s2; // 另一个不可变引用
println!("不可变引用: {}, {}", r1, r2);
// r1和r2在这里结束使用
}
let r3 = &mut s2; // 现在可以创建可变引用
println!("可变引用: {}", r3);
// 引用的作用域
demonstrate_reference_scope();
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
fn demonstrate_reference_scope() {
println!("\n引用作用域示例:");
let mut s = String::from("hello");
let r1 = &s; // 不可变引用开始
let r2 = &s; // 另一个不可变引用
println!("不可变引用: {}, {}", r1, r2);
// r1和r2的作用域在这里结束(最后一次使用)
let r3 = &mut s; // 可变引用开始
r3.push_str(", rust");
println!("可变引用: {}", r3);
// r3的作用域在这里结束
// 现在又可以创建新的引用了
let r4 = &s;
println!("新的不可变引用: {}", r4);
}
rustc mutable_references.rs && ./mutable_references
悬垂引用(Dangling References)
fn main() {
println!("⚠️ 悬垂引用示例");
println!("=================");
// Rust编译器防止悬垂引用
// let reference_to_nothing = dangle(); // 这会编译错误!
// 正确的做法:返回值而不是引用
let s = no_dangle();
println!("正确返回的字符串: {}", s);
// 演示作用域和生命周期
demonstrate_lifetimes();
}
// 这个函数会导致悬垂引用(编译错误)
/*
fn dangle() -> &String { // 返回字符串的引用
let s = String::from("hello"); // s是新字符串
&s // 返回字符串s的引用
} // s离开作用域并被丢弃,其内存被释放
// 引用指向的内存已经无效!
*/
// 正确的做法:返回String本身
fn no_dangle() -> String {
let s = String::from("hello");
s // 返回String,所有权转移给调用者
}
fn demonstrate_lifetimes() {
println!("\n生命周期演示:");
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(&string1, &string2);
println!("最长的字符串: {}", result);
} // string2在这里离开作用域
// 如果我们试图在string2离开作用域后使用result,会出错
// println!("结果: {}", result); // 编译错误!
}
// 这个函数需要生命周期参数(在后续章节详细讲解)
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
rustc dangling_references.rs && ./dangling_references
4.3 切片类型
字符串切片
fn main() {
println!("🔪 字符串切片示例");
println!("==================");
let s = String::from("hello world");
// 创建字符串切片
let hello = &s[0..5]; // 或 &s[..5]
let world = &s[6..11]; // 或 &s[6..]
let whole = &s[..]; // 整个字符串的切片
println!("原字符串: {}", s);
println!("hello: {}", hello);
println!("world: {}", world);
println!("whole: {}", whole);
// 字符串字面量就是切片
let literal = "Hello, world!"; // 类型是 &str
println!("字符串字面量: {}", literal);
// 使用切片的函数
let word = first_word(&s);
println!("第一个单词: {}", word);
let word2 = first_word("hello rust");
println!("第一个单词: {}", word2);
// 切片的安全性
demonstrate_slice_safety();
// 中文字符串切片(注意字节边界)
demonstrate_unicode_slices();
}
// 返回第一个单词的切片
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[..] // 如果没有空格,返回整个字符串
}
fn demonstrate_slice_safety() {
println!("\n切片安全性演示:");
let mut s = String::from("hello world");
let word = first_word(&s);
println!("第一个单词: {}", word);
// 如果我们试图修改s,编译器会阻止
// s.clear(); // 错误!不能在有不可变引用时修改
// 正确的做法:在使用完切片后再修改
// 这里word的作用域结束
s.clear(); // 现在可以修改了
println!("清空后的字符串: '{}'", s);
}
fn demonstrate_unicode_slices() {
println!("\nUnicode切片演示:");
let s = String::from("你好世界");
// 中文字符是3字节,所以要小心切片边界
let hello = &s[0..6]; // "你好" (每个中文字符3字节)
let world = &s[6..12]; // "世界"
println!("中文字符串: {}", s);
println!("你好: {}", hello);
println!("世界: {}", world);
// 更安全的方式:使用字符迭代器
let chars: Vec<char> = s.chars().collect();
println!("字符数组: {:?}", chars);
// 按字符切片
let char_slice: String = chars[0..2].iter().collect();
println!("前两个字符: {}", char_slice);
}
rustc string_slices.rs && ./string_slices
数组切片
fn main() {
println!("📊 数组切片示例");
println!("===============");
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 创建数组切片
let slice1 = &arr[1..4]; // [2, 3, 4]
let slice2 = &arr[..3]; // [1, 2, 3]
let slice3 = &arr[7..]; // [8, 9, 10]
let slice4 = &arr[..]; // 整个数组
println!("原数组: {:?}", arr);
println!("slice1 [1..4]: {:?}", slice1);
println!("slice2 [..3]: {:?}", slice2);
println!("slice3 [7..]: {:?}", slice3);
println!("slice4 [..]: {:?}", slice4);
// 切片的长度和索引
println!("\n切片信息:");
println!("slice1长度: {}", slice1.len());
println!("slice1第一个元素: {}", slice1[0]);
// 遍历切片
println!("\n遍历slice1:");
for (i, &value) in slice1.iter().enumerate() {
println!("索引{}: {}", i, value);
}
// 切片作为函数参数
let sum = sum_slice(slice1);
println!("slice1的和: {}", sum);
let max = find_max(&arr[5..8]);
println!("arr[5..8]的最大值: {}", max);
// 可变切片
demonstrate_mutable_slices();
}
fn sum_slice(slice: &[i32]) -> i32 {
let mut sum = 0;
for &value in slice {
sum += value;
}
sum
}
fn find_max(slice: &[i32]) -> i32 {
let mut max = slice[0];
for &value in slice {
if value > max {
max = value;
}
}
max
}
fn demonstrate_mutable_slices() {
println!("\n可变切片演示:");
let mut arr = [1, 2, 3, 4, 5];
println!("原数组: {:?}", arr);
// 创建可变切片
let slice = &mut arr[1..4];
println!("可变切片: {:?}", slice);
// 修改切片中的元素
slice[0] = 10;
slice[1] = 20;
slice[2] = 30;
println!("修改后的切片: {:?}", slice);
println!("修改后的数组: {:?}", arr);
// 使用函数修改切片
double_slice(&mut arr[..]);
println!("翻倍后的数组: {:?}", arr);
}
fn double_slice(slice: &mut [i32]) {
for value in slice {
*value *= 2;
}
}
rustc array_slices.rs && ./array_slices
4.4 所有权模式
函数中的所有权
fn main() {
println!("🔄 所有权模式示例");
println!("==================");
// 模式1: 传递所有权
let s1 = String::from("hello");
let s2 = take_ownership_pattern(s1);
// s1不再有效,s2现在拥有值
println!("返回的字符串: {}", s2);
// 模式2: 借用(不可变引用)
let s3 = String::from("world");
let len = borrow_pattern(&s3);
println!("字符串 '{}' 长度: {}", s3, len); // s3仍然有效
// 模式3: 可变借用
let mut s4 = String::from("rust");
modify_pattern(&mut s4);
println!("修改后: {}", s4);
// 模式4: 返回多个值
let s5 = String::from("programming");
let (s6, len) = calculate_length_pattern(s5);
println!("字符串 '{}' 长度: {}", s6, len);
// 复杂的所有权转移
demonstrate_complex_ownership();
}
// 模式1: 获取所有权并返回
fn take_ownership_pattern(s: String) -> String {
println!("函数接收: {}", s);
s // 返回所有权
}
// 模式2: 借用,不获取所有权
fn borrow_pattern(s: &String) -> usize {
s.len()
}
// 模式3: 可变借用
fn modify_pattern(s: &mut String) {
s.push_str(" is awesome!");
}
// 模式4: 获取所有权,返回所有权和其他值
fn calculate_length_pattern(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
fn demonstrate_complex_ownership() {
println!("\n复杂所有权转移:");
let mut vec = vec![String::from("a"), String::from("b"), String::from("c")];
println!("原向量: {:?}", vec);
// 从向量中取出元素(移动所有权)
let first = vec.remove(0);
println!("取出的元素: {}", first);
println!("剩余向量: {:?}", vec);
// 添加元素
vec.push(String::from("d"));
println!("添加后: {:?}", vec);
// 交换元素
vec.swap(0, 1);
println!("交换后: {:?}", vec);
// 使用迭代器(借用)
for (i, item) in vec.iter().enumerate() {
println!("索引{}: {}", i, item);
}
// 向量仍然有效
println!("向量仍然存在: {:?}", vec);
}
rustc ownership_patterns.rs && ./ownership_patterns
结构体中的所有权
// 拥有数据的结构体
struct User {
username: String, // 拥有String
email: String, // 拥有String
sign_in_count: u64, // Copy类型
active: bool, // Copy类型
}
// 使用引用的结构体(需要生命周期参数)
struct UserRef<'a> {
username: &'a str, // 借用str
email: &'a str, // 借用str
sign_in_count: u64,
active: bool,
}
fn main() {
println!("🏗️ 结构体所有权示例");
println!("====================");
// 创建拥有数据的结构体
let user1 = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
sign_in_count: 1,
active: true,
};
println!("用户1: {} ({})", user1.username, user1.email);
// 部分移动
let username = user1.username; // 移动username
let email = user1.email; // 移动email
// user1.username 和 user1.email 不再有效
// 但 user1.sign_in_count 和 user1.active 仍然有效(Copy类型)
println!("登录次数: {}", user1.sign_in_count);
println!("是否活跃: {}", user1.active);
// println!("用户名: {}", user1.username); // 错误!已移动
// 使用移动的值
println!("移动的用户名: {}", username);
println!("移动的邮箱: {}", email);
// 创建新用户
let user2 = User {
username: String::from("bob"),
email: String::from("bob@example.com"),
sign_in_count: 0,
active: false,
};
// 使用引用访问结构体字段
print_user_info(&user2);
// user2仍然有效
println!("user2仍然有效: {}", user2.username);
// 结构体更新语法
let user3 = User {
username: String::from("charlie"),
email: String::from("charlie@example.com"),
..user2 // 复制其他字段(这里会移动user2的String字段)
};
println!("用户3: {} ({})", user3.username, user3.email);
// user2的String字段已被移动,但Copy字段仍可用
println!("user2的登录次数: {}", user2.sign_in_count);
// 使用引用的结构体
demonstrate_reference_struct();
}
fn print_user_info(user: &User) {
println!("用户信息: {} ({}), 登录{}次, 活跃: {}",
user.username, user.email, user.sign_in_count, user.active);
}
fn demonstrate_reference_struct() {
println!("\n引用结构体演示:");
let username = "david";
let email = "david@example.com";
let user_ref = UserRef {
username,
email,
sign_in_count: 5,
active: true,
};
println!("引用用户: {} ({})", user_ref.username, user_ref.email);
// 原始数据仍然有效
println!("原始用户名: {}", username);
println!("原始邮箱: {}", email);
}
rustc struct_ownership.rs && ./struct_ownership
4.5 常见所有权问题和解决方案
问题1:使用值后再次使用
fn main() {
println!("🔧 常见所有权问题和解决方案");
println!("==============================");
// 问题1: 使用值后再次使用
problem1_solution();
// 问题2: 在循环中移动值
problem2_solution();
// 问题3: 返回引用到局部变量
problem3_solution();
// 问题4: 可变引用和不可变引用冲突
problem4_solution();
}
fn problem1_solution() {
println!("\n问题1: 使用值后再次使用");
// 错误的做法(会编译失败)
/*
let s = String::from("hello");
let len = calculate_length_wrong(s); // s被移动
println!("长度: {}", len);
println!("字符串: {}", s); // 错误!s已被移动
*/
// 解决方案1: 使用引用
let s = String::from("hello");
let len = calculate_length_right(&s); // 借用s
println!("字符串: '{}', 长度: {}", s, len); // s仍然有效
// 解决方案2: 返回所有权
let s2 = String::from("world");
let (s3, len) = calculate_length_return(s2);
println!("字符串: '{}', 长度: {}", s3, len);
// 解决方案3: 克隆
let s4 = String::from("rust");
let len = calculate_length_wrong(s4.clone()); // 克隆s4
println!("字符串: '{}', 长度: {}", s4, len); // s4仍然有效
}
fn calculate_length_wrong(s: String) -> usize {
s.len()
}
fn calculate_length_right(s: &String) -> usize {
s.len()
}
fn calculate_length_return(s: String) -> (String, usize) {
let len = s.len();
(s, len)
}
fn problem2_solution() {
println!("\n问题2: 在循环中移动值");
let strings = vec![
String::from("hello"),
String::from("world"),
String::from("rust"),
];
// 错误的做法(会编译失败)
/*
for s in strings {
println!("{}", s); // s被移动到这里
}
println!("{:?}", strings); // 错误!strings已被移动
*/
// 解决方案1: 使用引用
for s in &strings {
println!("引用: {}", s);
}
println!("原向量仍然有效: {:?}", strings);
// 解决方案2: 使用索引
for i in 0..strings.len() {
println!("索引访问: {}", strings[i]);
}
// 解决方案3: 使用iter()
for s in strings.iter() {
println!("迭代器: {}", s);
}
println!("向量仍然存在: {:?}", strings);
}
fn problem3_solution() {
println!("\n问题3: 返回引用到局部变量");
// 错误的做法(会编译失败)
/*
fn create_string() -> &String {
let s = String::from("hello");
&s // 错误!返回对局部变量的引用
}
*/
// 解决方案1: 返回拥有的值
let s = create_string_owned();
println!("拥有的字符串: {}", s);
// 解决方案2: 使用静态字符串
let s = get_static_string();
println!("静态字符串: {}", s);
// 解决方案3: 接受参数并返回引用
let mut buffer = String::new();
let s = fill_string(&mut buffer);
println!("填充的字符串: {}", s);
}
fn create_string_owned() -> String {
String::from("hello")
}
fn get_static_string() -> &'static str {
"hello, world"
}
fn fill_string(buffer: &mut String) -> &str {
buffer.push_str("hello from buffer");
buffer
}
fn problem4_solution() {
println!("\n问题4: 可变引用和不可变引用冲突");
let mut s = String::from("hello");
// 错误的做法(会编译失败)
/*
let r1 = &s; // 不可变引用
let r2 = &s; // 另一个不可变引用
let r3 = &mut s; // 错误!不能在有不可变引用时创建可变引用
println!("{}, {}, {}", r1, r2, r3);
*/
// 解决方案1: 分离作用域
{
let r1 = &s;
let r2 = &s;
println!("不可变引用: {}, {}", r1, r2);
} // r1和r2在这里离开作用域
let r3 = &mut s;
r3.push_str(", world");
println!("可变引用: {}", r3);
// 解决方案2: 理解引用的生命周期
let r4 = &s;
let r5 = &s;
println!("不可变引用: {}, {}", r4, r5);
// r4和r5在这里结束使用
let r6 = &mut s; // 现在可以创建可变引用
r6.push_str("!");
println!("最终字符串: {}", r6);
}
rustc ownership_problems.rs && ./ownership_problems
4.6 综合示例:文本处理器
use std::collections::HashMap;
/// 文本统计信息
struct TextStats {
word_count: usize,
char_count: usize,
line_count: usize,
word_frequency: HashMap<String, usize>,
}
impl TextStats {
fn new() -> Self {
TextStats {
word_count: 0,
char_count: 0,
line_count: 0,
word_frequency: HashMap::new(),
}
}
fn analyze_text(&mut self, text: &str) {
self.char_count = text.chars().count();
self.line_count = text.lines().count();
// 分析单词
for word in text.split_whitespace() {
let word = word.to_lowercase();
let word = word.trim_matches(|c: char| !c.is_alphabetic());
if !word.is_empty() {
self.word_count += 1;
*self.word_frequency.entry(word.to_string()).or_insert(0) += 1;
}
}
}
fn get_most_frequent_word(&self) -> Option<(&String, &usize)> {
self.word_frequency
.iter()
.max_by_key(|(_, &count)| count)
}
fn print_stats(&self) {
println!("文本统计:");
println!(" 字符数: {}", self.char_count);
println!(" 单词数: {}", self.word_count);
println!(" 行数: {}", self.line_count);
if let Some((word, count)) = self.get_most_frequent_word() {
println!(" 最频繁单词: '{}' (出现{}次)", word, count);
}
}
}
/// 文本处理器
struct TextProcessor {
content: String,
}
impl TextProcessor {
fn new(content: String) -> Self {
TextProcessor { content }
}
fn get_content(&self) -> &str {
&self.content
}
fn append_text(&mut self, text: &str) {
self.content.push_str(text);
}
fn replace_text(&mut self, from: &str, to: &str) {
self.content = self.content.replace(from, to);
}
fn get_lines(&self) -> Vec<&str> {
self.content.lines().collect()
}
fn get_words(&self) -> Vec<&str> {
self.content.split_whitespace().collect()
}
fn find_word_positions(&self, word: &str) -> Vec<usize> {
let mut positions = Vec::new();
let content_lower = self.content.to_lowercase();
let word_lower = word.to_lowercase();
let mut start = 0;
while let Some(pos) = content_lower[start..].find(&word_lower) {
positions.push(start + pos);
start += pos + word_lower.len();
}
positions
}
}
fn main() {
println!("📝 文本处理器示例");
println!("==================");
// 创建文本处理器
let sample_text = String::from(
"Rust is a systems programming language. \
Rust is fast and memory-safe. \
Rust prevents segfaults and guarantees thread safety. \
Many companies use Rust for performance-critical applications."
);
let mut processor = TextProcessor::new(sample_text);
println!("原始文本:");
println!("{}", processor.get_content());
println!();
// 分析文本
let mut stats = TextStats::new();
stats.analyze_text(processor.get_content());
stats.print_stats();
println!();
// 获取行和单词
let lines = processor.get_lines();
println!("文本行数: {}", lines.len());
for (i, line) in lines.iter().enumerate() {
println!("第{}行: {}", i + 1, line);
}
println!();
let words = processor.get_words();
println!("前10个单词: {:?}", &words[..10.min(words.len())]);
println!();
// 查找单词位置
let positions = processor.find_word_positions("Rust");
println!("'Rust'出现的位置: {:?}", positions);
// 修改文本
processor.replace_text("Rust", "The Rust language");
println!("\n替换后的文本:");
println!("{}", processor.get_content());
// 添加文本
processor.append_text(" Rust is amazing!");
println!("\n添加文本后:");
println!("{}", processor.get_content());
// 重新分析
let mut new_stats = TextStats::new();
new_stats.analyze_text(processor.get_content());
println!();
new_stats.print_stats();
// 演示所有权转移
demonstrate_ownership_transfer(processor);
}
fn demonstrate_ownership_transfer(processor: TextProcessor) {
println!("\n所有权转移演示:");
println!("处理器内容长度: {}", processor.get_content().len());
// processor的所有权已转移到这个函数
// 当函数结束时,processor会被丢弃
println!("函数结束,处理器将被丢弃");
}
// 辅助函数:创建文本摘要
fn create_summary(text: &str, max_words: usize) -> String {
let words: Vec<&str> = text.split_whitespace().take(max_words).collect();
words.join(" ") + "..."
}
// 辅助函数:计算文本相似度(简单版本)
fn calculate_similarity(text1: &str, text2: &str) -> f64 {
let words1: std::collections::HashSet<&str> = text1.split_whitespace().collect();
let words2: std::collections::HashSet<&str> = text2.split_whitespace().collect();
let intersection = words1.intersection(&words2).count();
let union = words1.union(&words2).count();
if union == 0 {
0.0
} else {
intersection as f64 / union as f64
}
}
rustc text_processor.rs && ./text_processor
本章小结
通过本章学习,你应该掌握了:
✅ 所有权规则: 每个值有一个所有者,同时只能有一个所有者,所有者离开作用域时值被丢弃 ✅ 移动语义: 值的所有权如何在赋值和函数调用中转移 ✅ 借用机制: 使用引用访问值而不获取所有权 ✅ 引用规则: 不可变引用可以有多个,可变引用同时只能有一个 ✅ 切片类型: 对数组或字符串部分的引用 ✅ 内存安全: Rust如何在编译时防止内存错误
下一章预告
在下一章《数据结构》中,我们将学习:
- 结构体的定义和使用
- 枚举类型和模式匹配
- 方法和关联函数
- 结构体的所有权模式
- 复杂数据结构的设计
练习题
练习1:字符串操作
编写函数实现字符串的各种操作,注意所有权的处理: - 字符串连接 - 字符串分割 - 字符串查找和替换
练习2:向量管理
创建一个向量管理器,实现添加、删除、查找元素的功能,正确处理所有权。
练习3:引用计数
实现一个简单的引用计数系统,跟踪值被引用的次数。
练习4:文件路径处理
编写函数处理文件路径,包括路径连接、文件名提取、扩展名处理等。
恭喜你掌握了Rust的所有权系统! 🎉
所有权是Rust最重要的概念,理解它是掌握Rust的关键。现在让我们继续学习如何使用结构体和枚举来组织数据!