29·智能指针进阶

Deref 与 Drop trait

Deref 与 Drop trait

学习目标

  1. 理解 Deref trait 和强制解引用
  2. 理解 Drop trait 和自动清理
  3. 掌握智能指针的自定义实现

核心概念

Deref trait

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) -> &T {
        &self.0
    }
}

fn hello(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);  // 通过 Deref 解引用

    // Deref 强制转换
    let name = MyBox::new(String::from("Rust"));
    hello(&name);  // MyBox<String> → &String → &str
}

Deref 强制转换规则

// 规则:
// &T → &U  当 T: Deref<Target=U>
// &mut T → &mut U  当 T: DerefMut<Target=U>
// &mut T → &U  当 T: Deref<Target=U>(可变→不可变总是允许)

fn print_str(s: &str) {
    println!("{}", s);
}

fn main() {
    let s = String::from("hello");
    print_str(&s);         // String → &str
    print_str(&&s);        // &String → &str
    print_str(&s[..]);     // 切片
}

Drop trait

struct DatabaseConnection {
    url: String,
}

impl DatabaseConnection {
    fn new(url: &str) -> Self {
        println!("连接到 {}", url);
        DatabaseConnection { url: String::from(url) }
    }
}

impl Drop for DatabaseConnection {
    fn drop(&mut self) {
        println!("断开连接: {}", self.url);
    }
}

fn main() {
    let conn1 = DatabaseConnection::new("postgres://localhost/db1");
    let conn2 = DatabaseConnection::new("postgres://localhost/db2");

    println!("使用连接...");

    // 离开作用域时自动调用 drop(后创建的先 drop)
    // 手动提前释放:
    drop(conn1);
    println!("conn1 已释放");

    // conn2 在这里自动释放
}

Drop 顺序

struct Named {
    name: String,
}

impl Drop for Named {
    fn drop(&mut self) {
        println!("dropping: {}", self.name);
    }
}

fn main() {
    let a = Named { name: String::from("first") };
    let b = Named { name: String::from("second") };
    let c = Named { name: String::from("third") }
    ;
    // drop 顺序: third, second, first(LIFO)
}

自定义智能指针

use std::ops::{Deref, DerefMut};
use std::fmt;

struct SmartPointer<T> {
    data: Box<T>,
    name: String,
}

impl<T> SmartPointer<T> {
    fn new(data: T, name: &str) -> Self {
        println!("[{}] 创建", name);
        SmartPointer {
            data: Box::new(data),
            name: String::from(name),
        }
    }
}

impl<T> Deref for SmartPointer<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.data
    }
}

impl<T> DerefMut for SmartPointer<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.data
    }
}

impl<T: fmt::Debug> Drop for SmartPointer<T> {
    fn drop(&mut self) {
        println!("[{}] 释放: {:?}", self.name, self.data);
    }
}

fn main() {
    let mut sp = SmartPointer::new(vec![1, 2, 3], "my_ptr");
    sp.push(4);  // DerefMut 允许直接调用 Vec 方法
    println!("{:?}", *sp);
}

实践练习

练习 1:带日志的指针

use std::ops::Deref;

struct LoggingRef<T> {
    value: T,
    access_count: std::cell::Cell<u32>,
}

impl<T> LoggingRef<T> {
    fn new(value: T) -> Self {
        LoggingRef { value, access_count: std::cell::Cell::new(0) }
    }

    fn access_count(&self) -> u32 {
        self.access_count.get()
    }
}

impl<T> Deref for LoggingRef<T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.access_count.set(self.access_count.get() + 1);
        &self.value
    }
}

fn main() {
    let data = LoggingRef::new(vec![1, 2, 3]);
    println!("len: {}", data.len());
    println!("first: {:?}", data.first());
    println!("访问次数: {}", data.access_count());
}

练习 2:资源管理器

struct Resource {
    name: String,
}

impl Resource {
    fn acquire(name: &str) -> Self {
        println!("获取资源: {}", name);
        Resource { name: String::from(name) }
    }

    fn use_resource(&self) {
        println!("使用资源: {}", self.name);
    }
}

impl Drop for Resource {
    fn drop(&mut self) {
        println!("释放资源: {}", self.name);
    }
}

fn main() {
    {
        let r1 = Resource::acquire("文件句柄");
        let r2 = Resource::acquire("网络连接");
        r1.use_resource();
        r2.use_resource();
        println!("作用域结束...");
    }
    println!("资源已全部释放");
}

常见错误

1. Deref 递归

// ❌ 无限递归
// impl Deref for MyType {
//     type Target = MyType;
//     fn deref(&self) -> &MyType { self }
// }

2. Drop 中使用已移动的值

struct Foo {
    data: Vec<i32>,
}

impl Drop for Foo {
    fn drop(&mut self) {
        // self.data 已经被 drop 了
        // 不能再 move 出去
    }
}

小结

Trait用途
Deref自定义解引用,支持 * 运算符
DerefMut可变解引用
Drop离开作用域时自动清理
drop(ptr)手动提前释放

Deref 强制转换让智能指针用起来像普通引用。

练习编辑器

rust
Loading...