Rust 所有權(三):Borrow - Reference 借用與引用

  在上一篇文章中給出了一段演示數據移動(Move)的示例代碼:

// 編譯錯誤
fn main() {
    let x = String::from("Hello, World!");
    let y = x;
    println!("{}", x);  // 打印 x
    //             ^ value borrowed here after move
}

  在第 4 行將 x 賦值給 y 後,變量 x 就變得不可用了。對於這種行爲吧,咬咬牙還能忍。再來看看下面這段代碼:

fn main() {
    let x = String::from("Hello, World!");
    print_length(x);
    println!("{}", x);
    //             ^ Error! value borrowed here after move
}
fn print_length(s: String) {
    println!("Length = {}", s.len());
}

  第 4 行,使用變量 x 作爲參數來調用函數 print_length,在調用函數後,變量 x 同樣變得不可用了。什麼!?我只是想看一下字符串的長度,就不再讓我使用變量 x 了?這就有點觸及底線了,簡直沒法玩兒了。

  爲什麼函數調用讓 x 變得不可用?

  因爲在將變量 x 作爲參數傳遞時,在 x 身上發生了 Move。爲了便於理解,可以將傳參看作是賦值操作。更深層的原因,在調用函數 print_length 時,函數的實際參數會執行入棧操作,作爲 print_length 函數棧幀的組成部分。在將參數 s 入棧時,相當於執行了 s = x 賦值語句,因爲 x 是 Move 而不是 Copy 的,因此所有權發生了移動。

  但是,我不想要所有權啊!我就是想臨時看看字符串的長度啊!在這種情況下,我們實際上想要的是 “借用 borrow” 數據,而非 “佔有 own” 數據。

  什麼是借用(Borrow)

  “借用” 這個詞兒形象地描述了它是如何使用數據的。我可以去圖書館借閱一本《論語》,顯然我不是這本書的所有者,還是要還回圖書館的。同理,我們可以借用字符串,而不讓所有權發生移動。示例:

fn main() {
    let x = String::from("Hello, World!");
    print_length(&x);
    //           ^ 添加了 '&'
    println!("{}", x);
    //             ^ OK
}
fn print_length(s: &String) {
    //             ^ 添加了 '&'
    println!("Length = {}", s.len());
}

   此例的代碼與之前版本間的差異在於添加了兩處 '&' 符號。熟悉 C 語言的朋友一眼就能看出來,它是取地址運算符。在 Rust 語言中,“&” 表示借用。借用是用名爲 “Reference(引用)" 的數據結構來實現的,宏觀上可將借用與引用劃等號。引用的數據數據如下所示:

  該數據結構有兩大核心:一是指向某數據的指針(本質上和 C 語言裏的指針一樣);二是附加的指針元數據(meta)。C 語言裏的指針是不帶元數據的,只包括純粹的地址信息,在 Rust 裏我們稱其爲 thin 瘦指針。相對應的,Rust 裏的 Reference 因帶有 meta 信息,我們將其稱爲 fat 胖指針,或者叫做 “smart(智能)” 指針。

  Rust 裏的 Reference 有什麼特別的東西嗎?

  我們多多少少聽說過,C 語言裏的指針很強大,但也伴隨着高風險。首先,C 指針(其它大多數語言也是)可以爲 NULL。NULL 的發明者說它是價值數十億美元的錯誤,考慮到通貨膨脹及深遠的影響,現在肯定不只這麼多了 :D。其次,你可能不確定指針指向的位置是否仍然有效,數據是否仍然還存儲在那個位置上。

  Rust 中的 Reference 不允許爲 NULL,或者說在 Rust 語言中沒有 NULL。此外,Rust 依賴所有權規則,它會保證每一個 Reference 都是有效的,不可能出現引用指向的位置是無效的情況。這麼一看,雖說不是那麼方便,但是還行吧。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/IRG60BLciMbB1gXPDLjesg