選單

Rust 中指標那些事

關注「Rust程式設計指北」,一起學習 Rust,給未來投資

大家好,我是胖蟹哥。

現在高階語言很多都沒有指標,可能因為指標太靈活、太難。Rust 支援指標,那和 C 比較有什麼異同呢?本文聊聊這個問題。

我已經關注Rust[1]大約一年了。於是決定用它來製作一個簡單的小程式,或者在其中實現一些簡單的功能,以親眼看看它到底有多符合人體工程學,以及 rustc 編譯出什麼樣的機器程式碼。但是上週末我發現需要一個工具來清理一些預處理器的問題,所以我決定用 Rust 編寫它,而不是將 Shell 和 Python 組合在一起。

從我之前經驗看,我知道有很多不同的“指標”,但我發現對它們的所有描述很少或者不明確。具體來說,Rust 稱自己是一種系統程式語言,但我沒有找到關於不同指標如何對映到 C(系統程式語言)的明確描述。最終,我偶然發現了The Periodic Table of Rust Types[2],這讓事情變得更清晰了一些,但我仍然覺得沒有真正理解。

週末,我對 Rust 進行了各種探索,已經掌握了足夠的東西來寫這篇關於 Rust 如何做事的解釋。歡迎反饋。

我將描述 C 中對應的術語。為了簡單起見,我將:

假設你瞭解 C

假設你可以閱讀 Rust

不要為 C 程式碼片段煩惱 const

不談論可變性

下文中,我們假設存在 struct T,具體欄位無關緊要,即:

const T 和 mut T

這些是原始指標。一般來說,最好別使用它們,因為只有unsafe程式碼才能進行解引用,而 Rust 的重點是編寫儘可能多的安全程式碼。

原始指標就像 C 中的指標。如果你建立一個指標,你最終會使用 位元組作為指標。即:

&T and &mut T

這些是借用引用。它們使用與原始指標相同的地址空間,並在生成的機器程式碼中以完全相同的方式執行。考慮這個簡單的例子:

經過 rustc 編譯後:

請注意,這兩個函式是逐位相同的。

借用引用和原始指標之間的唯一區別是:

引用永遠不會指向虛假地址(即,它們永遠不會為 NULL 或未初始化),

編譯器不允許你對引用進行任意指標運算,

借用檢查器會讓你在一段時間內質疑你的生活。。。Rust 的難點之一

(第三點隨著時間的推移會變得更好。)

即智慧指標。如果你是 C++ 程式設計師,那麼你大機率已經熟悉它們了。

幾乎所有的文件和教程都說, 不是指標,而是一個包含指向堆分配記憶體的結構,該記憶體大到足以容納 T。堆分配和釋放是自動處理的。(分配是在 Box::new 函式中完成的,而釋放是透過Drop trait[3]完成的,但這與記憶體佈局無關。)換句話說, 類似於:

Then, when you make a new box you end up putting only what amounts to sizeof(struct T *) on the stack and it magically starts pointing to somewhere on the heap。 In other words, the Rust code like this:

然後,當你建立一個新的 box 時,你最終只會把 放在堆疊上,它神奇地開始指向堆上的某個地方。換句話說,Rust 程式碼是這樣的:

大致相當於:

&[T] 和 &mut [T]

這是借用切片,這是事情變得有趣的地方。儘管看起來它們只是引用(如前所述,轉換為簡單的 C 樣式指標),但它們遠不止於此。這些型別的引用使用胖指標—— 即指標和長度的組合。

這非常強大,因為它允許在執行時進行邊界檢查並且獲取切片的子集基本上是無損耗的!

&[T; n] 和 &mut [T; n]

這些是對陣列的借用引用。它們與借用切片不同。由於陣列的長度是編譯時常量(如果 n 不是常量,編譯器報錯),所有邊界檢查都可以靜態執行。因此不需要在胖指標中傳遞長度。所以它們作為普通指標傳遞。

T, [T; n] 和  [T]

雖然這些不是指標,但為了完整起見,我把它們包括在這裡。

T

就像在 C 中一樣,結構使用其型別所需的儘可能多的空間(即,其成員的大小加上填充的總和)。

[T; n]

就像在 C 中一樣,結構體陣列使用結構體大小的 n 倍。

[T]

注意,你不能構造 [T]。當你考慮該型別的含義時,這實際上是完全合理的。這就是說我們有一些可變大小的記憶體切片,以便對 T 型別元素進行訪問。由於這是可變大小的,編譯器不可能在編譯時為其保留空間,因此我們會收到編譯器錯誤。

更復雜的答案涉及Sized trait[4],到目前為止我已經巧妙地設法避免了它。

總結

That was a lot of text, so I decided to compact it and make the following table。 In the table, I assume that our T struct is 100 bytes in size。 In other words:

以上內容不少,為了方便,我把它製作成下表。在表中,我假設 T 結構的大小為 100 位元組:

表格如下:

提醒一句:我假設各種指標的大小實際上是實現細節,不應該依賴於這種方式。

我沒有介紹 、、 和 ,因為我不認為它們是基本型別,而是構建在切片、結構、引用和 Box 之上的便利型別。

參考資料[1]

Rust:https://www。rust-lang。org/

[2]

The Periodic Table of Rust Types:http://cosmic。mearie。org/2014/01/periodic-table-of-rust-types/

[3]

Drop trait:https://doc。rust-lang。org/std/ops/trait。Drop。html

[4]

Sized trait:https://doc。rust-lang。org/std/marker/trait。Sized。html