实现From<PllState<PllDev>> for PllDev时遇E0210及E0119错误求助
问题场景
我正在使用rp2040_hal crate,该crate提供了PhaseLockedLoopDevice<State, Device> trait。为简化泛型参数使用,定义了如下枚举:
enum PllState<PllDev: PhaseLockedLoopDevice> { Disabled(PllDev), Locked(PhaseLockedLoop<pll::Locked, PllDev>), }
尝试实现From<> trait以通过PllState::into()获取内部PllDev时,出现编译错误:
impl<PllDev: PhaseLockedLoopDevice> From<PllState<PllDev>> for PllDev { fn from(value: PllState<PllDev>) -> Self { match value{ PllState::Locked(p) => p.free(), PllState::Disabled(p) => p } } }
错误信息:
error[E0210]: type parameter `PllDev` must be covered by another type when it appears before the first local type (`PllState<PllDev>`) --> src\kernel\clocks.rs:590:6 | 590 | impl<PllDev: PhaseLockedLoopDevice> From<PllState<PllDev>> for PllDev { | ^^^^^^ type parameter `PllDev` must be covered by another type when it appears before the first local type (`PllState<PllDev>`) | = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
移除From<>实现后尝试实现Into<>,又出现冲突错误:
impl<PllDev: PhaseLockedLoopDevice> Into<PllDev> for PllState<PllDev> { fn into(self) -> PllDev { match self{ PllState::Locked(p) => p.free(), PllState::Disabled(p) => p } } }
错误信息:
error[E0119]: conflicting implementations of trait `Into<_>` for type `PllState<_>` --> src\kernel\clocks.rs:598:1 | 598 | impl<PllDev: PhaseLockedLoopDevice> Into<PllDev> for PllState<PllDev> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `core`: - impl<T, U> Into<U> for T where U: From<T>;
解决方法
针对E0210错误
Rust的孤儿规则规定:实现外部trait时,至少有一个关联类型是本地定义的,且第一个本地类型之前不能存在未被覆盖的类型参数。这里From是标准库的外部trait,实现形式是From<本地类型> for 外部类型,此时外部类型PllDev作为for后的目标类型,出现在第一个本地类型PllState<PllDev>之前,且PllDev是未被覆盖的泛型参数——它没有被任何本地类型包裹,Rust无法保证这个实现不会和其他 crate 的实现产生冲突。
最简洁的解决方式是为PllState实现一个自定义方法,直接返回内部的PllDev:
impl<PllDev: PhaseLockedLoopDevice> PllState<PllDev> { pub fn into_inner(self) -> PllDev { match self { PllState::Locked(p) => p.free(), PllState::Disabled(p) => p, } } }
使用时直接调用pll_state.into_inner()即可获取PllDev,完全符合Rust的规则。
针对E0119错误
标准库的Into trait有一个全局 blanket 实现:impl<T, U> Into<U> for T where U: From<T>。即使你没有实现From,Rust也会认为存在潜在的Into实现可能,手动实现Into<PllDev> for PllState<PllDev>会和这个全局实现冲突——Rust无法判断两个实现的优先级。
同样,使用上述自定义的into_inner方法是最稳妥的方案,避免和标准库的全局实现冲突。
如果一定要使用Into/From的形式,可以把PllDev包装到一个本地类型中,但这种方式会增加复杂度,不如自定义方法简洁:
// 定义本地包装类型 struct PllDevWrapper<PllDev: PhaseLockedLoopDevice>(PllDev); // 为包装类型实现From<PllState> impl<PllDev: PhaseLockedLoopDevice> From<PllState<PllDev>> for PllDevWrapper<PllDev> { fn from(value: PllState<PllDev>) -> Self { PllDevWrapper(match value { PllState::Locked(p) => p.free(), PllState::Disabled(p) => p, }) } } // 使用时转换后解包 let pll_dev = PllDevWrapper::from(pll_state).0;
内容的提问来源于stack exchange,提问作者Anon

