casting-between-types.md
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
Rust,和它對安全的關(guān)注,提供了兩種不同的在不同類型間轉(zhuǎn)換的方式。第一個,as
,用于安全轉(zhuǎn)換。相反,transmute
允許任意的轉(zhuǎn)換,而這是 Rust 中最危險的功能之一!
類型間的強(qiáng)制轉(zhuǎn)換是隱式的并沒有自己的語法,不過可以寫作as
。
強(qiáng)轉(zhuǎn)出現(xiàn)在let
,const
和static
語句;函數(shù)調(diào)用參數(shù);結(jié)構(gòu)體初始化的字符值;和函數(shù)返回值中。
最常用的強(qiáng)轉(zhuǎn)的例子是從引用中去掉可變性:
&mut T
到&T
一個相似的轉(zhuǎn)換時去掉一個[裸指針](Raw Pointers 裸指針.md)的可變性:
*mut T
到*const T
引用也能被強(qiáng)轉(zhuǎn)為裸指針:
&T
到*const T
&mut T
到*mut T
自定義強(qiáng)轉(zhuǎn)可以用[Deref
](Deref
coercions Deref
強(qiáng)制多態(tài).md)定義。
強(qiáng)轉(zhuǎn)是可傳遞的。
as
as
關(guān)鍵字進(jìn)行安全的轉(zhuǎn)換:
let x: i32 = 5;
let y = x as i64;
有三種形式的安全轉(zhuǎn)換:顯式轉(zhuǎn)換,數(shù)字類型之間的轉(zhuǎn)換,和指針轉(zhuǎn)換。
轉(zhuǎn)換并不是可傳遞的:即便是e as U1 as U2
是一個有效的表達(dá)式,e as U2
也不必要是(事實上只有在U1
強(qiáng)轉(zhuǎn)為U2
時才有效)。
e as U
是有效的僅當(dāng)e
是T
類型而且T
能強(qiáng)轉(zhuǎn)為U
。
e as U
的轉(zhuǎn)換在如下情況下也是有效的:
e
是T
類型而且T
和U
是任意數(shù)值類型:numeric-cast
e
是一個類 C 語言枚舉(變量并沒有附加值),而且U
是一個整型:enum-cast
e
是bool
或char
而且T
是一個整型:prim-int-cast
e
是u8
而且U
是char
:u8-char-cast
例如:
let one = true as u8;
let at_sign = 64 as char;
let two_hundred = -56i8 as u8;
數(shù)值轉(zhuǎn)換的語義是:
i32
->u32
)的轉(zhuǎn)換是一個no-op
u32
->u8
)會截斷u8
->u32
)會
你也許會驚訝,[裸指針](Raw Pointers 裸指針.md)與整型之間的轉(zhuǎn)換是安全的,而且不同類型的指針之間的轉(zhuǎn)換遵循一些限制。只有解引用指針是不安全的:
let a = 300 as *const char; // a pointer to location 300
let b = a as u32;
e as U
在如下情況是一個有效的指針轉(zhuǎn)換:
e
是*T
類型,U
是*U_0
類型,且要么U_0: Sized
要么unsize_kind(T) == unsize_kind(U_0)
:ptr-ptr-cast
e
是*T
類型且U
是數(shù)值類型,同時T: Sized
:ptr-addr-cast
e
是一個整型且U
是*U_0
類型,同時U_0: Sized
:addr-ptr-cast
e
是&[T; n]
類型且U
是*const T
類型:array-ptr-cast
e
是函數(shù)指針且U
是*T
類型,同時T: Sized
:fptr-ptr-cast
e
是函數(shù)指針且U
是一個整型:fptr-addr-cast
transmute
as
只允許安全的轉(zhuǎn)換,并會拒絕例如嘗試將 4 個字節(jié)轉(zhuǎn)換為一個u32
:
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
^~~~~~~~
這是一個“非標(biāo)量轉(zhuǎn)換(non-scalar cast)”因為這里我們有多個值:四個元素的數(shù)組。這種類型的轉(zhuǎn)換是非常危險的,因為他們假設(shè)多種底層結(jié)構(gòu)的實現(xiàn)方式。為此,我們需要一些更危險的東西。
transmute
函數(shù)由[編譯器固有功能](Intrinsics 固有功能.md)提供,它做的工作非常簡單,不過非??膳?。它告訴Rust對待一個類型的值就像它是另一個類型一樣。它這樣做并不管類型檢查系統(tǒng),并完全信任你。
在我們之前的例子中,我們知道一個有4個u8
的數(shù)組可以正常代表一個u32
,并且我們想要進(jìn)行轉(zhuǎn)換。使用transmute
而不是as
,Rust允許我們:
use std::mem;
unsafe {
let a = [0u8, 0u8, 0u8, 0u8];
let b = mem::transmute::<[u8; 4], u32>(a);
}
為了使它編譯通過我們要把這些操作封裝到一個unsafe
塊中。技術(shù)上講,只有mem::transmute
調(diào)用自身需要位于塊中,不過在這個情況下包含所有相關(guān)的內(nèi)容是有好處的,這樣你就知道該看哪了。在這例子中,a
的細(xì)節(jié)也是重要的,所以它們放到了塊中。你會看到各種風(fēng)格的代碼,有時上下文離得太遠(yuǎn),因此在unsafe
中包含所有的代碼并不是一個好主意。
雖然transmute
做了非常少的檢查,至少它確保了這些類型是相同大小的,這個錯誤:
use std::mem;
unsafe {
let a = [0u8, 0u8, 0u8, 0u8];
let b = mem::transmute::<[u8; 4], u64>(a);
}
和:
error: transmute called with differently sized types: [u8; 4] (32 bits) to u64
(64 bits)
除了這些,你可以自行隨意轉(zhuǎn)換,只能幫你這么多了!