引用與借用
fn main() {
let s1 = String::from("abc");
let l = f1(&s1);
println!("s1 = {}, len = {}", s1, l);
}
fn f1(s: &String) -> usize {
s.len()
}
這里將s1
傳給了 f1
函數(shù)犁功,使用的是&s1
,f1
定義的形參類型是&String
绍豁。這些&
符號(hào)就是引用,它們?cè)试S你使用值但不獲取所有權(quán)枷踏。
&s
語法 讓我們創(chuàng)建了一個(gè)指向s1
的引用驳规,但是不擁有它揪胃。因?yàn)椴粨碛羞@個(gè)值,在離開作用域時(shí)也不需要drop
特殊操作缰猴。
我們將獲取引用作為函數(shù)參數(shù)稱為借用产艾。正如變量默認(rèn)是不可邊的,應(yīng)用也是一樣滑绒。
fn f1(s: &String) -> usize {
s.push_str("hello"); // 這里將發(fā)生錯(cuò)誤胰舆,嘗試修改不可變的借用的值
s.len();
}
與運(yùn)算符
&
引用相反的操作是*
解引用
可變引用
fn main() {
let mut s = String:from("hello");
change(&mut s);
}
fn change(s: &mut String) {
s.push_str(", world");
}
首選將s
改為 mut
的,然后必須創(chuàng)建一個(gè)可變引用&mut s
和接受一個(gè)可變引用s: &mut String
蹬挤。
不過可邊引用有很大的限制缚窿,在特定作用域的特定數(shù)據(jù)有且只有一個(gè)可變引用。
let mut s1 = String::from("hello");
let s2 = &mut s1;
let s3 = &mut s1;
println!("{}, {}", s2, s3);
這里將報(bào)錯(cuò)焰扳,這個(gè)限制的好處是Rust
在編譯時(shí)就避免數(shù)據(jù)競(jìng)爭(zhēng)倦零。數(shù)據(jù)競(jìng)爭(zhēng)類似于競(jìng)態(tài)條件。它由這三個(gè)條件造成:
- 兩個(gè)或更多指針同時(shí)訪問同一數(shù)據(jù)
- 至少有一個(gè)指針被用來寫數(shù)據(jù)
- 沒有同步數(shù)據(jù)訪問的機(jī)制
數(shù)據(jù)競(jìng)爭(zhēng)會(huì)導(dǎo)致未定義行為吨悍,難以在運(yùn)行時(shí)追蹤扫茅,且難以診斷和恢復(fù)。Rust
避免了這種情況發(fā)生育瓜,因?yàn)樗踔敛粫?huì)編譯存在數(shù)據(jù)競(jìng)爭(zhēng)的代碼葫隙。
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 離開了作用域,可以創(chuàng)建一個(gè)新的引用
let r2 = &mut s;
類似的規(guī)則也存在于可變引用和不可變引用中躏仇。
let mut s = String::from("hello");
let r1 = &s; // 可以
let r2 = &s; // 可以
let r3 = &mut s; // 不行
Rust
不允許不可變引用和可變引用同時(shí)指向一個(gè)值恋脚,不可變引用不希望再讀取值時(shí)被修改腺办。多個(gè)不可變引用是可以的。
懸垂引用
在具有指針的語言中糟描,很容易變量指向一個(gè)已釋放的內(nèi)存怀喉,而錯(cuò)誤的產(chǎn)生一個(gè) 懸垂指針。嘗試創(chuàng)建一個(gè)懸垂指針:
fn main() {
let s = f1();
}
fn f1() -> &String {
let s = String::from("hello"); // 創(chuàng)建字符串船响,分配內(nèi)存
&s // 返回字符串的引用
} // s 離開作用域躬拢,釋放內(nèi)存
當(dāng)s
離開作用域時(shí),s
將被釋放见间。嘗試返回s
的引用聊闯,Rust
不允許這么做,這里將發(fā)生錯(cuò)誤米诉。
引用的規(guī)則
總結(jié):
- 在任意給定時(shí)間馅袁,要么只能有一個(gè)可變應(yīng)用,要么只能是多個(gè)不可變引用
- 引用必須總是有效