派生PartialEq时,含boxed trait对象的枚举触发E0507编译错误
咱们先来看你遇到的问题:你想给包含Trait对象的枚举派生PartialEq,但编译时出现了error[E0507]: cannot move out of borrowed content错误。先把你的代码贴出来方便分析:
trait Trait { fn partial_eq(&self, rhs: &Box<Trait>) -> bool; } impl PartialEq for Box<Trait> { fn eq(&self, rhs: &Box<Trait>) -> bool { self.partial_eq(rhs) } } #[derive(PartialEq)] enum Enum { Trait(Box<Trait>), }
错误原因分析
这个错误的根源出在impl PartialEq for Box<Trait>的代码里:当你调用self.partial_eq(rhs)时,self是&Box<Trait>类型,而你的Trait中partial_eq方法的第一个参数是&dyn Trait(也就是trait对象的引用)。编译器尝试自动解引用Box<Trait>来匹配参数类型,但这个过程中错误地试图从被借用的Box<Trait>中移动内容——而你只有Box<Trait>的引用,自然不允许移动,于是就触发了E0507错误。
另外,从设计角度来说,让trait方法接收&Box<Trait>也不太合理,因为我们真正要比较的是trait对象本身,而不是包裹它的Box智能指针。
修正方案
方案一:优化Trait方法签名(推荐)
调整Trait的partial_eq方法,让它接收&dyn Trait作为参数,然后在Box<Trait>的PartialEq实现中正确解引用:
trait Trait { // 改为接收trait对象的引用,更符合设计逻辑 fn partial_eq(&self, rhs: &dyn Trait) -> bool; } // 为Box<dyn Trait>实现PartialEq impl PartialEq for Box<dyn Trait> { fn eq(&self, rhs: &Self) -> bool { // 明确解引用Box,得到trait对象的引用后调用方法 (**self).partial_eq(&**rhs) } } // 现在可以正常派生PartialEq了 #[derive(PartialEq)] enum Enum { Trait(Box<dyn Trait>), }
方案二:保留原Trait方法签名的修正
如果你坚持要让trait方法接收&Box<Trait>,只需要在调用时明确获取trait对象的引用即可:
trait Trait { fn partial_eq(&self, rhs: &Box<Trait>) -> bool; } impl PartialEq for Box<Trait> { fn eq(&self, rhs: &Box<Trait>) -> bool { // 解引用Box得到trait对象的引用,再调用方法 (**self).partial_eq(rhs) } } #[derive(PartialEq)] enum Enum { Trait(Box<Trait>), }
额外说明
这样修改后,#[derive(PartialEq)]生成的枚举比较代码会自动调用Box<Trait>的eq方法,进而触发你在Trait中定义的自定义相等逻辑。同时,因为Trait没有为partial_eq提供默认实现,所有实现Trait的类型都必须手动编写相等判断逻辑,满足了你强制实现者编写相等方法的需求。
内容的提问来源于stack exchange,提问作者Zack

