鍍金池/ 教程/ Java/ 類型轉(zhuǎn)換
哲學(xué)家就餐問題
鏈接進(jìn)階
名詞中英文對照
測試
引用和借用
泛型
方法語法
函數(shù)
不安全代碼
并發(fā)
裝箱語法和模式
注釋
棧和堆
運(yùn)算符與重載
語法索引
文檔
固有功能
所有權(quán)
循環(huán)
通用函數(shù)調(diào)用語法
不定長類型
<code>const</code> 和 <code>static</code>
迭代器
其他語言中的 Rust
枚舉
詞匯表
If語句
猜猜看
錯誤處理
生命周期
編譯器插件
發(fā)布途徑
閉包
trait 對象
不使用標(biāo)準(zhǔn)庫
關(guān)聯(lián)常量
外部函數(shù)接口(FFI)
類型轉(zhuǎn)換
原生類型
匹配
參考文獻(xiàn)
Rust 編程語言
內(nèi)聯(lián)匯編
條件編譯
選擇你的保證
學(xué)習(xí) Rust
`type`別名
自定義內(nèi)存分配器
屬性
if let
高效 Rust
可變性
語法和語義
模式
基準(zhǔn)測試
結(jié)構(gòu)體
變量綁定
語言項
切片模式
<code>Deref</code> 強(qiáng)制多態(tài)
關(guān)聯(lián)類型
裸指針
<code>Borrow</code> 和 <code>AsRef</code>
準(zhǔn)備
Rust 開發(fā)版
字符串

類型轉(zhuǎn)換

casting-between-types.md
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4

Rust,和它對安全的關(guān)注,提供了兩種不同的在不同類型間轉(zhuǎn)換的方式。第一個,as,用于安全轉(zhuǎn)換。相反,transmute允許任意的轉(zhuǎn)換,而這是 Rust 中最危險的功能之一!

強(qiáng)制轉(zhuǎn)換(Coercion)

類型間的強(qiáng)制轉(zhuǎn)換是隱式的并沒有自己的語法,不過可以寫作as。

強(qiáng)轉(zhuǎn)出現(xiàn)在letconststatic語句;函數(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時才有效)。

顯式轉(zhuǎn)換(Explicit coercions)

e as U是有效的僅當(dāng)eT類型而且T能強(qiáng)轉(zhuǎn)為U。

數(shù)值轉(zhuǎn)換

e as U的轉(zhuǎn)換在如下情況下也是有效的:

  • eT類型而且TU是任意數(shù)值類型:numeric-cast
  • e是一個類 C 語言枚舉(變量并沒有附加值),而且U是一個整型:enum-cast
  • eboolchar而且T是一個整型:prim-int-cast
  • eu8而且Ucharu8-char-cast

例如:

let one = true as u8;
let at_sign = 64 as char;
let two_hundred = -56i8 as u8;

數(shù)值轉(zhuǎn)換的語義是:

指針轉(zhuǎn)換

你也許會驚訝,[裸指針](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: Sizedptr-addr-cast
  • e是一個整型且U*U_0類型,同時U_0: Sizedaddr-ptr-cast
  • e&[T; n]類型且U*const T類型:array-ptr-cast
  • e是函數(shù)指針且U*T類型,同時T: Sizedfptr-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)換,只能幫你這么多了!

上一篇:不安全代碼下一篇:選擇你的保證