Function std::mem::transmute 1.0.0 (const: 1.46.0)[−][src]
pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(e: T) -> U
Expand description
将一种类型的值的位重新解释为另一种类型。
两种类型都必须具有相同的大小。 invalid value 既不是原始版本,也不是结果。
transmute
在语义上等效于从一种类型到另一种类型的按位移动。它将位从源值复制到目标值,然后忘记原始值。
就像 transmute_copy
一样,它等同于 C 的引擎盖下的 memcpy
。
由于 transmute
是按值运算,因此不必担心 transmuted values 本身的对齐。
与任何其他函数一样,编译器已经确保 T
和 U
都正确对齐。
但是,当将 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]
) 转换为 u32
,f64
等:
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