"补充第二章所有权内容,新增移动语义、借用检查、字符串转换等详细示例和实践"
This commit is contained in:
parent
52a1aa7bdf
commit
0b1fc76e73
222
02.md
222
02.md
|
|
@ -1,4 +1,92 @@
|
|||
# 第二章:所有权:Rust的灵魂解码(补充版)
|
||||
# 第二章:所有权:Rust的灵魂解码
|
||||
|
||||
所有权系统是Rust最独特且强大的特性,它让Rust能够在编译期保证内存安全而无需垃圾回收。本章将深入探讨所有权的核心机制及其工程实践。
|
||||
|
||||
## 2.1 移动语义 vs 克隆
|
||||
|
||||
Rust中的赋值操作默认采用移动语义,而非浅拷贝或深拷贝。理解这一点对掌握Rust至关重要。
|
||||
|
||||
### 移动语义示例
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("Rust");
|
||||
let s2 = s1; // s1的所有权移动到s2
|
||||
|
||||
// println!("{}", s1); // 编译错误!s1不再有效
|
||||
println!("{}", s2); // 正确
|
||||
}
|
||||
```
|
||||
|
||||
### 克隆实现深拷贝
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("Rust");
|
||||
let s2 = s1.clone(); // 显式深拷贝
|
||||
|
||||
println!("s1 = {}, s2 = {}", s1, s2); // 两者都有效
|
||||
}
|
||||
```
|
||||
|
||||
### 栈数据的拷贝语义
|
||||
```rust
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = x; // 栈上的简单值自动拷贝
|
||||
|
||||
println!("x = {}, y = {}", x, y); // 两者都有效
|
||||
}
|
||||
```
|
||||
|
||||
## 2.2 借用检查器错误分析
|
||||
|
||||
Rust的借用检查器防止数据竞争和悬垂指针。以下是常见错误及解决方案:
|
||||
|
||||
### 错误1:同时存在可变与不可变引用
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
let r1 = &s; // 不可变借用
|
||||
let r2 = &s; // 另一个不可变借用
|
||||
let r3 = &mut s; // 错误!可变借用冲突
|
||||
|
||||
println!("{}, {}, and {}", r1, r2, r3);
|
||||
}
|
||||
```
|
||||
|
||||
**解决方案**:确保作用域不重叠
|
||||
```rust
|
||||
fn main() {
|
||||
let mut s = String::from("hello");
|
||||
|
||||
let r1 = &s;
|
||||
let r2 = &s;
|
||||
println!("{} and {}", r1, r2); // 作用域结束
|
||||
|
||||
let r3 = &mut s; // 现在允许
|
||||
r3.push_str(", world");
|
||||
}
|
||||
```
|
||||
|
||||
### 错误2:悬垂引用
|
||||
```rust
|
||||
fn main() {
|
||||
let r = dangle(); // 返回悬垂引用
|
||||
}
|
||||
|
||||
fn dangle() -> &String {
|
||||
let s = String::from("hello");
|
||||
&s // 错误!s离开作用域被释放
|
||||
}
|
||||
```
|
||||
|
||||
**解决方案**:返回所有权而非引用
|
||||
```rust
|
||||
fn no_dangle() -> String {
|
||||
let s = String::from("hello");
|
||||
s // 所有权被移出
|
||||
}
|
||||
```
|
||||
|
||||
## 2.3 生命周期标注详解:实践与约束
|
||||
|
||||
|
|
@ -116,6 +204,138 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
## 2.4 String与&str转换场景
|
||||
|
||||
理解String(堆分配)和&str(字符串切片)的区别及转换:
|
||||
|
||||
| 操作 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| String → &str | `&s` 或 `s.as_str()` | 零成本转换 |
|
||||
| &str → String | `to_string()` 或 `String::from()` | 需要内存分配 |
|
||||
| 连接String | `s1 + &s2` | s2需转换为&str |
|
||||
| 连接多个 | `format!("{}{}", s1, s2)` | 更清晰的方式 |
|
||||
|
||||
```rust
|
||||
fn process_text(text: &str) { // 接受String或&str
|
||||
println!("Processing: {}", text);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = String::from("hello");
|
||||
let slice = "world";
|
||||
|
||||
process_text(&s); // String转&str
|
||||
process_text(slice); // 直接使用&str
|
||||
|
||||
let combined = s + " " + slice; // 连接字符串
|
||||
println!("{}", combined);
|
||||
}
|
||||
```
|
||||
|
||||
## 2.5 实现其他语言的常量与变量
|
||||
|
||||
Rust通过`let`和`const`提供变量和常量,但语义与其他语言不同:
|
||||
|
||||
| 类型 | 关键字 | 可变性 | 作用域 | 内存位置 |
|
||||
|------|--------|--------|--------|----------|
|
||||
| 变量 | `let` | 默认不可变,`mut`可变 | 块作用域 | 栈/堆 |
|
||||
| 常量 | `const` | 永远不可变 | 全局 | 编译期已知 |
|
||||
| 静态变量 | `static` | 可声明为可变(unsafe) | 全局 | 固定内存地址 |
|
||||
|
||||
```rust
|
||||
const MAX_USERS: u32 = 100_000; // 编译时常量
|
||||
|
||||
static mut COUNTER: u32 = 0; // 可变全局变量(需unsafe)
|
||||
|
||||
fn main() {
|
||||
// 不可变变量
|
||||
let x = 5;
|
||||
// x = 6; // 错误
|
||||
|
||||
// 可变变量
|
||||
let mut y = 10;
|
||||
y += 1;
|
||||
|
||||
// 隐藏变量
|
||||
let z = 5;
|
||||
let z = z + 1;
|
||||
let z = z * 2;
|
||||
println!("z = {}", z); // 12
|
||||
|
||||
// 使用全局可变变量(需要unsafe块)
|
||||
unsafe {
|
||||
COUNTER += 1;
|
||||
println!("Counter: {}", COUNTER);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2.6 实例:安全字符串处理器
|
||||
|
||||
实现一个安全的字符串处理器,避免悬垂指针:
|
||||
|
||||
```rust
|
||||
struct StringProcessor<'a> {
|
||||
source: &'a str,
|
||||
processed: String,
|
||||
}
|
||||
|
||||
impl<'a> StringProcessor<'a> {
|
||||
fn new(source: &'a str) -> Self {
|
||||
StringProcessor {
|
||||
source,
|
||||
processed: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self) {
|
||||
// 示例处理:转换为大写并添加前缀
|
||||
self.processed = self.source
|
||||
.chars()
|
||||
.map(|c| c.to_ascii_uppercase())
|
||||
.collect();
|
||||
self.processed = format!("PROCESSED: {}", self.processed);
|
||||
}
|
||||
|
||||
fn get_result(&self) -> &str {
|
||||
&self.processed
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = String::from("hello rust");
|
||||
|
||||
let mut processor = StringProcessor::new(&input);
|
||||
processor.process();
|
||||
|
||||
// input在这里仍然有效
|
||||
println!("Original: {}", input);
|
||||
println!("Result: {}", processor.get_result());
|
||||
|
||||
// 处理器结果的生命周期独立于输入
|
||||
let result;
|
||||
{
|
||||
let temp = String::from("temporary");
|
||||
let mut p2 = StringProcessor::new(&temp);
|
||||
p2.process();
|
||||
result = p2.get_result().to_string(); // 获取所有权
|
||||
} // temp离开作用域被释放
|
||||
|
||||
// 但result拥有独立的数据
|
||||
println!("Saved result: {}", result);
|
||||
}
|
||||
```
|
||||
|
||||
### 安全设计要点:
|
||||
1. 使用生命周期标注确保源字符串的引用有效
|
||||
2. 处理结果存储在String中,拥有独立所有权
|
||||
3. 返回结果时提供引用,避免不必要的拷贝
|
||||
4. 需要长期保存结果时,使用to_string()获取所有权
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 2.7 实例:单例模式的安全实现
|
||||
|
||||
在Rust中实现线程安全的单例模式需要特殊处理,因为全局可变状态需要同步机制。以下是使用`OnceLock`的现代实现:
|
||||
|
|
|
|||
Loading…
Reference in New Issue