5.30.类型转换
最后更新于:2022-04-01 00:44:33
Rust,和它对安全的关注,提供了两种不同的在不同类型间转换的方式。第一个,`as`,用于安全转换。相反,`transmute`允许任意的转换,而这是Rust中最危险的功能之一!
## `as`
`as`关键字进行了基本的转换:
~~~
let x: i32 = 5;
let y = x as i64;
~~~
然而,它只允许特定类型的转换:
~~~
let a = [0u8, 0u8, 0u8, 0u8];
let b = a as u32; // four eights makes 32
~~~
这会报错:
~~~
error: non-scalar cast: `[u8; 4]` as `u32`
let b = a as u32; // four eights makes 32
^~~~~~~~
~~~
它是一个“混合类型转换”因为这里我们有多个值:数组的4个元素。这种类型的转换灰常危险,因为它们假设了多个底层结构的实现方式。为此,我们需要一些更危险的东西。
## `transmute`
`transmute`函数由[编译器固有功能](http://doc.rust-lang.org/nightly/book/intrinsics.html)提供,它做的工作非常简单,不过非常可怕。它告诉Rust对待一个类型的值就像它是另一个类型一样。它这样做并不管类型检查系统,并单单完全信任你。
在我们之前的例子中,我们知道一个有4个`u8`的数组可以正常代表一个`u32`,并且我们想要进行转换。使用`transmute`而不是`as`,Rust允许我们:
~~~
use std::mem;
unsafe {
let a = [0u8, 0u8, 0u8, 0u8];
let b = mem::transmute::<[u8; 4], u32>(a);
}
~~~
为了使它编译通过我们要把这些操作封装到一个`unsafe`块中。技术上讲,只有`mem::transmute`调用自身需要位于块中,不过在这个情况下包含所有相关的内容是有好处的,这样你就知道该看哪了。在这例子中,`a`的细节也是重要的,所以它们放到了块中。你会看到各种风格的代码,有时上下文离得太远,因此在`unsafe`中包含所有的代码并不是一个好主意。
虽然`transmute`做了非常少的检查,至少它确保了这些类型是相同大小的,这个错误:
~~~
use std::mem;
unsafe {
let a = [0u8, 0u8, 0u8, 0u8];
let b = mem::transmute::<[u8; 4], u64>(a);
}
~~~
和:
~~~
error: transmute called on types with different sizes: [u8; 4] (32 bits) to u64
(64 bits)
~~~
除了这些,你可以自行转换!