5.30.类型转换

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函数由编译器固有功能提供,它做的工作非常简单,不过非常可怕。它告诉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)

除了这些,你可以自行转换!

文章导航