昨天在猜數字遊戲中看到了這段程式碼:
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("讀取該行失敗");
其中我好奇這個 &mut
的語法如果不寫會怎麼樣?來試著拿掉後編譯看看,會發現印出這樣的錯誤:
error[E0308]: mismatched types
--> src/main.rs:15:31
|
15 | io::stdin().read_line(guess).expect("讀取該行失敗");
| --------- ^^^^^ expected `&mut String`, found `String`
| |
| arguments to this method are incorrect
|
note: method defined here
--> /.../rust/library/std/src/io/stdio.rs:398:12
|
398 | pub fn read_line(&self, buf: &mut String) -> io::Result<usize> {
| ^^^^^^^^^
help: consider mutably borrowing here
|
15 | io::stdin().read_line(&mut guess).expect("讀取該行失敗");
這個錯誤會說需要使用 &mut
這樣的 mutably borrowing
的型別,那這個 borrow 又是在借什麼呢?
這就會跟 Rust 其中一個重要的特性 —— 所有權 (Ownership) 有關,而這對像我這樣平常大多寫動態語言的人來說,可能就是學習 Rust 的第一道坎,那以下就跟著我這個 Rust 新手一起來由淺入深吧。
參考文件定義,所有權是指 Rust 中用來管理程式記憶體的一系列規則。在了解是什麼規則前,會需要先學習「程式是如何管理記憶體的」這件事。
參考這個影片提到的,程式管理記憶體的方式有三種:
clearTimeout
等,久而久之可能會造成記憶體洩漏 (memory leaks) 讓程式卡頓或 crash 掉malloc
(記憶體配置 memory allocation 的縮寫)、free
,C++ 可以使用 new
、delete
來操作記憶體的配置與釋放而要了解記憶體管理,另一個重點就是要知道一段執行中的程式的記憶體架構是什麼,這裡參考幾個教學影片與文章整理出上面這張圖,有興趣也可以參考下方的延伸閱讀,首推這個影片與這篇文章。
下面也大概說明一下記憶體架構,由低位到高位可以分成 4 個區段:
而 stack 與 heap 的關係,可以直接參考這個影片中的上面這張截圖,雖然範例是 C 語言,但程式碼算蠻單純的應該不會太難懂:
a
,會被放進 stack 中p
,先到 heap 中配置一段可以存整數大小的記憶體區段,並取得這個區段的記憶體位址 (例如圖上的 200
),存到 stack 中,而這個 p
也就是 C 語言中指標的概念,這個變數會指向 heap 中實際存放的 value假如今天在 C 中沒去用 free
做記憶體釋放,就把 p
指向另一段新的記憶體位址時 (例如圖上的 400
),這個 200
區段的記憶體很可能造成浪費。而如果是像前面提到的有 garbage collector 的語言就會定時把這些沒用到的記憶體清掉。
而在 Rust 裡面沒有 garbage collector,它又是如何優雅地管理記憶體的呢,且待下回分解。