Rust 引用與借用

2022-06-07 03:12:09 字數 4040 閱讀 5044

我們的上一遍內容的**有這樣乙個問題:我們必須將string返回給呼叫函式,以便在呼叫calculatelen後仍能使用string,因為string被移動到了calculatelen內。

下面是如何定義並使用乙個(新的)calculatelen函式,它以乙個物件的引用作為引數而不是獲取值的所有權:

fun main(),len:{}", s1, len)

}fn calculatelen(s: &string) ->usize

首先,注意變數宣告和函式返回值中的所有元組**都沒有了。其次,注意我們傳遞&s1給calculatelen,同時在函式定義中,我們獲取&string而不是string。

這些&符號就是引用,它們允許你使用值但不獲取其所有權。以下展示了一張示意圖,&string s 指向 string s1:

注意:與使用 & 引用相反的操作是解引用(dereferencing),它使用解引用運算子,* 。

fn calculatelen(s: &string) -> usize 

//這裡,s離開作用域。但因為它並不擁有引用值的所有權,所以什麼也不會發生。

變數s有效的作用域與函式引數的作用域一樣,不過當引用離開作用域後並不丟棄它指向的資料,因為我們沒有所有權。當函式使用引用而不是實際值作為引數,無需返回值來交還所有權,因為就不曾擁有所有權。

我們將獲取引用作為函式引數稱為借用(borrowing)。正如現實生活中,如果乙個人擁有某樣東西,你可以從他那裡借來。當你使用完比,必須還回去。

如果我們要修改借用的變數呢?是不行的。

正如變數預設是不可變的,引用也一樣。(預設)不允許修改引用的值。

fn main2()

fn change(somestring: &string)

借用規則:當擁有某值的不可變引用時,就不能再獲取乙個可變引用。

可變引用

fn main2()

fn change(somestring: &mut string)

首先,必須將 s 改為 mut。然後必須建立乙個可變引用 &mut s 和接受乙個可變引用 somestring: &mut string。

不過可變引用有乙個很大的限制:在特定作用域中的特定資料只能有乙個可變引用。以下**會失敗:

let mut s = string::from("hello");

let r1 = &mut s;

let r2 = &mut s;

println!("{}",r1);

println!("{}",r2);

錯誤如下:

error[e0499]: cannot borrow `s` as mutable more than once at a time

--> src/main.rs:125:14

|124 | let r1 = &mut s;

| ------first mutable borrow occurs here

125 | let r2 = &mut s;

| ^^^^^^second mutable borrow occurs here

126 | println!("{}",r1);

| -- first borrow later used here

這個限制允許可變性,不過是以一種受限制的方式允計。對於我們的rust新手經常難以適應這一點,因為大部分語言中變數任何時候都是可變的。

這個限制的好處是rust可以在編譯時就避免資料競爭。資料競爭(data race)類似於競態條件,它可由這三個行為造成:

資料競爭會導致未定義行為,難以在執行時追蹤,並且難以論斷和修復;rust避免了這種情況的發生,因為它甚至不會編譯存在資料競爭的**。

fn main()  

//r1 在這裡離開了作用域,所以我們完全可以建立乙個新的引用

let r2 = &mut s;

類似的規則也存在於同時修改用可變與不可變引用中。

let mut s = string::from("hello");

let r1 = &s; //

沒問題let r2 = &s; //

沒問題let r3 = &mut s; //

大問題println!("{}, {}, and {}", r1, r2, r3);

錯誤如下:

error[e0502]: cannot borrow `s` as mutable because it is also borrowed as immutable

--> src/main.rs:126:14

|124 | let r1 = &s; //

沒問題 | --immutable borrow occurs here

125 | let r2 = &s; //

沒問題126 | let r3 = &mut s; //

大問題 | ^^^^^^mutable borrow occurs here

127 | println!("{}, {}, and {}", r1, r2, r3);

| -- immutable borrow later used here

注意乙個引用的作用域從宣告的地方開始一直持續到最後一次使用為止。例如,因為最後一次使用不可變引用在宣告可變引用之前,所以如下**是可以編譯的:

let mut s = string::from("hello");

let r1 = &s; //

沒問題 let r2 = &s; //

沒問題 println!("{}, {}", r1, r2);

//此位置之後,r1和r2不再使用

let r3 = &mut s; //

沒問題 println!("{}", r3);

不可變引用r1和r2的作用域在println!最後一次使用後結束,這也是建立可變引用r3的地方。它們的作用域沒有重疊,所以**是可以編譯的。

儘管這些錯誤有時使人沮喪,但請牢記這是rust編譯器在提前指出乙個潛在的bug(在編譯時而不是在執行時)並精準顯示問題所在。這樣你就不必去跟蹤為何資料並不是你想象中的那樣。

懸垂引用(dangling references)

在具有指標的語言中,很容易通過釋放記憶體時保留指向它的指標而錯誤地生成乙個懸垂指標(dangling pointer),所謂懸垂指標是其指向的記憶體可能已經被分配給其它持有者。相比之下,在rust中編譯器確保引用永遠也不會變成懸垂狀態:當你擁有一些資料的引用,編譯器確保資料不會在其引用之前離開作用域。

讓我們嘗試建立乙個懸垂引用,rust會通過乙個編譯時錯誤來避免:

fn main()

fn dangle() -> &string

這裡是錯誤:

error[e0106]: missing lifetime specifier

--> src/main.rs:138:16

|138 | fn dangle() -> &string //

這裡s離開作用域並被丟棄。其記憶體被釋放。

//危險!

因為s是在dangle函式內建立的,當dangle的**執行完畢後,s將被釋放。不過我們嘗試返回它的引用。這意味著這個引用會指向乙個無效的string,這可是不對的。rust不會允許我們這麼做。

這裡解決方法是直接返回string:

fn dangle() ->string
這樣就沒有任何錯誤了。所有權被移動出去,所以沒有值被釋放。

引用的規則

讓我們概括一下之前對引用的討論:

Rust 引用 借用

二 rust借用 三 可變引用 四 懸垂引用 總結在rust 中的引用是什麼,引用有什麼用。這是我們下面的主題之一。先上 let r1 string from hello world 注意這裡沒有mut let r2 r1 注意這裡沒有mut這個 很神奇,這樣給 r2 賦值以後 不會產生 rust中...

rust學習筆記 引用與借用

fn main is s1,len fn calculate length s string usize 上面的 中,calculate length s string 使用了string的引用傳遞引數,而沒有直接轉移值的所有權。在呼叫函式時也需要傳遞引數的引用。這些 代表的就是引用語義,它們允許你...

rust 語法和語義 10 引用和借用

引用和借用 references and borrowing 所有權概念將依照官方介紹,分為3個部分說明 操作格式 宣告example t引用 物件不可變 vec mut t 引用 物件 可變 mut vec 訪問 可變物件的引用 似乎類似 c 中的取位址操作,獲得乙個指標 似乎類似 c 中的取位址...