Trait DerefMut
DerefMut trait 来自 std::ops 模块,它的主要目的是实现可变解引用操作,如 *mut 操作符在可变上下文中的使用。它允许自定义类型像指针一样工作,支持“可变解引用强制转换”(mutable deref coercion),让编译器自动插入 deref_mut 调用,使类型更灵活。与 Deref 不同,DerefMut 专注于可变引用,常用于智能指针如 Box、Rc、Arc 和 RefCell 的可变访问。
1. DerefMut Trait 简介
1.1 定义和目的
DerefMut trait 定义在 std::ops::DerefMut 中,自 Rust 1.0.0 起稳定可用。它继承 Deref,其核心是定义可变解引用的方法:
#![allow(unused)] fn main() { pub trait DerefMut: Deref { fn deref_mut(&mut self) -> &mut Self::Target; } }
- 继承:
DerefMut要求实现Deref,所以可变解引用隐含不可变解引用。 - 方法:
deref_mut(&mut self) -> &mut Self::Target- 返回目标类型的独占引用。
目的:DerefMut 使自定义类型支持可变 * 操作符,并启用 mutable deref coercion:编译器自动将 &mut T 转换为 &mut U(如果 T: DerefMut<Target=U>),允许类型“继承”目标类型的可变方法。这在智能指针中特别有用,例如 Box<T> 可变解引用到 T,让用户像修改 T 一样操作 Box<T>。 它促进抽象,提供零成本可变指针语义,而不牺牲安全。
根据官方文档,DerefMut 应仅用于廉价、透明的可变解引用操作,且不应失败。它不提供默认方法,仅要求 deref_mut。
- 为什么需要
DerefMut? Rust 的借用系统需要显式可变借用。DerefMut简化智能指针的可变使用,支持方法解析(如在Box<Vec<i32>>上调用Vec的可变方法),并避免 boilerplate 代码。 例如,在库设计中,实现DerefMut让包装类型可变透明。
1.2 与相关 Trait 的区别
DerefMut 与几个引用 trait 相关,但侧重可变解引用:
-
与
Deref:DerefMut用于可变解引用(&mut Target);Deref用于不可变解引用(&Target)。DerefMut继承Deref,所以实现DerefMut必须也实现Deref。- 选择:如果需要修改,用
DerefMut;否则Deref足够。
-
与
AsMut:DerefMut支持*mut和 mutable coercion;AsMut是显式转换 trait,无 coercion。DerefMut隐式调用;AsMut需手动as_mut()。- 许多类型同时实现两者,但
DerefMut更强大(可变方法继承)。 - 选择:对于智能指针,用
DerefMut;对于通用转换,用AsMut。
-
与
BorrowMut:DerefMut不要求哈希等价;BorrowMut要求借用与原值哈希/比较等价,用于集合键。BorrowMut更严格;DerefMut更通用,用于解引用。- 示例:
Vec<T>实现BorrowMut<[T]>以用于键借用;Box<T>用DerefMut<T>。
何时选择? 实现 DerefMut 如果类型是智能指针或透明包装,支持可变访问;否则用 AsMut 或 BorrowMut 以避免意外 coercion。 最佳实践:仅在解引用廉价且透明时实现。
2. 手动实现 DerefMut
DerefMut 不能自动派生,必须手动实现。但实现简单:定义 deref_mut,并实现 Deref。
2.1 基本示例:自定义智能指针
从官方示例:
use std::ops::{Deref, DerefMut}; 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 } } impl<T> DerefMut for MyBox<T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } fn main() { let mut x = MyBox::new(5); *x = 10; // 使用 *mut assert_eq!(10, *x); }
- 这里,
MyBox支持可变解引用。
2.2 Mutable Deref Coercion 示例
fn modify(s: &mut str) { s.make_ascii_uppercase(); } fn main() { let mut m = MyBox::new(String::from("hello")); modify(&mut m); // Coercion: &mut MyBox<String> -> &mut String -> &mut str assert_eq!(*m, "HELLO"); }
- 编译器自动插入
deref_mut调用。
2.3 新类型(Newtype)实现
struct NonNegative(i32); impl Deref for NonNegative { type Target = i32; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for NonNegative { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } fn main() { let mut num = NonNegative(42); *num = 100; // 可变访问 println!("{}", *num); // 100 }
- 但小心:对于新类型,实现
DerefMut可能导致混淆,因为它允许绕过类型检查。
2.4 泛型类型
struct Wrapper<T>(T); impl<T> Deref for Wrapper<T> { type Target = T; fn deref(&self) -> &T { &self.0 } } impl<T> DerefMut for Wrapper<T> { fn deref_mut(&mut self) -> &mut T { &mut self.0 } } fn main() { let mut w = Wrapper(vec![1, 2, 3]); w.push(4); // 通过 coercion 调用 Vec::push assert_eq!(w.len(), 4); }
- 泛型 impl,支持任何
T。
3. Mutable Deref Coercion 详解
Mutable deref coercion 是 DerefMut 的关键特性:编译器在类型不匹配时自动应用 deref_mut。
- 规则:如果
T: DerefMut<Target=U>,则&mut T可强制为&mut U。 支持多级:&mut MyBox<MyBox<T>>->&mut T。 - 应用:函数参数、可变方法调用、字段修改。
- 限制:仅在可变引用上下文中;不影响所有权。
示例:多级 coercion。
#![allow(unused)] fn main() { let mut inner = String::from("hello"); let mut outer = MyBox::new(MyBox::new(inner)); modify(&mut outer); // &mut MyBox<MyBox<String>> -> &mut MyBox<String> -> &mut String -> &mut str assert_eq!(*outer, "HELLO"); }
- 自动可变解包。
4. 高级主题
4.1 Deref Polymorphism(反模式)
使用 DerefMut 来模拟继承或多态,常被视为反模式。 示例:
#![allow(unused)] fn main() { struct Child { parent: Parent, } impl Deref for Child { type Target = Parent; fn deref(&self) -> &Parent { &self.parent } } impl DerefMut for Child { fn deref_mut(&mut self) -> &mut Parent { &mut self.parent } } }
- 这允许
Child“继承”Parent的可变方法,但可能导致方法冲突或意外行为。 替代:使用组合和显式委托。
4.2 对于 Trait 对象
#![allow(unused)] fn main() { trait Drawable {} struct MyDrawable; impl Drawable for MyDrawable {} struct Pointer(Box<dyn Drawable>); impl Deref for Pointer { type Target = dyn Drawable; fn deref(&self) -> &Self::Target { &*self.0 } } impl DerefMut for Pointer { fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.0 } } }
- 支持动态可变分发。
4.3 与 RefCell 结合
DerefMut 常用于内部可变性:
#![allow(unused)] fn main() { use std::cell::RefCell; use std::ops::{Deref, DerefMut}; let cell = RefCell::new(vec![1, 2, 3]); let mut guard = cell.borrow_mut(); // RefMut<Vec<i32>> guard.push(4); // 通过 DerefMut 修改 assert_eq!(*cell.borrow(), vec![1, 2, 3, 4]); }
RefMut实现DerefMut支持运行时借用。
5. 常见用例
- 智能指针:
Box、Rc等可变访问。 - 包装类型:如
RefCell、Mutex的守卫。 - 可变方法继承:让包装类型使用内部可变方法。
- API 设计:透明可变包装外部类型。
- 性能优化:零成本可变抽象。
6. 最佳实践
- 配对 Deref:始终实现
Deref以匹配。 - 仅透明时实现:可变解引用应像访问内部一样。
- 避免失败:
deref_mut不应 panic 或失败。 - 文档:说明 mutable coercion 行为。
- 测试:验证
*mut和可变方法调用。 - 避免新类型 DerefMut:使用显式方法以防混淆。
7. 常见陷阱和错误
- 方法冲突:包装类型方法覆盖内部可变方法。
- 意外 Coercion:导致类型推断问题。
- 借用规则违反:多级可变解引用可能导致别名问题。
- Deref Polymorphism:模拟继承导致维护难题。
- Unsized 类型:需小心
?Sized约束。