?可變引用
在上一篇文章中,我們提到了借用的概念,將獲取引用作為函數(shù)參數(shù)稱為?借用(borrowing),通常情況下,我們無法修改借來的變量
,但是可以通過可變引用實(shí)現(xiàn)修改借來的變量
。代碼示例如下:
fn main() {
let mut s = String::from("hello"); // s是可變的變量
change(&mut s); // &mut 表示可變引用
}
fn change(some_string: &mut String) { // &mut 表示可變引用
some_string.push_str(", world");
}
要想實(shí)現(xiàn)修改借來的變量
就必須將?s
改為?mut
。然后必須創(chuàng)建一個(gè)可變引用?&mut s
和接受一個(gè)可變引用?some_string: &mut String
。
但是可變引用有一個(gè)很大的限制:在特定作用域中的特定數(shù)據(jù)只能有一個(gè)可變引用。比如下述代碼就不會(huì)被成功編譯。
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
}
編譯運(yùn)行就會(huì)拋出如下異常:
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 here
5 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", r1, r2);
| -- first borrow later used here
所以這種修改借來的變量
的可變引用是以一種受限制的方式允許修改,這個(gè)限制的好處是 Rust 可以在編譯時(shí)就避免數(shù)據(jù)競爭。數(shù)據(jù)競爭(data race)類似于競態(tài)條件,它可由這三個(gè)行為造成:
- 兩個(gè)或更多指針同時(shí)訪問同一數(shù)據(jù)。
- 至少有一個(gè)指針被用來寫入數(shù)據(jù)。
- 沒有同步數(shù)據(jù)訪問的機(jī)制。
數(shù)據(jù)競爭會(huì)導(dǎo)致未定義行為,難以在運(yùn)行時(shí)追蹤,并且難以診斷和修復(fù);Rust 避免了這種情況的發(fā)生。我們可以使用{}
創(chuàng)建一個(gè)新的作用域,這樣就能夠允許多個(gè)可變引用了,只是不能在同一個(gè)作用域中同時(shí)擁有:
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 在這里離開了作用域,所以我們完全可以創(chuàng)建一個(gè)新的引用
let r2 = &mut s;
}
另外還需要注意的是,不能在擁有不可變引用的同時(shí)擁有可變引用。不可變引用的用戶可不希望在他們的眼皮底下值就被意外的改變了!但是多個(gè)不可變引用是可以的,因?yàn)闆]有哪個(gè)只能讀取數(shù)據(jù)的人有能力影響其他人讀取到的數(shù)據(jù)。如下述代碼:
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 沒問題
let r2 = &s; // 沒問題
let r3 = &mut s; // 在擁有不可變引用的同時(shí)擁有可變引用
println!("{}, {}, and {}", r1, r2, r3);
}
上面代碼示例編譯時(shí)會(huì)拋出如下異常:
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 here
5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM
| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{}, {}, and {}", r1, r2, r3);
| -- immutable borrow later used here
但是如果可變引用和不可變引用他們的作用域不重疊代碼就是可以編譯的,我們可以將上面的代碼示例進(jìn)行修改就可以正常運(yùn)行了。
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 沒問題
let r2 = &s; // 沒問題
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用
let r3 = &mut s; // 沒問題
println!("{}", r3);
}
懸垂引用(Dangling References)
在具有指針的語言中,很容易通過釋放內(nèi)存時(shí)保留指向它的指針而錯(cuò)誤地生成一個(gè)?懸垂指針(dangling pointer),所謂懸垂指針是其指向的內(nèi)存可能已經(jīng)被分配給其它持有者。相比之下,在 Rust 中編譯器確保引用永遠(yuǎn)也不會(huì)變成懸垂?fàn)顟B(tài):當(dāng)你擁有一些數(shù)據(jù)的引用,編譯器確保數(shù)據(jù)不會(huì)在其引用之前離開作用域。
當(dāng)我們不小心創(chuàng)建了懸垂引用,Rust在編譯的時(shí)候就會(huì)拋出異常:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String { // dangle 返回一個(gè)字符串的引用
let s = String::from("hello"); // s 是一個(gè)新字符串
&s // 返回字符串 s 的引用
}// 這里 s 離開作用域并被丟棄。其內(nèi)存被釋放。
因?yàn)?s
是在?dangle
函數(shù)內(nèi)創(chuàng)建的,當(dāng)?dangle
的代碼執(zhí)行完畢后,s
將被釋放。不過我們嘗試返回它的引用。這意味著這個(gè)引用會(huì)指向一個(gè)無效的?String
,所以在編譯時(shí)Rust就會(huì)拋出異常,解決方式就是直接返回String
。
fn no_dangle() -> String {
let s = String::from("hello");
s
} // 所有權(quán)被移動(dòng)出去,內(nèi)存沒有被釋放
最后感謝每一個(gè)認(rèn)真閱讀我文章的人,禮尚往來總是要有的,這些資料,對于【軟件測試】的朋友來說應(yīng)該是最全面最完整的備戰(zhàn)倉庫,雖然不是什么很值錢的東西,如果你用得到的話可以直接拿走:
這些資料,對于【軟件測試】的朋友來說應(yīng)該是最全面最完整的備戰(zhàn)倉庫,這個(gè)倉庫也陪伴上萬個(gè)測試工程師們走過最艱難的路程,希望也能幫助到你!文章來源:http://www.zghlxwxcb.cn/news/detail-814305.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-814305.html
到了這里,關(guān)于Rust - 可變引用和懸垂引用的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!