Rust中`as`操作符如何将枚举转换为整数?
Rust中
as转换为何不遵循Into实现? 我在项目里定义了一个Bit枚举,还给它实现了Into<u8, u16, …, u128> trait,本来以为Bit as u32是.into()的语法糖,结果测试全挂了。
最开始的枚举定义是这样的:
pub enum Bit { On, Off, }
后来我随便把变体顺序改成了:
pub enum Bit { Off, On, }
再跑测试,居然多过了2个。
排查后发现,as u32完全无视我写的Into实现,只按枚举的判别式做转换。但我搞不懂,像u32 as f64这种转换明显不是简单按位操作,那as到底遵循什么规则?
之前查相关问题,有人说枚举用了TryFrom实现,但我不明白为什么Rust会优先选可能panic的TryFrom,而不是我专门实现的Into——而且我的TryFrom是把0映射到Bit::Off、1映射到Bit::On,其他值返回错误,这也解释不了之前的现象。
想知道这个语言特性的官方文档指引,相关代码如下:
问题代码
使用as u32的函数:
fn bits_flipped(left: &BitString, right: &BitString) -> u32 { assert_eq!( left.len(), right.len(), "the length of the bitstrings is not equal. Left is {} and right is {}", left.len(), right.len() ); let mut difference: u32 = 0; for i in 0..left.len() { difference += (left[i] ^ right[i]) as u32; // 问题出在这里 } difference }
Into实现宏
macro_rules! bit_into_type { ($t:ty) => { impl Into<$t> for Bit { #![allow(clippy::from_over_into)] fn into(self) -> $t { match self { Self::On => 1, Self::Off => 0, } } } }; }
TryFrom实现宏
macro_rules! bit_try_from { ($t:ty) => { impl TryFrom<$t> for Bit { type Error = String; fn try_from(value: $t) -> Result<Self, Self::Error> { match value { 0 => Ok(Bit::Off), 1 => Ok(Bit::On), value => Err(format!("Cannot represent {} as a single bit", value)), } } } }; }
官方规则说明
根据Rust官方文档,as是语言内置的强制转换机制,和Into/TryFrom等trait没有任何关联:
- 枚举转整数的
as转换:直接使用枚举的判别式(discriminant)。默认情况下,第一个变体的判别式为0,第二个为1,以此类推——这就是你修改变体顺序后测试通过的原因:原顺序中On是0、Off是1,as转换结果和Into实现的逻辑完全相反;修改顺序后Off是0、On是1,刚好和Into的输出匹配,所以测试通过。 - 其他类型的
as转换:遵循编译器硬编码的固定规则,比如整数转浮点数是基于数值的转换,而非按位复制;整数间的as转换会根据目标类型进行截断或符号扩展等,这些规则不依赖任何用户实现的trait。 as与trait的优先级:as是最低层级的语言特性,不会调用任何用户定义的trait方法。如果想使用自己的转换逻辑,必须显式调用.into(),不能依赖as语法。
内容的提问来源于stack exchange,提问作者Ryoji
相关产品推荐
相关产品推荐

