如何在数值类型之间安全地和惯用地进行转换?

编者按: 这个问题来自于 Rust 1.0之前的一个版本,它引用了 Rust 1.0中没有的一些条目。答案仍然包含有价值的信息。

从(比如说) usize转换成 u32的惯用方法是什么?

例如,使用 4294967295us as u32铸造工程和 Rust 0.12类型铸造参考文档

数值可以强制转换为任何数值类型。原始指针值可以强制转换为任何整数类型或原始指针类型,也可以从任何整数类型或原始指针类型强制转换。任何其他强制转换都不受支持,并且将无法编译。

但是 4294967296us as u32将无声地溢出并给出0的结果。

我发现 ToPrimitiveFromPrimitive提供了很好的功能,比如 to_u32() -> Option<u32>,但是它们被标记为不稳定:

#[unstable(feature = "core", reason = "trait is likely to be removed")]

在数字(和指针)类型之间进行转换的惯用(和安全)方法是什么?

isize/usize依赖于平台的大小是我问这个问题的原因之一——最初的场景是我想从 u32转换到 usize,这样我就可以在 Vec<u32>中表示一棵树(例如 let t = Vec![0u32, 0u32, 1u32],然后得到节点2的祖父节点就是 t[t[2us] as usize]) ,我想知道如果 usize小于32位,它将如何失败。

113726 次浏览

转换数值

从一个完全适合另一个的类型

这里没有问题。使用 From特性明确表示没有发生损失:

fn example(v: i8) -> i32 {
i32::from(v) // or v.into()
}

你可以选择使用 as,但是建议你在不需要的时候避免使用它(见下文) :

fn example(v: i8) -> i32 {
v as i32
}

从一个不完全适合另一个的类型

没有一种方法是通用的——你问的是如何在一个空间里放两样东西。一个好的初始尝试是在值符合时使用 OptionSome,否则使用 None。然后,您可以让程序失败,或者根据需要替换默认值。

由于 Rust 1.34,你可以使用 TryFrom:

use std::convert::TryFrom;


fn example(v: i32) -> Option<i8> {
i8::try_from(v).ok()
}

在此之前,您必须自己编写类似的代码:

fn example(v: i32) -> Option<i8> {
if v > std::i8::MAX as i32 {
None
} else {
Some(v as i8)
}
}

来自一个可能完全适合或不适合另一个类型的

数字 isize/usize的范围可以代表您正在编译的 基于平台的变化。无论您的 目前平台是什么,您都需要使用 TryFrom

参见:

as所做的

但是 4294967296us as u32将无声地溢出并给出0的结果

当转换成较小的类型时,as只取数字的较低位,而不考虑上面的位,包括符号:

fn main() {
let a: u16 = 0x1234;
let b: u8 = a as u8;
println!("0x{:04x}, 0x{:02x}", a, b); // 0x1234, 0x34


let a: i16 = -257;
let b: u8 = a as u8;
println!("0x{:02x}, 0x{:02x}", a, b); // 0xfeff, 0xff
}

参见:

关于 ToPrimitive/FromPrimitive

RFC 369,Num Reform,States

理想情况下[ ... ] ToPrimitive[ ... ]都将被删除,以便采用更原则的方式处理类似 C 的枚举

与此同时,这些特征在 木箱中依然存在: