在實(shí)際使用Rust過(guò)程中很多時(shí)候沃斤,基于rust自身的來(lái)實(shí)現(xiàn)功能和代碼的編寫蛋铆,并依托編譯期自身來(lái)幫助我們進(jìn)行“編譯檢查”触幼,這時(shí)候相對(duì)來(lái)說(shuō)我們使用的Rust是“安全的”;
不過(guò)另外一些“特殊”場(chǎng)景下的需求需要我們來(lái)處理底層實(shí)現(xiàn)微猖,比如直接與系統(tǒng)層面交互或與匯編指令操作等谈息,那此刻rust可能就變得“不安全”。換種說(shuō)法:rust是一種同時(shí)包含安全和非安全特性的編程語(yǔ)言凛剥。
- 安全的Rust
若是我們所編寫的代碼均是使用安全rust進(jìn)行編寫的侠仇,此時(shí)我們無(wú)需擔(dān)心類型安全和內(nèi)存安全,更無(wú)需專注于“垂懸指針”犁珠、“二次釋放引用”逻炊、“其他各種未定義的行為”等;諸如標(biāo)準(zhǔn)庫(kù)中也提供了很多工具庫(kù)犁享,來(lái)幫忙我們構(gòu)建符合安全Rust規(guī)范的高性能的應(yīng)用和庫(kù)余素。
安全的Rust是一種真正安全的編程語(yǔ)言。
- 非安全的Rust
在實(shí)際應(yīng)用過(guò)程中炊昆,我們也許會(huì)面臨:編寫一種標(biāo)準(zhǔn)庫(kù)沒(méi)有涉及到的底層抽象桨吊;開發(fā)標(biāo)準(zhǔn)庫(kù)(存粹使用rust);做一些類型系統(tǒng)不能理解的事情凤巨,直接操作各種字節(jié)碼视乐。這時(shí)我們就需要對(duì)底層實(shí)現(xiàn)細(xì)節(jié)的控制-不安全的rust。
不代表Rust不能進(jìn)行“非安全的行為”
安全與不安全的rust的異同
不過(guò)在Rust中安全和非安全rust的語(yǔ)法規(guī)則是完全相同的敢茁,只不過(guò)非安全的rust允許你可以做一些不安全的行為佑淀;
另外提供安全與非安全rust的價(jià)值在于既能享用類似C那樣非安全語(yǔ)言的便利及底層的把控,又減少處理C與其他安全語(yǔ)言集成時(shí)的問(wèn)題彰檬;安全與非安全代碼的交互
在Rust中安全與非安全代碼是靠unsafe關(guān)鍵字分離的類似兩種語(yǔ)言之間接口的角色伸刃。這也就可以認(rèn)為所有非安全代碼都被unsafe
隔離在外面的或者使用#![forbid(unsafe_code)]
確保只能編寫安全的代碼。
關(guān)于unsafe
1逢倍、聲明代碼中存在編譯期無(wú)法檢查的安全規(guī)范
2捧颅、開發(fā)者會(huì)自覺遵守相關(guān)規(guī)范并不會(huì)主動(dòng)破壞它(只能默認(rèn)認(rèn)為開發(fā)者“確保”了安全)
在實(shí)際應(yīng)用中我們可以使用unsafe
來(lái)定義函數(shù)和trait特型中存在不受編譯期檢查的規(guī)范:
- 對(duì)于函數(shù)较雕,
unsafe
意味著調(diào)用函數(shù)的開發(fā)者必須查閱文檔并確保他們的用法符合函數(shù)的安全要求隘道;
當(dāng)給一個(gè)代碼塊添加
unsafe
關(guān)鍵字,也就是意味著該聲明塊中的代碼都已進(jìn)行“人工檢查”并符合相關(guān)規(guī)范郎笆;
- 對(duì)于trait的聲明,
unsafe
意味著trait的開發(fā)者也必須查詢文檔以確保trait的實(shí)現(xiàn)符合其安全要求忘晤;
當(dāng)實(shí)現(xiàn)一個(gè)trait時(shí)使用了
unsafe
關(guān)鍵字宛蚓,聲明實(shí)現(xiàn)符合trait的安全規(guī)范;比如實(shí)現(xiàn)Send
(一個(gè)標(biāo)記trait设塔,只是聲明凄吏;其安全性均有實(shí)現(xiàn)該trait的開發(fā)者者來(lái)“確保”)的類型可以絕對(duì)安全move到另一個(gè)線程中。
3痕钢、標(biāo)準(zhǔn)庫(kù)中的非安全函數(shù)
-
slice::get_unchecked
图柏,可接受不受檢查的索引值,也就是存在內(nèi)存安全機(jī)制被破壞的可能; -
mem::transmute
任连,將值重新解析成另一種類型蚤吹,即允許隨意繞過(guò)類型安全機(jī)制的限制; - 所有指向確定大小類型 (sized type) 的裸指針都有
offset
方法,當(dāng)傳入的偏移量越界時(shí)將導(dǎo)致未定義行為; - 所有 FFI(Foreign Function Interface)函數(shù)都是
unsafe
的随抠,因?yàn)槠渌恼Z(yǔ)言可以做各種的操作而 Rust 編譯器無(wú)法檢查它;
4裁着、非安全的trait
-
Send
是一個(gè)標(biāo)志 trait(即沒(méi)有任何方法的 trait),承諾所有的實(shí)現(xiàn)都可以安全地 move 到另一個(gè)線程; -
Sync
也是一個(gè)標(biāo)志 trait拱她,承諾線程可以通過(guò)共享的引用共享它的實(shí)現(xiàn);
許多 Rust 標(biāo)準(zhǔn)庫(kù)其實(shí)內(nèi)部也使用了非安全 Rust二驰。這些庫(kù)的實(shí)現(xiàn)方法都經(jīng)過(guò)了嚴(yán)苛的人工檢查,所以這些基于非安全 Rust 實(shí)現(xiàn)的安全 Rust 接口依然可以認(rèn)為是安全的.
安全和非安全的 Rust 之間存在一種不對(duì)稱的信任關(guān)系秉沼。安全 Rust 必須無(wú)條件信任非安全 Rust桶雀,假定所有與之打交道的非安全代碼都是正確的。反過(guò)來(lái)唬复,非安全 Rust 卻要謹(jǐn)慎對(duì)待安全 Rust 的代碼矗积。
非安全 Rust 能做什么
非安全 Rust 比安全 Rust 可以多做的事情只有以下幾個(gè):
- 解引用裸指針
- 調(diào)用非安全函數(shù)(包括 C 語(yǔ)言函數(shù),編譯器內(nèi)聯(lián)函數(shù)盅抚,還有直接內(nèi)存分配等)
- 實(shí)現(xiàn)非安全 trait
- 訪問(wèn)或修改可變靜態(tài)變量
就這些操作被歸為非安全的漠魏,是因?yàn)槭褂玫貌徽_就會(huì)導(dǎo)致可怕的未定義行為。一旦觸發(fā)了未定義行為妄均,編譯器就可以放飛自我柱锹,肆意破壞你的程序。切記丰包,一定不能給未定義行為任何的機(jī)會(huì)禁熏。
與 C 不同,Rust 充分限制了可能出現(xiàn)的未定義行為的種類邑彪。語(yǔ)言核心只需要防止這幾種行為:
- 解引用 null 指針瞧毙,懸垂指針,或者未賦值的指針
- 讀取未初始化的內(nèi)存
- 破壞指針混淆規(guī)則
- 創(chuàng)建非法的基本類型:
- 懸垂引用與 null 引用
- 空的 fn 指針
- 0 和 1 以外的 bool 類型值
- 未定義的枚舉類型的項(xiàng)
- 在 [0x0,0xD&FF] 和 [0xE000, 0x10FFFF] 以外的 char 類型值
- 非 utf-8 編碼的 str
- 不謹(jǐn)慎地調(diào)用其他語(yǔ)言
- 數(shù)據(jù)競(jìng)爭(zhēng)
只有這些Rust語(yǔ)言自身可以導(dǎo)致未定義行為的操作寄症。當(dāng)然宙彪,非安全函數(shù)和 trait 可以聲明自己專有的安全規(guī)范,要求開發(fā)者必須遵守以避免未定義行為有巧。比如释漆,allocator API 聲明回收一段未分配的內(nèi)存是未定義行為。
但是篮迎,違背這些專有的規(guī)范通常也只是間接地觸發(fā)上面列出的行為男图。另外示姿,編譯器內(nèi)聯(lián)函數(shù)也可能引入一些規(guī)則,一般是針對(duì)代碼優(yōu)化的假設(shè)條件逊笆。比如栈戳,Vec 和 Box 使用的內(nèi)聯(lián)函數(shù)要求傳入的指針永遠(yuǎn)不能為 null。
Rust 對(duì)于一些模糊的操作則通常比較寬容难裆。Rust 會(huì)認(rèn)為下列操作是安全的:
- 死鎖
- 競(jìng)爭(zhēng)條件
- 內(nèi)存泄漏
- 調(diào)用析構(gòu)函數(shù)失敗
- 整型值溢出
- 終止程序
- 刪除產(chǎn)品數(shù)據(jù)庫(kù)
當(dāng)然子檀,有以上行為的程序極有可能就是錯(cuò)誤的。Rust 提供了一系列的工具減少這種事情的發(fā)生差牛,但是完全地杜絕它們其實(shí)是不現(xiàn)實(shí)的命锄。