未定义的引用_Rust 引用和借阅
清單4-5中的元組代碼的問題在于,我們必須String將調用返回 給調用函數,因此我們仍然可以String在調用to之后使用calculate_length,因為將String移到了 calculate_length。
這是定義和使用calculate_length將對象的引用作為參數的函數而不是獲取值所有權的方法:
文件名:src / main.rs
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len);}fn calculate_length(s: &String) -> usize { s.len()}首先,請注意,變量聲明和函數返回值中的所有元組代碼都消失了。其次,請注意,我們&s1進入 calculate_length,在定義上,我們接受&String而不是 String。
這些與符號是reference,它們使您可以引用某些值而無需擁有所有權。圖4-5顯示了一個示意圖。
圖4-5:&String s指向String s1
注意:使用&進行引用的反義詞是解引用,這是通過解引用運算符來完成的*。我們將在第8章中看到解引用運算符的一些用法,并在第15章中討論解引用的細節。
讓我們在這里仔細看一下函數調用:
let s1 = String::from("hello"); let len = calculate_length(&s1);該&s1語法讓我們創建一個基準是指它的值s1 ,但不擁有它。因為它不擁有它,所以當引用超出范圍時,它所指向的值將不會被刪除。
同樣,函數的簽名&用于指示參數的類型s是引用。讓我們添加一些解釋性注釋:
fn calculate_length(s: &String) -> usize { // s is a reference to a String s.len()} // Here, s goes out of scope. But because it does not have ownership of what // it refers to, nothing happens.變量s有效的作用域與任何函數參數的作用域相同,但是當變量超出作用域時我們不會刪除引用指向的內容,因為我們沒有所有權。當函數將引用作為參數而不是實際值作為參數時,我們將不需要返回這些值以歸還所有權,因為我們從未擁有過所有權。
我們稱將引用作為函數參數借用。與現實生活中一樣,如果某人擁有某物,則可以向他們借用。完成后,您必須將其歸還。
那么,如果我們嘗試修改要借用的內容會怎樣?嘗試清單4-6中的代碼。劇透警報:它不起作用!
文件名:src / main.rs
fn main() { let s = String::from("hello"); change(&s);}fn change(some_string: &String) { some_string.push_str(", world");}清單4-6:嘗試修改借入的值
這是錯誤的結果:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference --> src/main.rs:8:5 |7 | fn change(some_string: &String) { | ------- help: consider changing this to be a mutable reference: `&mut std::string::String`8 | some_string.push_str(", world"); | ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutableerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0596`.error: could not compile `ownership`.To learn more, run the command again with --verbose.正如變量在默認情況下是不可變的一樣,引用也是如此。我們不允許修改引用的內容。
可變引用
我們只需稍作調整就可以解決清單4-6中代碼中的錯誤:
文件名:src / main.rs
fn main() { let mut s = String::from("hello"); change(&mut s);}fn change(some_string: &mut String) { some_string.push_str(", world");}首先,我們必須更改s為mut。然后,我們必須使用創建一個可變引用,&mut s并使用接受一個可變引用some_string: &mut String。
但是可變引用有一個很大的限制:您只能在一個特定范圍內對一個特定的數據進行一個可變引用。此代碼將失敗:
文件名:src / main.rs
let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; println!("{}, {}", r1, r2);這是錯誤:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/main.rs:5:14 |4 | let r1 = &mut s; | ------ first mutable borrow occurs here5 | let r2 = &mut s; | ^^^^^^ second mutable borrow occurs here6 | 7 | println!("{}, {}", r1, r2); | -- first borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0499`.error: could not compile `ownership`.To learn more, run the command again with --verbose.該限制允許突變,但是以非常受控的方式。這是新的Rustaceans苦苦掙扎的事情,因為大多數語言都允許您隨時更改。
具有此限制的好處是Rust可以防止在編譯時發生數據爭用。一個數據的比賽相似,競爭條件,當這三種行為的發生情況:
- 兩個或多個指針同時訪問相同的數據。
- 至少有一個指針用于寫入數據。
- 沒有用于同步對數據的訪問的機制。
數據爭用會導致未定義的行為,并且在嘗試在運行時進行跟蹤時可能會難以診斷和修復。Rust阻止了此問題的發生,因為它甚至不會與數據競爭一起編譯代碼!
與往常一樣,我們可以使用大括號創建新的范圍,從而允許多個可變引用,而不能同時引用:
let mut s = String::from("hello"); { let r1 = &mut s; } // r1 goes out of scope here, so we can make a new reference with no problems. let r2 = &mut s;對于組合可變引用和不可變引用,存在類似的規則。此代碼導致錯誤:
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem let r3 = &mut s; // BIG PROBLEM println!("{}, {}, and {}", r1, r2, r3);這是錯誤:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> src/main.rs:6:14 |4 | let r1 = &s; // no problem | -- immutable borrow occurs here5 | let r2 = &s; // no problem6 | let r3 = &mut s; // BIG PROBLEM | ^^^^^^ mutable borrow occurs here7 | 8 | println!("{}, {}, and {}", r1, r2, r3); | -- immutable borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0502`.error: could not compile `ownership`.To learn more, run the command again with --verbose.ew!當我們擁有不變的參考時,我們也不能擁有可變的參考。不變引用的用戶不要期望值會突然從它們下面改變!但是,可以使用多個不可變的引用,因為沒有人會影響其他任何人對數據的讀取。
請注意,引用的范圍從引入它的地方開始,一直持續到最后一次使用該引用。例如,此代碼將被編譯,因為不可變引用的最后一次使用發生在引入可變引用之前:
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem println!("{} and {}", r1, r2); // r1 and r2 are no longer used after this point let r3 = &mut s; // no problem println!("{}", r3);不可改變的引用的范圍r1和r2結束后println! ,他們最后被使用,這是可變的引用之前r3被創建。這些范圍不重疊,因此允許使用此代碼。
即使借用錯誤有時可能令人沮喪,但請記住,Rust編譯器盡早(在編譯時而不是在運行時)指出了潛在的錯誤,并確切地指出了問題出在哪里。然后,您不必追蹤為什么您的數據與您想象的不一樣。
懸掛參考
在帶有指針的語言中,很容易錯誤地創建一個懸空指針,即通過在保留指向該內存的指針的同時釋放一些內存來引用可能已分配給他人的內存中某個位置的指針。相比之下,在Rust中,編譯器保證引用永遠不會成為懸掛引用:如果您對某些數據有引用,則編譯器將確保數據不會超出對數據的引用范圍。
讓我們嘗試創建一個懸空的引用,Rust將通過編譯時錯誤防止它:
文件名:src / main.rs
fn main() { let reference_to_nothing = dangle();}fn dangle() -> &String { let s = String::from("hello"); &s}這是錯誤:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0106]: missing lifetime specifier --> src/main.rs:5:16 |5 | fn dangle() -> &String { | ^ help: consider giving it a 'static lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed fromerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0106`.error: could not compile `ownership`.To learn more, run the command again with --verbose.此錯誤消息引用的是我們尚未涵蓋的功能:生存期。我們將在第10章中詳細討論生命周期。但是,如果您忽略有關生命周期的部分,則消息的確包含了導致此代碼出現問題的關鍵:
this function's return type contains a borrowed value, but there is no valuefor it to be borrowed from.讓我們仔細看看dangle代碼的每個階段到底發生了什么 :
文件名:src / main.rs
fn dangle() -> &String { // dangle returns a reference to a String let s = String::from("hello"); // s is a new String &s // we return a reference to the String, s} // Here, s goes out of scope, and is dropped. Its memory goes away. // Danger!因為s是在內部創建的dangle,當代碼dangle完成時, s將被釋放。但是我們試圖返回對它的引用。這意味著此引用將指向無效String。那不好!Rust不會讓我們這樣做。
這里的解決方案是String直接返回:
fn no_dangle() -> String { let s = String::from("hello"); s}這可以正常工作。所有權被移出,沒有任何東西被釋放。
總結
以上是生活随笔為你收集整理的未定义的引用_Rust 引用和借阅的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页设计作业_Dreamweaver简单
- 下一篇: uniapp动态显示数组_uni-app