天天看點

Rust 程式設計學習筆記Day 5 -借用or引用 Borrow語義

作者:碼上讀書

昨天(day4) 我們一起學習了Copy語義,在進行變量指派,傳參,函數傳回時,如果變量的資料結構實作了Copy trait,就自動使用Copy語義,否則就使用Move語義 轉移所有權,後面無法通路該變量。

還有一些資料結構既沒有實作Copy trait,也不想轉移所有權。這時候就需要用到今天的主角--Borrow語義。

Borrow語義

從名字就不難看出,變量的所有權并不會發生轉移,就可以被其他上下文借用了。

就像小時借同桌的橡皮,

長大了借房東的房子(要付錢)。

過年回家借女友,來應對親戚催婚的問題。

很明顯借來的,并沒有所有權,隻擁有臨時使用權。

Borrow 語義通過引用文法(& 或者 &mut)來實作。

其實,在 Rust 中,“借用”和“引用”是一個概念,隻不過在其他語言中引用的意義和 Rust 不同,是以 Rust 提出了新概念“借用”,便于區分。(你到是便于區分了,對于我們這些新手有點制造概念,增加學習成本了。我按照你的套路了解一大圈,然後你告訴我借用就是引用,就這?)。

預設情況下,Rust的借用都是隻讀的,就好像借來的房子,退租的時候,裡面不能少東西,也不能把人家原來的裝修風格變了。當然借來的女友,也不能被寫入任何資料。

是以,如果我們想避免 Copy 或者 Move,可以使用借用,或者說引用。

隻讀借用/隻讀引用

我們在學習其他語言的時候,函數傳參一般方式有:傳值,傳引用。

Rust 程式設計學習筆記Day 5 -借用or引用 Borrow語義

但在Rust中沒有傳引用的概念,Rust所有參數傳遞都是傳值,包括Copy和Move。

在Rust中,必須顯式地把某個資料的引用,傳給另一個函數。(我了解:傳值就是copy棧上的值,不論棧上存的是“指針/位址”還是“值”。)

Rust的引用實作了Copy trait(疑問:隻讀借用,可變借用都實作了嗎?)

是以按照 Copy 語義,這個引用會被複制一份交給要調用的函數。對這個函數來說,它并不擁有資料本身,資料隻是臨時借給它使用,所有權還在原來的擁有者那裡。

在 Rust 裡,引用是一等公民,和其他資料類型地位相等。

用之前的錯誤代碼來示範

fn main() {
    let data = vec![1, 2, 3, 4];
    let data1 = data;
    println!("sum of data1: {}", sum(data1));
    println!("data1: {:?}", data1); // error1
    println!("sum of data: {}", sum(data)); // error2
}
fn sum(data: Vec<u32>) -> u32 {
    data.iter().fold(0, |acc, x| acc + x)
}
           

通過調整成借用的方式,通過編譯,并檢視值和引用的位址。

fn main() {
    let data = vec![1, 2, 3, 4];
    let data1 = &data;
    // 值的位址是什麼?引用的位址又是什麼?
    println!(
        "addr of value: {:p}({:p}), addr of data {:p}, data1: {:p}",
        &data, data1, &&data, &data1
    );
    println!("sum of data1: {}", sum(data1));
    // 堆上資料的位址是什麼?
    println!(
        "addr of items: [{:p}, {:p}, {:p}, {:p}]",
        &data[0], &data[1], &data[2], &data[3]
    );
}
fn sum(data: &Vec<u32>) -> u32 {
    // 值的位址會改變麼?引用的位址會改變麼?
    println!("addr of value: {:p}, addr of ref: {:p}", data, &data);
    data.iter().fold(0, |acc, x| acc + x)
}
           

先别着急run,我們先想一下,data值的位址是否不變,而data1引用的位址,在傳給sum()函數後,是否指向同一個位址。

3,2,1.

我們來驗證一下,心裡的答案 正确與否。

Rust 程式設計學習筆記Day 5 -借用or引用 Borrow語義

可以看到data1,&data,以及sum()裡的data1'都指向data的ptr。這個值是不變的。

但是每個引用的位址都不一樣,這是因為上面說到的隻讀借用實作了Copy trait。

也就意味着引用的指派(data1 = &data)

引用的傳參(sum(data1))

都會産生新的淺拷貝。

現在我們看到雖然有多個隻讀引用指向了data,但在堆上的真實資料而言,它隻屬于data一個人,是以值的多個引用,并不影響所有權的唯一性。

那麼問題來了!一旦data離開了作用域,被釋放了。還有指向data的引用,那就會變成我們想避免的懸挂指針了?類似Golang裡的逃逸?

我們明天接着聊!我是老張一個陪你成長的碼農。

《陳天 · Rust 程式設計第一課》學習筆記Day 5

如果願意繼續聽老張叨叨,可以幫忙提點意見,點個關注。

讓老張更有動力繼續下去。

繼續閱讀