本文简要介绍rust语言中 Function std::mem::transmute
的用法。
用法
pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(e: T) -> U
将一种类型的值的位重新解释为另一种类型。
两种类型必须具有相同的尺寸。原始数据和结果都不能是 invalid value 。
transmute
在语义上等同于将一种类型按位移动到另一种类型。它将源值中的位复制到目标值中,然后忘记原始值。它相当于 C 的 memcpy
,就像 transmute_copy
一样。
因为transmute
是by-value操作,对齐值本身发生了转变不是一个问题。与任何其他函数一样,编译器已经确保了T
和U
正确对齐。但是,当转换值时指向别处(例如指针、引用、框...),调用者必须确保正确对齐指向的值。
transmute
是难以置信不安全。造成的方法有很多种未定义的行为有了这个函数。transmute
应该是绝对的最后手段。
在 const
上下文中将指针转换为整数是 undefined behavior 。任何将结果值用于整数运算的尝试都将中止const-evaluation。
nomicon 有附加文档。
例子
transmute
在某些方面非常有用。
将指针转换为函数指针。这对于函数指针和数据指针具有不同大小的机器是不可移植的。
fn foo() -> i32 {
0
}
let pointer = foo as *const ();
let function = unsafe {
std::mem::transmute::<*const (), fn() -> i32>(pointer)
};
assert_eq!(function(), 0);
延长寿命,或缩短不变的寿命。这是高级的,非常不安全的 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)
}
备择方案
不要绝望:transmute
的许多用途可以通过其他方式实现。以下是 transmute
的常见应用,可以用更安全的结构替换。
将原始字节(&[u8]
)转换为 u32
、f64
等:
let raw_bytes = [0x78, 0x56, 0x34, 0x12];
let num = unsafe {
std::mem::transmute::<[u8; 4], u32>(raw_bytes)
};
// use `u32::from_ne_bytes` instead
let num = u32::from_ne_bytes(raw_bytes);
// or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);
将指针变成 usize
:
let ptr = &0;
let ptr_num_transmute = unsafe {
std::mem::transmute::<&i32, usize>(ptr)
};
// Use an `as` cast instead
let ptr_num_cast = ptr as *const i32 as usize;
将 *mut T
变成 &mut T
:
let ptr: *mut i32 = &mut 0;
let ref_transmuted = unsafe {
std::mem::transmute::<*mut i32, &mut i32>(ptr)
};
// Use a reborrow instead
let ref_casted = unsafe { &mut *ptr };
将 &mut T
变成 &mut U
:
let ptr = &mut 0;
let val_transmuted = unsafe {
std::mem::transmute::<&mut i32, &mut u32>(ptr)
};
// Now, put together `as` and reborrowing - note the chaining of `as`
// `as` is not transitive
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
将 &str
变成 &[u8]
:
// this is not a good way to do this.
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);
// You could use `str::as_bytes`
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);
// Or, just use a byte string, if you have control over the string
// literal
assert_eq!(b"Rust", &[82, 117, 115, 116]);
将 Vec<&T>
变成 Vec<Option<&T>>
。
要转换容器内容的内部类型,必须确保不违反容器的任何不变量。为了Vec
,这意味着两个尺寸和对齐内部类型必须匹配。其他容器可能依赖于类型的大小、对齐方式,甚至是TypeId
,在这种情况下,在不违反容器不变量的情况下根本不可能进行转换。
let store = [0, 1, 2, 3];
let v_orig = store.iter().collect::<Vec<&i32>>();
// clone the vector as we will reuse them later
let v_clone = v_orig.clone();
// Using transmute: this relies on the unspecified data layout of `Vec`, which is a
// bad idea and could cause Undefined Behavior.
// However, it is no-copy.
let v_transmuted = unsafe {
std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(v_clone)
};
let v_clone = v_orig.clone();
// This is the suggested, safe way.
// It does copy the entire vector, though, into a new array.
let v_collected = v_clone.into_iter()
.map(Some)
.collect::<Vec<Option<&i32>>>();
let v_clone = v_orig.clone();
// This is the proper no-copy, unsafe way of "transmuting" a `Vec`, without relying on the
// data layout. Instead of literally calling `transmute`, we perform a pointer cast, but
// in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`),
// this has all the same caveats. Besides the information provided above, also consult the
// [`from_raw_parts`] documentation.
let v_from_raw = unsafe {
// Ensure the original vector is not dropped.
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())
};
实施 split_at_mut
:
use std::{slice, mem};
// There are multiple ways to do this, and there are multiple problems
// with the following (transmute) way.
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);
// first: transmute is not type safe; all it checks is that T and
// U are of the same size. Second, right here, you have two
// mutable references pointing to the same memory.
(&mut slice[0..mid], &mut slice2[mid..len])
}
}
// This gets rid of the type safety problems; `&mut *` will *only* give
// you an `&mut T` from an `&mut T` or `*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]);
// however, you still have two mutable references pointing to
// the same memory.
(&mut slice[0..mid], &mut slice2[mid..len])
}
}
// This is how the standard library does it. This is the best method, if
// you need to do something like this
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();
// This now has three mutable references pointing at the same
// memory. `slice`, the rvalue ret.0, and the rvalue ret.1.
// `slice` is never used after `let ptr = ...`, and so one can
// treat it as "dead", and therefore, you only have two real
// mutable slices.
(slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid))
}
}
相关用法
- Rust transmute_copy用法及代码示例
- Rust try用法及代码示例
- Rust try_exists用法及代码示例
- Rust try_from_fn用法及代码示例
- Rust type_name用法及代码示例
- Rust take_hook用法及代码示例
- Rust take用法及代码示例
- Rust thread_local用法及代码示例
- Rust todo用法及代码示例
- Rust type_name_of_val用法及代码示例
- Rust tuple用法及代码示例
- Rust temp_dir用法及代码示例
- Rust UdpSocket.set_multicast_loop_v6用法及代码示例
- Rust i64.overflowing_add_unsigned用法及代码示例
- Rust Box.downcast用法及代码示例
- Rust BTreeMap.last_key_value用法及代码示例
- Rust str.make_ascii_uppercase用法及代码示例
- Rust u128.checked_pow用法及代码示例
- Rust usize.wrapping_mul用法及代码示例
- Rust AtomicU8.fetch_sub用法及代码示例
- Rust PanicInfo.payload用法及代码示例
- Rust MaybeUninit.assume_init_mut用法及代码示例
- Rust String.try_reserve用法及代码示例
- Rust Mutex.new用法及代码示例
- Rust f32.exp用法及代码示例
注:本文由纯净天空筛选整理自rust-lang.org大神的英文原创作品 Function std::mem::transmute。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。