Function core::mem::transmute1.0.0 (const: 1.46.0)[][src]

pub unsafe extern "rust-intrinsic" fn transmute<T, U>(e: T) -> U
Expand description

将一种类型的值的位重新解释为另一种类型。

两种类型都必须具有相同的大小。 invalid value 既不是原始版本,也不是结果。

transmute 在语义上等效于从一种类型到另一种类型的按位移动。它将位从源值复制到目标值,然后忘记原始值。 就像 transmute_copy 一样,它等同于 C 的引擎盖下的 memcpy

由于 transmute 是按值运算,因此不必担心 transmuted values 本身的对齐。 与任何其他函数一样,编译器已经确保 TU 都正确对齐。 但是,当将 point 的值转换为其他位置(例如指针,引用,boxes…) 时,调用者必须确保所指向的值正确对齐。

transmute 是非常不安全的。有很多方法可以使用此函数来导致 未定义的行为transmute 应该是绝对不得已的方法。

nomicon 具有其他文档。

Examples

transmute 确实有一些用途。

将指针转换为函数指针。对于函数指针和数据指针具有不同大小的机器,这不是可移植的。

fn foo() -> i32 {
    0
}
let pointer = foo as *const ();
let function = unsafe {
    std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);
Run

延长生命周期或缩短不变的生命周期。这是高级的,非常不安全的 Rust!

struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
                                             -> &'b mut R<'c> {
    std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}
Run

Alternatives

不要失望: transmute 的许多用途可以通过其他方式实现。 以下是 transmute 的常见应用程序,可以用更安全的结构替换它。

将原始 bytes(&[u8]) 转换为 u32f64 等:

let raw_bytes = [0x78, 0x56, 0x34, 0x12];

let num = unsafe {
    std::mem::transmute::<[u8; 4], u32>(raw_bytes)
};

// 改用 `u32::from_ne_bytes`
let num = u32::from_ne_bytes(raw_bytes);
// 或使用 `u32::from_le_bytes` 或 `u32::from_be_bytes` 指定字节顺序
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);
Run

将指针变成 usize:

let ptr = &0;
let ptr_num_transmute = unsafe {
    std::mem::transmute::<&i32, usize>(ptr)
};

// 请改用 `as` 进行类型转换
let ptr_num_cast = ptr as *const i32 as usize;
Run

*mut T 变成 &mut T:

let ptr: *mut i32 = &mut 0;
let ref_transmuted = unsafe {
    std::mem::transmute::<*mut i32, &mut i32>(ptr)
};

// 改用借用
let ref_casted = unsafe { &mut *ptr };
Run

&mut T 变成 &mut U:

let ptr = &mut 0;
let val_transmuted = unsafe {
    std::mem::transmute::<&mut i32, &mut u32>(ptr)
};

// 现在,将 `as` 放在一起并重新借用 - 请注意,`as` `as` 的链接不是可传递的
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
Run

&str 变成 &[u8]:

// 这不是执行此操作的好方法。
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);

// 您可以使用 `str::as_bytes`
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);

// 或者,如果您可以控制字符串,则只需使用字节字符串即可。
assert_eq!(b"Rust", &[82, 117, 115, 116]);
Run

Vec<&T> 变成 Vec<Option<&T>>

要转换容器内容的内部类型,必须确保不违反容器的任何不变式。 对于 Vec,这意味着内部类型的大小和对齐方式都必须匹配。 其他容器可能依赖于类型,对齐方式甚至 TypeId 的大小,在这种情况下,在不违反容器不变式的情况下根本不可能进行转换。

let store = [0, 1, 2, 3];
let v_orig = store.iter().collect::<Vec<&i32>>();

// 克隆 vector,因为稍后我们将重用它们
let v_clone = v_orig.clone();

// 使用 transmute: 这依赖于 `Vec` 的未指定数据布局,这是一个坏主意,并可能导致未定义的行为。
// 但是,它是无副本的。
let v_transmuted = unsafe {
    std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_clone)
};

let v_clone = v_orig.clone();

// 这是建议的安全方法。
// 但是,它确实将整个 vector 复制到一个新数组中。
let v_collected = v_clone.into_iter()
                         .map(Some)
                         .collect::<Vec<Option<&i32>>>();

let v_clone = v_orig.clone();

// 这是 "transmuting" 和 `Vec` 的正确无复制,不安全的方式,而无需依赖数据布局。
// 我们不执行字面上的调用 `transmute`,而是执行指针强制转换,但是就将原始内部类型 (`&i32`) 转换为新的 (`Option<&i32>`) 而言,这具有所有相同的警告。
// 除了上面提供的信息之外,还请查阅 [`from_raw_parts`] 文档。
let v_from_raw = unsafe {
    // 确保原始 vector 没有被丢弃。
    let mut v_clone = std::mem::ManuallyDrop::new(v_clone);
    Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>,
                        v_clone.len(),
                        v_clone.capacity())
};
Run

实现 split_at_mut:

use std::{slice, mem};

// 有多种方法可以执行此操作,并且以下 (transmute) 方法存在多个问题。
fn split_at_mut_transmute<T>(slice: &mut [T], mid: usize)
                             -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice);
        // 第一: 转换不是安全类型; 它所检查的只是 T 和 U 的大小相同。
        // 其次,在这里,您有两个指向同一内存的可变引用。
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// 这消除了类型安全问题; `&mut *`* 仅 *将为您提供 `&mut T` 或 `* mut T` 的 `&mut T`。
fn split_at_mut_casts<T>(slice: &mut [T], mid: usize)
                         -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let slice2 = &mut *(slice as *mut [T]);
        // 但是,您仍然有两个指向同一内存的可变引用。
        (&mut slice[0..mid], &mut slice2[mid..len])
    }
}

// 这就是标准库的工作方式。
// 如果您需要执行以下操作,这是最好的方法
fn split_at_stdlib<T>(slice: &mut [T], mid: usize)
                      -> (&mut [T], &mut [T]) {
    let len = slice.len();
    assert!(mid <= len);
    unsafe {
        let ptr = slice.as_mut_ptr();
        // 现在,它具有三个指向同一内存的可变引用。`slice`,右值 ret.0 和右值 ret.1。
        // `slice` `let ptr = ...` 之后从未使用过,因此可以将其视为 "dead",因此,您只有两个实际的可变切片。
        (slice::from_raw_parts_mut(ptr, mid),
         slice::from_raw_parts_mut(ptr.add(mid), len - mid))
    }
}
Run