Deref 与 Drop trait
学习目标
- 理解
Dereftrait 和强制解引用 - 理解
Droptrait 和自动清理 - 掌握智能指针的自定义实现
核心概念
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 强制转换让智能指针用起来像普通引用。