为何此场景下Rust的trait解析会失败?
Rust Trait解析歧义问题:为何&mut T实现无法被选中?
我原本以为Rust的孤儿规则等机制能保证trait解析无歧义,但下面的场景编译失败——编译器会优先选中&'a T的实现,删掉这个实现后,&'a mut T的实现才能正常被选中工作。
问题代码
use core::ops::Deref; trait RefTaker<T> where Self: Deref<Target=T> + Sized { fn take_ref(self) -> Self { self } } impl<'a, T> RefTaker<T> for &'a T { } impl<'a, T> RefTaker<T> for &'a mut T { } fn main() { let mut x = 3u8; let _: &mut u8 = x.take_ref(); }
问题原因
Rust的方法解析流程分为候选收集、优先级排序、类型检查三个阶段,这里的问题出在前两个阶段:
- 候选收集:调用
x.take_ref()时,x是u8类型,本身没实现RefTaker,编译器会生成两个候选:将x转为&u8后调用、转为&mut u8后调用; - 优先级排序:自动共享引用(
&x)和自动可变引用(&mut x)的候选优先级相同,但编译器会优先尝试共享引用——因为共享引用是更“安全”的转换,不会破坏变量的可变性; - 类型检查:选中
&u8的实现后,返回值是&u8,无法匹配目标类型&mut u8,直接报错,而不会回溯到候选列表重新选择&mut u8的实现。
简单来说,方法解析阶段不考虑返回值类型,只看方法是否可调用,共享引用的转换被优先尝试,导致选中了不符合返回值要求的实现。
无需硬指定的解决办法
方法1:为原类型直接实现RefTaker
调整trait的实现范围,让T本身实现RefTaker并返回可变引用,跳过自动引用转换的歧义:
use core::ops::Deref; trait RefTaker<T> where Self: Sized { fn take_ref(self) -> &mut T; } // 为原类型实现,直接返回可变引用 impl<T> RefTaker<T> for T { fn take_ref(mut self) -> &mut T { &mut self } } // 保留可变引用类型的实现 impl<'a, T> RefTaker<T> for &'a mut T { fn take_ref(self) -> &mut T { self } } // 若无需共享引用转可变引用,可删除此实现(共享转可变需unsafe) impl<'a, T> RefTaker<T> for &'a T { fn take_ref(self) -> &mut T { unsafe { &mut *(self as *const T as *mut T) } } } fn main() { let mut x = 3u8; let _: &mut u8 = x.take_ref(); }
方法2:利用AsMut引导可变引用转换
借助AsMut trait显式获取可变引用,避免自动转换的歧义:
use core::ops::Deref; use core::convert::AsMut; trait RefTaker<T> where Self: Deref<Target=T> + Sized { fn take_ref(self) -> Self { self } } impl<'a, T> RefTaker<T> for &'a T { } impl<'a, T> RefTaker<T> for &'a mut T { } fn main() { let mut x = 3u8; let _: &mut u8 = x.as_mut().take_ref(); }
x.as_mut()会返回&mut u8,此时调用take_ref()会直接选中&mut T的实现,无需硬指定类型。
方法3:调整trait为关联类型,让返回值引导推断
将trait的泛型参数改为关联类型,让编译器根据返回值类型自动匹配正确的实现:
use core::ops::Deref; trait RefTaker: Sized { type Target; type Output; fn take_ref(self) -> Self::Output where Self: Deref<Target=Self::Target>; } impl<'a, T> RefTaker for &'a T { type Target = T; type Output = &'a T; fn take_ref(self) -> Self::Output { self } } impl<'a, T> RefTaker for &'a mut T { type Target = T; type Output = &'a mut T; fn take_ref(self) -> Self::Output { self } } impl<T> RefTaker for T { type Target = T; type Output = &mut T; fn take_ref(mut self) -> Self::Output { &mut self } } fn main() { let mut x = 3u8; let _: &mut u8 = x.take_ref(); }
编译器会根据返回值&mut u8自动推断出应该选择T的实现,返回正确的可变引用。
内容的提问来源于stack exchange,提问作者ajp
相关产品推荐
相关产品推荐

