Skip to content

Rust 引用与解引用完全指南

📚 一、引用基本概念

1. 引用是什么?

引用是 Rust 中的一种指针,它允许你访问数据而不获取其所有权。引用是借用(borrowing)的体现。

fn main() {
    let x = 5;
    let y = &x;  // y 是 x 的引用

    println!("x = {}, y = {}", x, *y);  // 解引用
}

2. 引用的特点

  • 不拥有所有权:只是借用数据
  • 有生命周期:确保不会出现悬垂引用
  • 类型安全:编译时检查
  • 零成本抽象:运行时几乎没有开销

🎯 二、引用基本操作

1. 创建引用

fn basic_references() {
    let x = 10;
    let s = String::from("hello");

    // 不可变引用
    let r1 = &x;      // &i32
    let r2 = &s;      // &String
    let r3 = &s[..];  // &str

    // 可变引用
    let mut y = 20;
    let m1 = &mut y;  // &mut i32

    // 多重不可变引用
    let r4 = &x;
    let r5 = &x;      // ✅ 允许多个不可变引用

    // ❌ 不能同时有可变和不可变引用
    // let r6 = &y;     // 编译错误
    // let m2 = &mut y; // 编译错误
}

2. 解引用操作

fn dereferencing() {
    let x = 5;
    let r = &x;

    // 方法1: 使用 * 运算符
    assert_eq!(5, *r);

    // 方法2: 自动解引用
    let s = String::from("hello");
    let rs = &s;

    println!("长度: {}", rs.len());  // 自动解引用
    println!("长度: {}", (*rs).len());  // 手动解引用

    // 方法3: 在赋值时解引用
    let mut y = 10;
    let ry = &mut y;
    *ry = 20;  // 通过引用修改值

    println!("y = {}", y);  // 20
}

🔄 三、引用规则

1. 借用规则

fn borrowing_rules() {
    let mut data = vec![1, 2, 3];

    // 规则1: 任意时刻,要么只能有一个可变引用
    // 规则2: 要么只能有多个不可变引用
    // 规则3: 引用必须总是有效的

    // ✅ 正确示例
    let r1 = &data;        // 不可变引用
    let r2 = &data;        // 另一个不可变引用
    println!("{}, {}", r1[0], r2[1]);

    // 不可变引用使用完毕后
    drop(r1);
    drop(r2);

    // ✅ 现在可以获取可变引用
    let r3 = &mut data;
    r3.push(4);

    // ❌ 错误示例
    // let r4 = &data;      // 编译错误:已有可变引用
    // let r5 = &mut data;  // 编译错误:已有可变引用
}

2. 作用域的重要性

fn scope_examples() {
    let mut s = String::from("hello");

    {
        let r1 = &s;  // 不可变引用
        println!("r1: {}", r1);
    }  // r1 离开作用域,不再占用引用

    let r2 = &mut s;  // ✅ 现在可以获取可变引用
    r2.push_str(" world");

    println!("r2: {}", r2);
}

🔧 四、自动解引用

1. 自动解引用场景

fn auto_deref_examples() {
    // 场景1: 方法调用
    let s = String::from("hello");
    let rs = &s;

    println!("长度: {}", rs.len());  // 自动调用 s.len()
    println!("大写: {}", rs.to_uppercase());

    // 场景2: 字段访问
    struct Point {
        x: i32,
        y: i32,
    }

    let p = Point { x: 5, y: 10 };
    let rp = &p;

    println!("x = {}, y = {}", rp.x, rp.y);  // 自动解引用

    // 场景3: 运算符
    let x = 5;
    let y = 10;
    let rx = &x;
    let ry = &y;

    println!("和: {}", rx + ry);  // 自动解引用
    println!("相等: {}", rx == ry);

    // 场景4: 模式匹配
    let option = Some(5);
    let ro = &option;

    match ro {
        Some(value) => println!("值: {}", value),  // 自动解引用
        None => println!("无"),
    }
}

2. 自动解引用级别

fn auto_deref_levels() {
    let s = String::from("hello");

    // 多级引用
    let r1 = &s;        // &String
    let r2 = &r1;       // &&String
    let r3 = &r2;       // &&&String

    // 所有级别都能自动解引用
    println!("长度1: {}", s.len());     // String.len()
    println!("长度2: {}", r1.len());    // &String -> String.len()
    println!("长度3: {}", r2.len());    // &&String -> &String -> String.len()
    println!("长度4: {}", r3.len());    // &&&String -> &&String -> &String -> String.len()

    // 显示解引用
    println!("显式: {}", (***r3).len());  // 手动三级解引用
}

🎨 五、引用与模式匹配

1. 引用模式

fn ref_patterns() {
    let x = 5;
    let y = &x;

    // 匹配引用
    match y {
        &val => println!("值: {}", val),  // 解构引用
    }

    // 使用 ref 关键字
    let value = Some(5);

    match value {
        Some(ref v) => {  // 使用 ref 获取引用
            println!("引用值: {}", v);
        }
        None => (),
    }

    // 复杂结构
    let point = (10, 20);

    match point {
        (ref x, ref y) => {  // 获取每个字段的引用
            println!("点: ({}, {})", x, y);
        }
    }
}

2. ref 和 ref mut

fn ref_and_ref_mut() {
    let mut data = vec![1, 2, 3];

    // 使用 ref 获取不可变引用
    match data.get(0) {
        Some(ref value) => {
            println!("第一个元素: {}", value);
            // *value = 10;  // ❌ 编译错误:value 是不可变引用
        }
        None => (),
    }

    // 使用 ref mut 获取可变引用
    let mut option = Some(String::from("hello"));

    match option {
        Some(ref mut s) => {  // 获取可变引用
            s.push_str(" world");
            println!("修改后: {}", s);
        }
        None => (),
    }

    println!("最终: {:?}", option);
}

🔧 六、解引用强制转换

1. 解引用强制转换类型

fn deref_coercion() {
    use std::ops::Deref;

    // 定义自定义类型
    struct MyBox<T>(T);

    impl<T> MyBox<T> {
        fn new(x: T) -> MyBox<T> {
            MyBox(x)
        }
    }

    impl<T> Deref for MyBox<T> {
        type Target = T;

        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }

    // 解引用强制转换示例
    let m = MyBox::new(String::from("hello"));

    // 自动转换: &MyBox<String> -> &String -> &str
    fn takes_str(s: &str) {
        println!("字符串: {}", s);
    }

    takes_str(&m);  // 自动调用 deref

    // 多级转换
    fn takes_string(s: &String) {
        println!("String: {}", s);
    }

    takes_string(&m);  // &MyBox<String> -> &String

    // 链式转换
    let m2 = MyBox::new(MyBox::new(5));
    println!("值: {}", ***m2);  // 手动解引用
}

2. 常见转换场景

fn common_coercions() {
    // String/&str 转换
    let s = String::from("hello");
    let rs: &str = &s;  // String 解引用为 &str

    // Vec/切片转换
    let v = vec![1, 2, 3];
    let slice: &[i32] = &v;  // Vec 解引用为切片

    // 智能指针转换
    use std::rc::Rc;

    let rc = Rc::new(String::from("hello"));
    let rc_ref: &String = &rc;  // Rc 解引用为 &String

    // 多级解引用强制转换
    let r = &&&5;
    let value: i32 = ***r;  // 手动
    let value_auto: i32 = **r;  // 自动(部分)
}

📦 七、引用与函数

1. 函数参数引用

fn function_references() {
    // 接收引用参数
    fn calculate_length(s: &String) -> usize {
        s.len()
    }

    fn modify_number(n: &mut i32) {
        *n *= 2;
    }

    // 使用
    let s = String::from("hello");
    let len = calculate_length(&s);
    println!("长度: {}", len);

    let mut x = 5;
    modify_number(&mut x);
    println!("x: {}", x);

    // 返回引用
    fn get_first_char(s: &str) -> Option<&str> {
        s.get(0..1)
    }

    let first = get_first_char("hello");
    println!("第一个字符: {:?}", first);

    // 返回可变引用
    struct Buffer {
        data: Vec<u8>,
    }

    impl Buffer {
        fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
            self.data.get_mut(index)
        }
    }
}

2. 生命周期标注

fn lifetime_annotations() {
    // 简单生命周期
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() { x } else { y }
    }

    let s1 = String::from("short");
    let s2 = "very long string";

    let result = longest(&s1, s2);
    println!("最长的: {}", result);

    // 结构体中的引用
    struct ImportantExcerpt<'a> {
        part: &'a str,
    }

    let novel = String::from("从前有座山...");
    let first_sentence = novel.split('。').next().unwrap();
    let i = ImportantExcerpt {
        part: first_sentence,
    };

    println!("摘要: {}", i.part);

    // 方法中的生命周期
    impl<'a> ImportantExcerpt<'a> {
        fn announce_and_return_part(&self, announcement: &str) -> &str {
            println!("注意: {}", announcement);
            self.part
        }
    }
}

🔄 八、引用与迭代器

1. 迭代器引用

fn iterator_references() {
    let v = vec![1, 2, 3, 4, 5];

    // 产生引用
    let iter = v.iter();  // 产生 &i32
    for num in iter {
        println!("{}", num);
    }

    // 产生可变引用
    let mut v2 = vec![1, 2, 3];
    let iter_mut = v2.iter_mut();  // 产生 &mut i32
    for num in iter_mut {
        *num *= 2;
    }
    println!("{:?}", v2);

    // 消耗所有权
    let v3 = vec![1, 2, 3];
    let into_iter = v3.into_iter();  // 产生 i32
    for num in into_iter {
        println!("{}", num);
    }
}

2. 引用与闭包

fn closure_references() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 闭包捕获引用
    let sum: i32 = numbers.iter()
        .map(|x| x * 2)     // x 是 &i32
        .sum();

    println!("两倍和: {}", sum);

    // 过滤中的引用模式
    let evens: Vec<&i32> = numbers.iter()
        .filter(|&&x| x % 2 == 0)  // &&x 匹配 &i32
        .collect();

    println!("偶数: {:?}", evens);

    // 修改元素
    let mut numbers2 = vec![1, 2, 3];
    numbers2.iter_mut()
        .for_each(|x| *x += 1);

    println!("增加后: {:?}", numbers2);
}

⚡ 九、裸指针

1. 原始指针类型

fn raw_pointers() {
    let mut x = 5;

    // 创建原始指针
    let r1 = &x as *const i32;     // 不可变原始指针
    let r2 = &mut x as *mut i32;   // 可变原始指针

    // 创建空指针
    let null_ptr: *const i32 = std::ptr::null();
    let null_mut_ptr: *mut i32 = std::ptr::null_mut();

    // 从地址创建指针
    let address = 0x1234usize;
    let ptr = address as *const i32;

    // 解引用原始指针(不安全)
    unsafe {
        println!("r1 指向的值: {}", *r1);
        *r2 = 10;
        println!("修改后: {}", *r2);
    }

    // 原始指针比较
    println!("相等: {}", r1 == r2);
    println!("地址差: {:?}", r1.wrapping_offset_from(r2));
}

2. 原始指针操作

fn raw_pointer_operations() {
    let array = [1, 2, 3, 4, 5];
    let ptr = array.as_ptr();

    unsafe {
        // 指针运算
        let second = ptr.add(1);
        println!("第二个元素: {}", *second);

        // 读取多个字节
        let bytes = std::slice::from_raw_parts(ptr as *const u8, 4);
        println!("前4字节: {:?}", bytes);

        // 复制内存
        let mut dest = [0i32; 5];
        std::ptr::copy_nonoverlapping(ptr, dest.as_mut_ptr(), 5);
        println!("复制后: {:?}", dest);

        // 写入内存
        let mut value = 42;
        let value_ptr = &mut value as *mut i32;
        value_ptr.write(100);
        println!("写入后: {}", value);

        // 交换值
        let mut a = 1;
        let mut b = 2;
        std::ptr::swap(&mut a, &mut b);
        println!("交换后: a={}, b={}", a, b);
    }
}

🔧 十、引用与模式匹配

1. ref 关键字

fn ref_keyword() {
    // 在 let 绑定中使用 ref
    let x = 5;
    let ref y = x;  // 等价于 let y = &x;
    println!("y: {}", *y);

    // 在结构体模式中使用
    struct Point {
        x: i32,
        y: i32,
    }

    let point = Point { x: 10, y: 20 };
    let Point { x: ref a, y: ref b } = point;
    println!("a: {}, b: {}", a, b);

    // 在元组模式中使用
    let tuple = (1, 2, 3);
    let (ref a, ref b, ref c) = tuple;
    println!("元组: {}, {}, {}", a, b, c);

    // 数组模式
    let array = [1, 2, 3, 4, 5];
    match array {
        [ref first, .., ref last] => {
            println!("第一个: {}, 最后一个: {}", first, last);
        }
        _ => {}
    }
}

2. ref mut 关键字

fn ref_mut_keyword() {
    // 创建可变绑定
    let mut x = 5;
    let ref mut y = x;  // 可变引用
    *y += 1;
    println!("x: {}", x);

    // 在结构体中使用
    #[derive(Debug)]
    struct Container {
        value: i32,
    }

    let mut container = Container { value: 0 };
    let Container { value: ref mut val } = container;
    *val = 42;
    println!("容器: {:?}", container);

    // 在枚举中使用
    let mut option = Some(String::from("hello"));

    match option {
        Some(ref mut s) => {
            s.push_str(" world");
            println!("修改: {}", s);
        }
        None => {}
    }

    println!("最终: {:?}", option);
}

📊 十一、引用性能优化

1. 减少引用拷贝

fn reduce_copy_references() {
    use std::time::Instant;

    // 大结构体
    #[derive(Clone)]
    struct LargeData {
        data: [u8; 1024],  // 1KB
    }

    let large = LargeData { data: [0; 1024] };

    // ❌ 不好的做法:传递值(复制)
    fn process_value(data: LargeData) {
        // 复制 1KB 数据
    }

    // ✅ 好的做法:传递引用
    fn process_ref(data: &LargeData) {
        // 只传递指针
    }

    // 性能测试
    let start = Instant::now();
    for _ in 0..10000 {
        process_value(large.clone());
    }
    println!("传值耗时: {:?}", start.elapsed());

    let start = Instant::now();
    for _ in 0..10000 {
        process_ref(&large);
    }
    println!("传引用耗时: {:?}", start.elapsed());
}

2. 引用 vs 智能指针

fn ref_vs_smart_pointer() {
    use std::rc::Rc;
    use std::sync::Arc;

    // 小数据:使用引用
    let x = 5;
    let r = &x;

    // 需要共享所有权:使用 Rc/Arc
    let rc = Rc::new(5);
    let rc_ref = Rc::clone(&rc);

    // 需要线程安全:使用 Arc
    let arc = Arc::new(5);
    let arc_ref = Arc::clone(&arc);

    // 需要可变性:根据情况选择
    let mut y = 5;
    let y_ref = &mut y;

    let rc_mut = Rc::new(std::cell::RefCell::new(5));
    let rc_mut_ref = rc_mut.borrow_mut();

    // 选择指南:
    // 1. 如果可以,优先使用引用
    // 2. 需要多个所有者时用 Rc/Arc
    // 3. 需要线程安全时用 Arc
    // 4. 需要可变性时用 &mut 或 RefCell/Mutex
}

🎯 十二、常见错误和解决方案

1. 悬垂引用

fn dangling_reference_example() {
    // ❌ 悬垂引用
    // fn dangle() -> &String {
    //     let s = String::from("hello");
    //     &s
    // }  // s 离开作用域,&s 成为悬垂引用

    // ✅ 解决方案1: 返回所有权
    fn no_dangle() -> String {
        let s = String::from("hello");
        s
    }

    // ✅ 解决方案2: 使用静态生命周期
    fn static_str() -> &'static str {
        "hello"
    }

    // ✅ 解决方案3: 接受引用参数
    fn process_and_return<'a>(s: &'a str) -> &'a str {
        s
    }
}

2. 借用检查器错误

fn borrow_checker_errors() {
    // 错误1: 同时拥有可变和不可变引用
    let mut data = vec![1, 2, 3];

    // let r1 = &data;        // 不可变引用
    // let r2 = &mut data;    // ❌ 编译错误
    // r1.push(4);            // 通过不可变引用修改

    // 解决方案: 限制作用域
    {
        let r1 = &data;
        println!("{:?}", r1);
    }  // r1 离开作用域

    let r2 = &mut data;  // ✅ 现在可以获取可变引用
    r2.push(4);

    // 错误2: 迭代时修改集合
    let mut v = vec![1, 2, 3];

    // for i in &v {
    //     v.push(i * 2);  // ❌ 编译错误
    // }

    // 解决方案: 收集到新向量
    let mut new_v = Vec::new();
    for i in &v {
        new_v.push(i * 2);
    }
    v.extend(new_v);
}

📦 十三、引用与 trait

1. 引用实现 trait

fn references_implement_traits() {
    use std::fmt;

    // 引用实现了 Debug
    let x = 5;
    let r = &x;
    println!("引用: {:?}", r);

    // 引用实现了 Display
    println!("值: {}", r);

    // 引用实现了 PartialEq
    let a = 5;
    let b = 5;
    let ra = &a;
    let rb = &b;

    println!("相等: {}", ra == rb);

    // 自定义类型的引用
    #[derive(Debug)]
    struct Point(i32, i32);

    let p = Point(1, 2);
    let rp = &p;

    println!("点: {:?}", rp);
    println!("坐标: {}, {}", rp.0, rp.1);
}

2. AsRefAsMut

fn asref_asmut_examples() {
    use std::path::Path;

    // AsRef 允许类型转换
    let s = String::from("hello");
    let rs: &str = s.as_ref();

    let path = Path::new("/tmp/file.txt");
    let rp: &Path = path.as_ref();

    // 使用泛型函数
    fn print_as_str<T: AsRef<str>>(s: T) {
        let rs: &str = s.as_ref();
        println!("{}", rs);
    }

    print_as_str("hello");      // &str
    print_as_str(String::from("world"));  // String
    print_as_str(&String::from("rust"));  // &String

    // AsMut 用于可变引用
    fn add_world<T: AsMut<String>>(mut s: T) {
        s.as_mut().push_str(" world");
    }

    let mut string = String::from("hello");
    add_world(&mut string);
    println!("结果: {}", string);
}

🔧 十四、实用技巧

1. 避免多层引用

fn avoid_nested_references() {
    // ❌ 不必要的多层引用
    let x = 5;
    let r1 = &x;
    let r2 = &r1;    // &&i32
    let r3 = &r2;    // &&&i32

    // ✅ 直接使用引用
    let value = ***r3;  // 需要多次解引用

    // 在函数参数中避免
    fn process(value: &&&i32) -> i32 {
        ***value
    }

    fn better_process(value: &i32) -> i32 {
        *value
    }

    // 在集合中避免
    let numbers = vec![1, 2, 3];
    let refs: Vec<&i32> = numbers.iter().collect();  // Vec<&i32>

    // 而不是 Vec<&&i32>
    let bad_refs: Vec<&&i32> = numbers.iter().map(|x| &x).collect();
}

2. 引用与字符串切片

fn string_slice_references() {
    // String 和 &str
    let s = String::from("hello world");

    // 字符串切片
    let slice: &str = &s[0..5];
    println!("切片: {}", slice);

    // 字符串字面量就是 &str
    let literal: &str = "hello";

    // 函数接受 &str
    fn print_str(s: &str) {
        println!("{}", s);
    }

    print_str(&s);      // String -> &str
    print_str("rust");  // 字符串字面量
    print_str(slice);   // 字符串切片

    // 避免不必要的 String 创建
    fn get_first_word(s: &str) -> &str {
        s.split_whitespace().next().unwrap_or("")
    }

    let first = get_first_word(&s);
    println!("第一个词: {}", first);
}

🎯 十五、总结要点

1. 引用核心概念

  • &T: 不可变引用,允许多个
  • &mut T: 可变引用,只能有一个
  • *: 解引用运算符
  • 引用规则: 编译时检查保证安全

2. 重要特性

  1. 自动解引用: 在需要时自动解引用
  2. 解引用强制转换: 自动转换引用类型
  3. 生命周期: 确保引用有效性
  4. 借用检查器: 编译时防止数据竞争

3. 最佳实践

  • 优先使用引用而非智能指针
  • 使用 &str 而非 &String 作为参数
  • 避免不必要的多层引用
  • 合理使用 refref mut 模式
  • 注意函数返回引用的生命周期

4. 性能考虑

  • 引用是零成本抽象
  • 大结构体应传递引用
  • 小类型可考虑传递值
  • 避免不必要的引用计数

理解 Rust 的引用系统是掌握 Rust 的关键。引用提供了安全、高效的内存访问方式,是 Rust 内存安全模型的基石。正确使用引用可以编写出既安全又高效的代码。