为何Vin类型无需指定Into实现,Vcu类型却需显式注解?
Rust中Vin与Vcu结构体Into实现行为差异的原因分析
问题场景
我定义了以下两个Rust结构体Vin和Vcu,并为它们分别实现了转换为对应长度u8数组的Into trait:
#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Vin { data: [u8; 17], } impl Vin { pub fn new(data: [u8; 17]) -> Vin { Vin { data } } } impl Into<[u8;17]> for Vin { fn into(self) -> [u8;17] { self.data } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Vcu { data: [u8; 11], } impl Vcu { pub fn new(data: [u8; 11]) -> Vcu { Vcu { data } } pub fn as_string_utf8(&self) -> String { std::str::from_utf8(&self.data).unwrap().to_owned() } } impl Into<[u8;11]> for Vcu { fn into(self) -> [u8;11] { self.data } }
在尝试使用Into实现转换时,Vin类型的代码可以正常编译,但Vcu类型触发了编译错误:
let mut packet_data: [u8; 36] = [0; 36]; let a: &[u8] = <[u8;17]>::from(vin_number.clone().into()).as_ref(); let b: &[u8] = <[u8;11]>::from(serial_ecu.clone().into()).as_ref();
编译错误信息
error[E0283]: type annotations needed --> | 35 | let b: &[u8] = <[u8;11]>::from(serial_ecu.clone().into()).as_ref(); | --------------- ^^^^ | | | required by a bound introduced by this call | = note: multiple `impl`s satisfying `[u8; 11]: From<_>` found in the `core` crate: - impl<T> From<!> for T; - impl<T> From<(T, T, T, T, T, T, T, T, T, T, T)> for [T; 11]; - impl<T> From<T> for T; help: try using a fully qualified path to specify the expected types | 35 | let b: &[u8] = <[u8;11]>::from(<motorcycle::Vcu as Into<T>>::into(serial_ecu.clone())).as_ref(); | +++++++++++++++++++++++++++++++++++ ~ error[E0283]: type annotations needed --> | 35 | let b: &[u8] = <[u8;11]>::from(serial_ecu.clone().into()).as_ref(); | ^^^^ | note: multiple `impl`s satisfying `motorcycle::Vcu: Into<_>` found --> | 112 | impl Into<[u8;11]> for Vcu { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: and another `impl` found in the `core` crate: - impl<T, U> Into<U> for T where U: From<T>;; help: try using a fully qualified path to specify the expected types | 35 | let b: &[u8] = <[u8;11]>::from(<motorcycle::Vcu as Into<T>>::into(serial_ecu.clone())).as_ref(); | +++++++++++++++++++++++++++++++++++ ~
我已经通过显式指定Into实现路径解决了该问题:
let a: &[u8] = <[u8;17]>::from(vin_number.clone().into()).as_ref(); let b: &[u8] = <[u8;11]>::from(<self::Vcu as Into<[u8;11]>>::into(serial_ecu.clone())).as_ref();
差异原因分析
这两种类型的行为差异,本质是Rust类型推导时的歧义问题,根源在于标准库提供的通用实现:
Vin类型无歧义的原因
标准库仅为长度1到12的元组实现了From<(T,T,...)>对应长度数组的转换,而[u8;17]不在这个范围内,不存在额外的From实现。因此编译器在处理vin_number.clone().into()时,唯一符合条件的就是我们自己实现的Into<[u8;17]>,类型推导可以顺利完成。Vcu类型出现歧义的原因
[u8;11]属于标准库支持元组转换的长度范围,同时标准库还提供了通用的impl<T> From<T> for T(即类型可以转换为自身),再加上我们自己实现的Into<[u8;11]>,此时编译器无法确定into()的目标类型到底是[u8;11],还是其他符合约束的类型(比如11个u8组成的元组),因此抛出类型注解缺失的错误。
这并不是你的代码操作有误,而是标准库的通用实现与自定义实现产生了推导歧义,对于长度在1-12之间的数组,这种歧义会触发编译错误,需要显式指定Into的目标类型来消除歧义。
内容的提问来源于stack exchange,提问作者Larry Merino
相关产品推荐
相关产品推荐

