引用&借用(References&Borrowing)
如上所示,Owership讓我們改變一個變量的值變得“複雜”,那能否像其他編程語言那樣隨意改變變量的值呢?答案是有的。
所有權系統允許我們通過“Borrowing”的方式達到這個目的。這個機制非常像其他編程語言中的“讀寫鎖”,即同一時刻,只能擁有一個“寫鎖”,或只能擁有多個“讀鎖”,不允許“寫鎖”和“讀鎖”在同一時刻同時出現。當然這也是數據讀寫過程中保障一致性的典型做法。只不過Rust是在編譯中完成這個(Borrowing)檢查的,而不是在運行時,這也就是為什麼其他語言程序在運行過程中,容易出現死鎖或者野指針的問題。
通過&符號完成Borrowing:
fn main() {
let x: Vec<i32> = vec!(1i32, 2, 3);
let y = &x;
println!("x={:?}, y={:?}", x, y);
}
Borrowing(&x)並不會發生所有權moved,所以println可以同時訪問x和y。 通過引用,就可以對普通類型完成修改。
fn main() {
let mut x: i32 = 100;
{
let y: &mut i32 = &mut x;
*y += 2;
}
println!("{}", x);
}
借用與引用的區別
借用與引用是一種相輔相成的關係,若B是對A的引用,也可稱之為B借用了A。
很相近對吧,但是借用一詞本意為要歸還。所以在Rust用引用時,一定要注意應該在何處何時正確的“歸回”借用/引用。 最後面的“高級”小節會詳細舉例。
規則
- 同一作用域,特定數據最多隻有一個可變借用(&mut T),或者2。
- 同一作用域,特定數據可有0個或多個不可變借用(&T),但不能有任何可變借用。
- 借用在離開作用域後釋放。
- 在可變借用釋放前不可訪問源變量。
可變性
Borrowing也分“不可變借用”(默認,&T)和“可變借用”(&mut T)。
顧名思義,“不可變借用”是隻讀的,不可更新被引用的內容。
fn main() {
let x: Vec<i32> = vec!(1i32, 2, 3);
//可同時有多個不可變借用
let y = &x;
let z = &x;
let m = &x;
//ok
println!("{:?}, {:?}, {:?}, {:?}", x, y, z, m);
}
再次強調下,同一作用域下只能有一個可變借用(&mut T),且被借用的變量本身必須有可變性 :
fn main() {
//源變量x可變性
let mut x: Vec<i32> = vec!(1i32, 2, 3);
//只能有一個可變借用
let y = &mut x;
// let z = &mut x; //錯誤
y.push(100);
//ok
println!("{:?}", y);
//錯誤,可變借用未釋放,源變量不可訪問
// println!("{:?}", x);
} //y在此處銷燬
高級例子
下面的複雜例子,進行了詳細的註釋,即使看不懂也沒關係,可以在完成Lifetimes(生命週期)的學習後再仔細思考本例子。
fn main() {
let mut x: Vec<i32> = vec!(1i32, 2, 3);
//更新數組
//push中對數組進行了可變借用,並在push函數退出時銷燬這個借用
x.push(10);
{
//可變借用1
let mut y = &mut x;
y.push(100);
//可變借用2,注意:此處是對y的借用,不可再對x進行借用,
//因為y在此時依然存活。
let z = &mut y;
z.push(1000);
println!("{:?}", z); //打印: [1, 2, 3, 10, 100, 1000]
} //y和z在此處被銷燬,並釋放借用。
//訪問x正常
println!("{:?}", x); //打印: [1, 2, 3, 10, 100, 1000]
}
總結
- 借用不改變內存的所有者(Owner),借用只是對源內存的臨時引用。
- 在借用週期內,借用方可以讀寫這塊內存,所有者被禁止讀寫內存;且所有者保證在有“借用”存在的情況下,不會釋放或轉移內存。
- 失去所有權的變量不可以被借用(訪問)。
- 在租借期內,內存所有者保證不會釋放/轉移/可變租借這塊內存,但如果是在非可變租借的情況下,所有者是允許繼續非可變租借出去的。
- 借用週期滿後,所有者收回讀寫權限
- 借用週期小於被借用者(所有者)的生命週期。
備註: 借用週期,指的是借用的有效時間段。