本文簡要介紹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。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。