第十二篇: Rust 所有权、借用和生命周期完全指南

85 阅读18分钟

Rust 所有权、借用和生命周期完全指南

Rust 最独特和最强大的特性:所有权系统。通过编译期检查实现内存安全,无需垃圾回收。

目录

  1. 所有权(Ownership)
  2. 借用(Borrowing)
  3. 生命周期(Lifetime)
  4. 智能指针
  5. 实战示例
  6. 常见问题

所有权(Ownership)

为什么需要所有权?

在传统编程语言中:

  • 手动管理内存(C/C++):程序员负责 malloc/free,容易导致内存泄漏、悬垂指针
  • 垃圾回收(Java/Go):运行时自动回收,但有性能开销和不确定的暂停时间
  • Rust 的方案:编译期所有权检查,零运行时开销 + 内存安全

所有权三大规则

// 规则 1:每个值都有一个所有者(owner)
// 规则 2:同一时间只能有一个所有者
// 规则 3:当所有者离开作用域,值被丢弃(dropped)

1. 基本概念:移动(Move)

fn main() {
    // s1 是 String 的所有者
    let s1 = String::from("hello");
    
    // 所有权从 s1 移动到 s2
    let s2 = s1;
    
    // ❌ 错误:s1 的值已经被移动,不能再使用
    // println!("{}", s1); // 编译错误:value borrowed here after move
    
    // ✅ 正确:s2 现在是所有者
    println!("{}", s2); // 输出:hello
}

为什么会移动?

// String 在堆上分配内存
// 栈上存储:指针、长度、容量
// 堆上存储:实际字符串数据

let s1 = String::from("hello");
// 栈:s1 -> 指针 -> 堆:"hello"

let s2 = s1;
// 栈:s2 -> 指针 -> 堆:"hello"
// 栈:s1 (无效)

// 如果允许 s1 和 s2 同时有效,当它们离开作用域时
// 会尝试释放同一块内存两次(double free)
// Rust 通过移动语义避免这个问题

2. 复制(Copy)类型

对于存储在栈上的简单类型,Rust 会自动复制而不是移动:

fn main() {
    // i32 实现了 Copy trait
    let x = 5;
    let y = x;
    
    // ✅ x 和 y 都可以使用
    println!("x = {}, y = {}", x, y); // 输出:x = 5, y = 5
}

实现 Copy 的类型

// 所有整数类型:i8, i16, i32, i64, i128, u8, u16, u32, u64, u128
// 浮点类型:f32, f64
// 布尔类型:bool
// 字符类型:char
// 元组(如果所有元素都是 Copy):(i32, i32)
// 数组(如果元素是 Copy):[i32; 5]

fn example() {
    let tuple = (1, 2, 3);
    let tuple2 = tuple; // 复制,不是移动
    println!("{:?}", tuple); // ✅ 仍然可用
    
    let array = [1, 2, 3];
    let array2 = array; // 复制,不是移动
    println!("{:?}", array); // ✅ 仍然可用
}

3. 所有权与函数

fn main() {
    let s = String::from("hello");
    
    // s 的所有权移动到函数中
    takes_ownership(s);
    
    // ❌ 错误:s 的值已经被移动
    // println!("{}", s);
    
    let x = 5;
    
    // x 是 Copy 类型,传递副本
    makes_copy(x);
    
    // ✅ 正确:x 仍然可用
    println!("{}", x);
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string 离开作用域,调用 drop,释放内存

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer 离开作用域,没有特殊操作

4. 返回值与所有权

fn main() {
    let s1 = gives_ownership();         // 函数返回值的所有权移动给 s1
    
    let s2 = String::from("hello");     // s2 进入作用域
    
    let s3 = takes_and_gives_back(s2);  // s2 移动到函数,返回值移动给 s3
    
    // ❌ s2 不可用
    // ✅ s1 和 s3 可用
}

fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string // 返回值移动给调用者
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string // 返回值移动给调用者
}

5. 克隆(Clone)

如果确实需要深拷贝堆上的数据:

fn main() {
    let s1 = String::from("hello");
    
    // 显式克隆
    let s2 = s1.clone();
    
    // ✅ 两者都可用
    println!("s1 = {}, s2 = {}", s1, s2);
}

// 克隆的成本
fn expensive_clone() {
    let large_vec = vec![1; 1_000_000]; // 100 万个元素
    
    // ⚠️ 这会复制 100 万个元素
    let cloned = large_vec.clone();
    
    // 建议:尽量使用引用而不是克隆
}

借用(Borrowing)

为什么需要借用?

问题:每次传递值都移动所有权很麻烦

fn main() {
    let s = String::from("hello");
    
    let len = calculate_length(s);
    
    // ❌ s 已被移动,不能再使用
    // println!("The length of '{}' is {}", s, len);
}

fn calculate_length(s: String) -> usize {
    s.len()
} // s 离开作用域并被释放

解决方案:借用(创建引用,不获取所有权)

1. 不可变借用(Immutable Borrowing)

fn main() {
    let s = String::from("hello");
    
    // 创建不可变引用(借用)
    let len = calculate_length(&s);
    
    // ✅ s 仍然可用
    println!("The length of '{}' is {}", s, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
    // s 是引用,离开作用域不会释放数据
}

引用规则

fn main() {
    let s = String::from("hello");
    
    // 可以有多个不可变引用
    let r1 = &s;
    let r2 = &s;
    let r3 = &s;
    
    println!("{}, {}, {}", r1, r2, r3); // ✅ 正确
}

2. 可变借用(Mutable Borrowing)

fn main() {
    let mut s = String::from("hello");
    
    // 创建可变引用
    change(&mut s);
    
    println!("{}", s); // 输出:hello, world
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

3. 借用规则(重要!)

// 规则:在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用

fn main() {
    let mut s = String::from("hello");
    
    // ❌ 错误示例 1:同时有多个可变引用
    let r1 = &mut s;
    let r2 = &mut s; // ❌ 编译错误:cannot borrow `s` as mutable more than once
    // println!("{}, {}", r1, r2);
    
    // ❌ 错误示例 2:同时有可变引用和不可变引用
    let r1 = &s;         // 不可变引用
    let r2 = &s;         // 不可变引用
    let r3 = &mut s;     // ❌ 编译错误:cannot borrow as mutable
    // println!("{}, {}, {}", r1, r2, r3);
}

为什么有这些限制?

// 防止数据竞争(data race)

fn data_race_example() {
    let mut data = vec![1, 2, 3];
    
    // 假设允许同时有可变和不可变引用
    let r1 = &data;              // 读取引用
    let r2 = &mut data;          // 写入引用
    
    r2.push(4);                  // 修改数据,可能导致重新分配内存
    // println!("{:?}", r1);     // r1 指向的内存可能已失效!
    
    // Rust 在编译期就阻止了这种情况
}

4. 引用的作用域

fn main() {
    let mut s = String::from("hello");
    
    // ✅ 正确:引用的作用域在最后一次使用后结束
    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);
    // r1 和 r2 的作用域在这里结束
    
    // ✅ 可以创建可变引用
    let r3 = &mut s;
    println!("{}", r3);
}

5. 悬垂引用(Dangling References)

Rust 编译器防止悬垂引用:

// ❌ 错误示例
fn dangle() -> &String {
    let s = String::from("hello");
    &s  // ❌ 编译错误:返回局部变量的引用
}       // s 离开作用域被释放,引用指向无效内存

// ✅ 正确做法
fn no_dangle() -> String {
    let s = String::from("hello");
    s  // 移动所有权给调用者
}

6. 引用作为参数

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];
    
    // 不可变借用:只读访问
    print_vec(&numbers);
    
    // 可变借用:可以修改
    double_vec(&mut numbers);
    
    println!("{:?}", numbers); // [2, 4, 6, 8, 10]
}

fn print_vec(v: &Vec<i32>) {
    for num in v {
        print!("{} ", num);
    }
    println!();
}

fn double_vec(v: &mut Vec<i32>) {
    for num in v.iter_mut() {
        *num *= 2; // 解引用并修改
    }
}

7. 切片(Slice)- 特殊的引用

fn main() {
    let s = String::from("hello world");
    
    // 字符串切片
    let hello = &s[0..5];   // "hello"
    let world = &s[6..11];  // "world"
    
    // 语法糖
    let hello = &s[..5];    // 从开头到索引 5
    let world = &s[6..];    // 从索引 6 到结尾
    let whole = &s[..];     // 整个字符串
    
    println!("{}, {}", hello, world);
}

// 切片的实际用途
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

fn use_first_word() {
    let mut s = String::from("hello world");
    
    let word = first_word(&s);
    
    // ❌ 错误:不能在有不可变引用时修改
    // s.clear(); // 编译错误
    
    println!("first word: {}", word);
}

8. 数组切片

fn main() {
    let a = [1, 2, 3, 4, 5];
    
    // 数组切片
    let slice = &a[1..3];
    
    println!("{:?}", slice); // [2, 3]
    
    // 函数使用切片
    let sum = sum_slice(&a[..]);
    println!("sum: {}", sum);
}

fn sum_slice(slice: &[i32]) -> i32 {
    slice.iter().sum()
}

生命周期(Lifetime)

为什么需要生命周期?

// 问题:引用的有效期是多久?

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
// ❌ 编译错误:missing lifetime specifier
// 编译器不知道返回的引用是 x 还是 y,无法确定其生命周期

1. 生命周期标注语法

// 生命周期参数以 ' 开头,通常使用 'a, 'b, 'c
// 不改变引用的实际生命周期,只是描述引用之间的关系

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
// 含义:返回值的生命周期与参数中生命周期较短的那个相同

fn main() {
    let string1 = String::from("long string");
    let string2 = String::from("short");
    
    let result = longest(string1.as_str(), string2.as_str());
    println!("longest: {}", result);
}

2. 生命周期规则详解

// 示例 1:有效的使用
fn example1() {
    let string1 = String::from("long string is long");
    
    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    } // result, string2 离开作用域
}

// 示例 2:无效的使用
fn example2() {
    let string1 = String::from("long string is long");
    let result;
    
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    } // ❌ string2 离开作用域,result 可能指向无效内存
    
    // println!("The longest string is {}", result); // 编译错误
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

3. 深入理解生命周期

// 'a 表示一个生命周期,是一个泛型参数

// 例 1:返回值的生命周期与一个参数关联
fn first_word<'a>(s: &'a str) -> &'a str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

// 例 2:返回值的生命周期与第一个参数关联
fn longest_string<'a>(x: &'a str, y: &str) -> &'a str {
    x // 只返回 x,所以只需要 x 的生命周期
}

// 例 3:多个生命周期参数
fn longest_with_announcement<'a, 'b>(
    x: &'a str,
    y: &'b str,
    ann: &str,
) -> &'a str {
    println!("Announcement: {}", ann);
    if x.len() > y.len() {
        x
    } else {
        // 如果要返回 y,需要改为 -> &'b str 或使用相同的生命周期
        x
    }
}

4. 结构体中的生命周期

// 结构体持有引用时需要生命周期标注
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    // 方法中的生命周期
    fn level(&self) -> i32 {
        3
    }
    
    // 返回引用的方法
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention: {}", announcement);
        self.part
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    
    // i 的生命周期不能超过 novel
    let i = ImportantExcerpt {
        part: first_sentence,
    };
    
    println!("Excerpt: {}", i.part);
}

5. 生命周期省略规则

编译器可以在某些情况下自动推断生命周期:

// 规则 1:每个引用参数都有自己的生命周期
fn print(s: &str) {
    // 等价于:fn print<'a>(s: &'a str)
    println!("{}", s);
}

// 规则 2:如果只有一个输入生命周期,它被赋予所有输出生命周期
fn first_word(s: &str) -> &str {
    // 等价于:fn first_word<'a>(s: &'a str) -> &'a str
    &s[..1]
}

// 规则 3:如果有多个输入生命周期,其中一个是 &self 或 &mut self,
//        self 的生命周期被赋予所有输出生命周期
struct StrWrapper<'a> {
    s: &'a str,
}

impl<'a> StrWrapper<'a> {
    fn get_str(&self) -> &str {
        // 等价于:fn get_str(&'a self) -> &'a str
        self.s
    }
}

// 需要显式标注的情况
fn longest(x: &str, y: &str) -> &str {
    // ❌ 编译器无法推断,需要显式标注
    if x.len() > y.len() { x } else { y }
}

6. 静态生命周期

// 'static 表示整个程序运行期间都有效

// 字符串字面量有 'static 生命周期
let s: &'static str = "I have a static lifetime.";

// 不要轻易使用 'static
fn wrong_use() -> &'static str {
    let s = String::from("hello");
    // &s // ❌ 错误:s 不是静态的
    "hello" // ✅ 字符串字面量是静态的
}

// 正确使用 'static 的场景
static GLOBAL_CONFIG: &str = "configuration";

fn use_static() {
    println!("{}", GLOBAL_CONFIG);
}

7. 复杂的生命周期示例

// 示例 1:结构体方法返回引用
struct Context<'a>(&'a str);

impl<'a> Context<'a> {
    fn parse(&self) -> Result<(), &str> {
        // 返回的 &str 生命周期与 self 相同
        Err(&self.0[1..])
    }
}

// 示例 2:多个生命周期
struct Parser<'c, 's> {
    context: &'c Context<'c>,
    input: &'s str,
}

impl<'c, 's> Parser<'c, 's> {
    fn parse(&self) -> Result<(), &'s str> {
        // 返回的引用与 input 的生命周期相同
        self.context.parse()
    }
}

// 示例 3:生命周期约束
struct Ref<'a, T: 'a>(&'a T);
// T: 'a 表示 T 中的所有引用都必须活得比 'a 长

智能指针

1. Box - 堆分配

fn main() {
    // 在堆上分配
    let b = Box::new(5);
    println!("b = {}", b);
    
    // 递归类型必须使用 Box
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

2. Rc - 引用计数

use std::rc::Rc;

fn main() {
    // 多个所有者共享数据
    let a = Rc::new(5);
    
    // 克隆 Rc 增加引用计数,不克隆数据
    let b = Rc::clone(&a);
    let c = Rc::clone(&a);
    
    println!("count: {}", Rc::strong_count(&a)); // 3
    
    {
        let d = Rc::clone(&a);
        println!("count: {}", Rc::strong_count(&a)); // 4
    }
    
    println!("count: {}", Rc::strong_count(&a)); // 3
}

// Rc 的实际应用
fn rc_example() {
    use std::rc::Rc;
    
    enum List {
        Cons(i32, Rc<List>),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
    
    // b 和 c 共享 a 的所有权
}

3. RefCell - 内部可变性

use std::cell::RefCell;

fn main() {
    // 运行时借用检查
    let data = RefCell::new(5);
    
    // 获取不可变引用
    let r1 = data.borrow();
    let r2 = data.borrow();
    println!("{}, {}", r1, r2);
    
    drop(r1);
    drop(r2);
    
    // 获取可变引用
    let mut r3 = data.borrow_mut();
    *r3 += 1;
    println!("{}", r3);
}

// RefCell 允许内部可变性
pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct MockMessenger {
    sent_messages: RefCell<Vec<String>>,
}

impl MockMessenger {
    pub fn new() -> MockMessenger {
        MockMessenger {
            sent_messages: RefCell::new(vec![]),
        }
    }
}

impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        // self 是不可变的,但可以修改内部数据
        self.sent_messages.borrow_mut().push(String::from(message));
    }
}

4. Rc - 多所有者 + 可变

use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    // 多个所有者,可变数据
    let value = Rc::new(RefCell::new(5));
    
    let a = Rc::clone(&value);
    let b = Rc::clone(&value);
    let c = Rc::clone(&value);
    
    *a.borrow_mut() += 10;
    println!("{}", value.borrow()); // 15
    
    *b.borrow_mut() += 20;
    println!("{}", value.borrow()); // 35
}

实战示例

示例 1:实现链表

type Link<T> = Option<Box<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

pub struct LinkedList<T> {
    head: Link<T>,
}

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        LinkedList { head: None }
    }
    
    pub fn push(&mut self, elem: T) {
        let new_node = Box::new(Node {
            elem,
            next: self.head.take(),
        });
        
        self.head = Some(new_node);
    }
    
    pub fn pop(&mut self) -> Option<T> {
        self.head.take().map(|node| {
            self.head = node.next;
            node.elem
        })
    }
    
    pub fn peek(&self) -> Option<&T> {
        self.head.as_ref().map(|node| &node.elem)
    }
    
    pub fn peek_mut(&mut self) -> Option<&mut T> {
        self.head.as_mut().map(|node| &mut node.elem)
    }
}

impl<T> Drop for LinkedList<T> {
    fn drop(&mut self) {
        let mut cur_link = self.head.take();
        while let Some(mut boxed_node) = cur_link {
            cur_link = boxed_node.next.take();
        }
    }
}

fn test_list() {
    let mut list = LinkedList::new();
    
    list.push(1);
    list.push(2);
    list.push(3);
    
    assert_eq!(list.pop(), Some(3));
    assert_eq!(list.peek(), Some(&2));
    assert_eq!(list.pop(), Some(2));
    assert_eq!(list.pop(), Some(1));
    assert_eq!(list.pop(), None);
}

示例 2:实现迭代器

pub struct LinkedList<T> {
    head: Link<T>,
}

type Link<T> = Option<Box<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

// 不可变迭代器
pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

impl<T> LinkedList<T> {
    pub fn iter(&self) -> Iter<T> {
        Iter {
            next: self.head.as_deref(),
        }
    }
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    
    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.as_deref();
            &node.elem
        })
    }
}

// 可变迭代器
pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<T> LinkedList<T> {
    pub fn iter_mut(&mut self) -> IterMut<T> {
        IterMut {
            next: self.head.as_deref_mut(),
        }
    }
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;
    
    fn next(&mut self) -> Option<Self::Item> {
        self.next.take().map(|node| {
            self.next = node.next.as_deref_mut();
            &mut node.elem
        })
    }
}

fn test_iterator() {
    let mut list = LinkedList::new();
    list.push(1);
    list.push(2);
    list.push(3);
    
    // 不可变迭代
    for elem in list.iter() {
        println!("{}", elem);
    }
    
    // 可变迭代
    for elem in list.iter_mut() {
        *elem *= 2;
    }
}

示例 3:实现缓存

use std::collections::HashMap;
use std::hash::Hash;

struct Cacher<T, U, V>
where
    T: Fn(&U) -> V,
    U: Eq + Hash,
{
    calculation: T,
    cache: HashMap<U, V>,
}

impl<T, U, V> Cacher<T, U, V>
where
    T: Fn(&U) -> V,
    U: Eq + Hash + Clone,
    V: Clone,
{
    fn new(calculation: T) -> Cacher<T, U, V> {
        Cacher {
            calculation,
            cache: HashMap::new(),
        }
    }
    
    fn value(&mut self, arg: U) -> V {
        match self.cache.get(&arg) {
            Some(v) => v.clone(),
            None => {
                let v = (self.calculation)(&arg);
                self.cache.insert(arg, v.clone());
                v
            }
        }
    }
}

fn expensive_calculation(intensity: &u32) -> u32 {
    println!("calculating slowly...");
    std::thread::sleep(std::time::Duration::from_secs(2));
    *intensity
}

fn use_cacher() {
    let mut cacher = Cacher::new(expensive_calculation);
    
    println!("{}", cacher.value(10)); // 慢
    println!("{}", cacher.value(10)); // 快(使用缓存)
    println!("{}", cacher.value(20)); // 慢
    println!("{}", cacher.value(20)); // 快(使用缓存)
}

示例 4:本项目中的所有权应用

// src/services/user_service.rs

use sqlx::MySqlPool;
use crate::models::user::User;
use crate::utils::cache::RedisCache;

pub struct UserService {
    // 拥有 MySqlPool 的所有权
    db: MySqlPool,
    // 拥有 RedisCache 的所有权
    cache: RedisCache,
}

impl UserService {
    // 取得所有权
    pub fn new(db: MySqlPool, cache: RedisCache) -> Self {
        Self { db, cache }
    }
    
    // 借用 self(不可变引用)
    pub async fn get_user(&self, user_id: i64) -> Result<Option<User>> {
        let cache_key = format!("user:{}", user_id);
        
        // 借用 cache
        if let Some(cached) = self.cache.get(&cache_key).await? {
            // 反序列化返回(移动所有权给调用者)
            return Ok(Some(serde_json::from_str(&cached)?));
        }
        
        // 借用 db
        let user = sqlx::query_as::<_, User>(
            "SELECT * FROM users WHERE id = ?"
        )
        .bind(user_id)
        .fetch_optional(&self.db) // &self.db 是借用
        .await?;
        
        // 返回 Option<User>(移动所有权)
        Ok(user)
    }
    
    // 借用 self(可变引用)- 如果需要修改内部状态
    // 注意:在本例中,即使修改数据,也只需要不可变引用
    // 因为 MySqlPool 和 RedisCache 内部使用了内部可变性
}

// 使用示例
async fn use_service() {
    let pool = create_pool().await;
    let cache = RedisCache::new("redis://localhost").await.unwrap();
    
    // service 拥有 pool 和 cache 的所有权
    let service = UserService::new(pool, cache);
    
    // 借用 service
    if let Ok(Some(user)) = service.get_user(1).await {
        // user 拥有数据的所有权
        println!("{:?}", user);
    }
    
    // service 离开作用域,pool 和 cache 被清理
}

示例 5:生命周期在本项目中的应用

// src/models/user.rs

// 结构体持有引用,需要生命周期标注
pub struct UserView<'a> {
    username: &'a str,
    email: &'a str,
}

impl<'a> UserView<'a> {
    // 从 User 创建视图(借用)
    pub fn from_user(user: &'a User) -> Self {
        UserView {
            username: &user.username,
            email: &user.email,
        }
    }
    
    // 返回的引用与 self 的生命周期相同
    pub fn get_username(&self) -> &str {
        self.username
    }
}

// 使用示例
fn use_user_view() {
    let user = User {
        id: 1,
        username: String::from("alice"),
        email: String::from("alice@example.com"),
        // ...
    };
    
    // 视图借用 user 的数据
    let view = UserView::from_user(&user);
    println!("{}", view.get_username());
    
    // view 的生命周期不能超过 user
}

常见问题

1. 如何选择使用 & 还是 &mut?

// 规则:
// - 需要修改数据:使用 &mut
// - 只需要读取数据:使用 &
// - 多个函数同时读取:使用 &(可以有多个)
// - 一个函数独占修改:使用 &mut(只能有一个)

fn read_only(data: &Vec<i32>) {
    // 只读,不修改
    for item in data {
        println!("{}", item);
    }
}

fn modify(data: &mut Vec<i32>) {
    // 修改数据
    data.push(42);
}

fn main() {
    let mut numbers = vec![1, 2, 3];
    
    read_only(&numbers); // 借用
    read_only(&numbers); // 可以多次借用
    
    modify(&mut numbers); // 可变借用
    // modify(&mut numbers); // ❌ 不能同时有多个可变借用
}

2. 如何理解 "借用检查器"?

// 借用检查器在编译期检查:
// 1. 没有悬垂引用
// 2. 不会同时有可变和不可变引用
// 3. 不会有多个可变引用

fn borrow_checker() {
    let mut s = String::from("hello");
    
    // 阶段 1:不可变借用
    let r1 = &s;
    let r2 = &s;
    println!("{} {}", r1, r2);
    // r1 和 r2 的作用域结束
    
    // 阶段 2:可变借用(安全,因为之前的不可变引用已经结束)
    let r3 = &mut s;
    r3.push_str(" world");
    println!("{}", r3);
}

3. 什么时候使用 Clone?

// 使用 Clone 的场景:
// 1. 需要真正的副本
// 2. 多个所有者需要独立修改数据
// 3. 跨线程传递数据

fn when_to_clone() {
    let original = vec![1, 2, 3];
    
    // 场景 1:需要独立的副本
    let mut copy = original.clone();
    copy.push(4);
    // original 不受影响
    
    // 场景 2:传递给会取得所有权的函数
    process_vec(original.clone());
    // original 仍然可用
    
    // 场景 3:避免,如果可以用引用
    // ❌ 不必要的克隆
    let unnecessary = original.clone();
    print_vec(&unnecessary);
    
    // ✅ 更好的方式
    print_vec(&original);
}

fn process_vec(v: Vec<i32>) {
    // 取得所有权
}

fn print_vec(v: &Vec<i32>) {
    // 只是借用
}

4. String vs &str

// String:拥有所有权的字符串(堆分配)
// &str:字符串切片(引用)

fn string_vs_str() {
    // String - 可变、可增长
    let mut s = String::from("hello");
    s.push_str(" world");
    
    // &str - 不可变、固定大小
    let s1: &str = "hello";           // 字符串字面量
    let s2: &str = &s;                // String 的引用
    let s3: &str = &s[0..5];          // 切片
    
    // 函数参数:优先使用 &str(更灵活)
    fn print_str(s: &str) {
        println!("{}", s);
    }
    
    print_str("literal");             // ✅
    print_str(&s);                    // ✅
    print_str(&s[..]);                // ✅
    
    // 如果需要所有权或修改,使用 String
    fn take_ownership(s: String) {
        // 拥有 s
    }
}

5. Vec vs &[T]

// Vec<T>:拥有所有权的动态数组
// &[T]:数组切片(引用)

fn vec_vs_slice() {
    // Vec - 可变、可增长
    let mut v = vec![1, 2, 3];
    v.push(4);
    
    // &[T] - 引用,不拥有数据
    let slice: &[i32] = &v;
    let slice2: &[i32] = &v[1..3];
    
    // 函数参数:优先使用 &[T](更灵活)
    fn sum_slice(numbers: &[i32]) -> i32 {
        numbers.iter().sum()
    }
    
    let array = [1, 2, 3];
    sum_slice(&array);    // ✅ 数组
    sum_slice(&v);        // ✅ Vec
    sum_slice(slice);     // ✅ 切片
}

6. 如何处理 "cannot move out of borrowed content"?

struct Container {
    data: String,
}

fn move_error() {
    let c = Container {
        data: String::from("hello"),
    };
    
    let r = &c;
    
    // ❌ 错误:不能从借用的内容中移出
    // let s = r.data;
    
    // ✅ 解决方案 1:克隆
    let s = r.data.clone();
    
    // ✅ 解决方案 2:使用引用
    let s = &r.data;
    
    // ✅ 解决方案 3:如果不再需要 c,获取所有权
    let s = c.data; // c 被移动
}

7. 循环引用问题

use std::rc::Rc;
use std::cell::RefCell;

// ❌ 问题:循环引用导致内存泄漏
struct Node {
    next: Option<Rc<RefCell<Node>>>,
}

fn memory_leak() {
    let a = Rc::new(RefCell::new(Node { next: None }));
    let b = Rc::new(RefCell::new(Node { next: Some(Rc::clone(&a)) }));
    
    // 创建循环
    a.borrow_mut().next = Some(Rc::clone(&b));
    
    // a 和 b 相互引用,引用计数永远不会归零
}

// ✅ 解决方案:使用 Weak<T>
use std::rc::Weak;

struct BetterNode {
    next: Option<Rc<RefCell<BetterNode>>>,
    prev: Option<Weak<RefCell<BetterNode>>>, // 使用 Weak 避免循环
}

总结

所有权规则速查

概念规则用途
所有权每个值有唯一所有者自动内存管理
移动默认转移所有权避免双重释放
复制Copy 类型自动复制简单类型高效
借用创建引用不获取所有权临时访问数据
不可变引用可以有多个 &T共享只读访问
可变引用只能有一个 &mut T独占修改访问
生命周期引用有效期标注防止悬垂引用

关键原则

  1. 默认移动:赋值和传参默认移动所有权
  2. 明确借用:使用 & 创建引用
  3. 单一可变:同时只能有一个可变引用
  4. 引用安全:引用的生命周期不能超过数据
  5. 编译期检查:所有检查在编译期完成,零运行时开销

实践建议

// ✅ 好的实践

// 1. 参数优先使用引用
fn process(data: &Vec<i32>) { }

// 2. 返回值可以移动所有权
fn create() -> Vec<i32> {
    vec![1, 2, 3]
}

// 3. 需要修改才用 &mut
fn modify(data: &mut Vec<i32>) {
    data.push(42);
}

// 4. 使用切片而不是具体类型
fn flexible(s: &str) { }      // 而不是 &String
fn flexible2(nums: &[i32]) { } // 而不是 &Vec<i32>

// 5. 生命周期让编译器推断
fn auto_lifetime(s: &str) -> &str { s }
// 而不是:fn explicit<'a>(s: &'a str) -> &'a str { s }

调试建议

// 遇到借用检查错误时:

// 1. 检查引用的作用域
// 2. 确认是否需要 &mut
// 3. 考虑使用 clone()(临时方案)
// 4. 重新设计数据结构
// 5. 使用智能指针(Rc, RefCell)

记住:Rust 的所有权系统看起来复杂,但它在编译期就防止了大量的运行时错误!掌握它是成为 Rust 高手的关键。