如何定义允许任意次数Deref调用的Rust Deref trait约束?
问题:如何让函数接受可多次Deref到&MyTrait的类型
我持有一个Rc<Box<dyn MyTrait>>类型的变量,想编写一个函数,它接受能够通过任意次数Deref::deref调用最终转换为&MyTrait的类型。以下是我的尝试代码及遇到的错误,请问Rust中能否实现这个需求?
尝试代码
use std::rc::Rc; use std::ops::Deref; trait MyTrait { } struct Foo { } impl MyTrait for Foo { } fn bar<T : Deref<Target = dyn MyTrait>>(obj: &T) { } fn main() { let foo = Rc::new(Box::new(Foo {}) as Box<dyn MyTrait>); bar(&foo) }
编译错误
Compiling playground v0.0.1 (/playground) error[E0271]: type mismatch resolving `<Rc<Box<dyn MyTrait>> as Deref>::Target == dyn MyTrait` --> src/main.rs:21:9 | 21 | bar(&foo) | --- ^^^^ 预期类型为`dyn MyTrait`,实际找到`Box<dyn MyTrait>` | | | 此调用引入的约束要求 | = note: 预期 trait 对象 `(dyn MyTrait + 'static)` 找到结构体 `Box<dyn MyTrait>` note: 由`bar`中的约束要求 --> src/main.rs:15:18 | 15 | fn bar<T : Deref<Target = dyn MyTrait>>(obj: &T) { | ^^^^^^^^^^^^^^^^^^^^ `bar`中的此约束要求 For more information about this error, try `rustc --explain E0271`. error: could not compile `playground` (bin "playground") due to 1 previous error
解决方案
可以实现,当前代码的问题在于bar的约束过于严格——要求T直接解引用为dyn MyTrait,但Rc<Box<dyn MyTrait>>的Deref目标是Box<dyn MyTrait>,而非直接的dyn MyTrait。下面提供两种可行方案:
方案一:递归Deref约束
通过定义辅助trait实现递归匹配,支持任意层数的Deref转换:
use std::rc::Rc; use std::ops::Deref; trait MyTrait {} struct Foo {} impl MyTrait for Foo {} // 辅助trait,定义可转换为&dyn MyTrait的能力 trait DerefToMyTrait { fn deref_to_trait(&self) -> &dyn MyTrait; } // 直接匹配dyn MyTrait类型 impl DerefToMyTrait for dyn MyTrait { fn deref_to_trait(&self) -> &dyn MyTrait { self } } // 递归匹配:只要T的Deref目标实现了DerefToMyTrait,T就自动实现该trait impl<T> DerefToMyTrait for T where T: Deref, <T as Deref>::Target: DerefToMyTrait, { fn deref_to_trait(&self) -> &dyn MyTrait { self.deref().deref_to_trait() } } // 修改bar函数的约束为DerefToMyTrait fn bar<T: DerefToMyTrait>(obj: &T) { let trait_ref = obj.deref_to_trait(); // 这里可以使用trait_ref调用MyTrait的方法 } fn main() { let foo = Rc::new(Box::new(Foo {}) as Box<dyn MyTrait>); bar(&foo); // 编译通过 }
方案二:使用AsRef trait(更简洁)
Rc、Box等智能指针都实现了AsRef,可以直接利用这一点约束参数能转换为&dyn MyTrait:
use std::rc::Rc; trait MyTrait {} struct Foo {} impl MyTrait for Foo {} // 约束T可以AsRef<dyn MyTrait> fn bar<T: AsRef<dyn MyTrait>>(obj: &T) { let trait_ref = obj.as_ref(); // 使用trait_ref } fn main() { let foo = Rc::new(Box::new(Foo {}) as Box<dyn MyTrait>); bar(&foo); // 编译通过 }
错误原因说明
你的代码中T: Deref<Target = dyn MyTrait>要求T必须直接解引用得到dyn MyTrait,但Rc<Box<dyn MyTrait>>解引用后是Box<dyn MyTrait>,还需要再解引用一次才能得到dyn MyTrait,因此不符合约束。上面的两种方案通过递归匹配或AsRef的链式转换,解决了多层Deref的问题。
内容的提问来源于stack exchange,提问作者Shmoopy
相关产品推荐
相关产品推荐

