唯一的區別就是對象的字面形式仍然需要使用“,”來分割元素,而class語法不需要。。。。
通過前面的學習,我們知道了所有權可以有效避免程序在運行期間執行某些無效操作,對于某些我們希望可以寬容的繞過所有權的嚴格處理的方法就是——讓借出去的所有權歸還給自己。但是上篇對此所提到的做法并不優雅,實在是繁瑣,看看下面的邋遢的代碼段:
fn main(){
let mut v1:Vec<i32> = vec![1,2,3];
let mut v2:Vec<i32> = vec![4,5,6];
/*
**let answer:i32;
**(v1, v2, answer) = giveBack(v1, v2);
**error[E0070]: invalid left-hand side expression
*/
let (v1, v2, answer) = giveBack(v1, v2);
for num1 in v1{println!("{}", num1);}
for num2 in v2{println!("{}", num2);}
println!("{}", answer);
}
fn giveBack(v1:Vec<i32>, v2:Vec<i32>) -> (Vec<i32>, Vec<i32>, i32){
println!("give back to you!");
(v1, v2, 99)
}
就像看上去的那樣,而且如果利用模式賦值的話貌似左邊必須出現一個let?暫且拋開這個問題不提,下面聊聊rust中的借用和引用。
1.使用借用
我們可以使用借用改寫一下上面的那個例子:
fn main(){
let v1:Vec<i32> = vec![1,2,3];
let v2:Vec<i32> = vec![4,5,6];
let answer:i32 = borrow(&v1, &v2);
println!("{}", answer);
for num1 in v1{
println!("{}", num1);
}
for num2 in v2{
println!("{}", num2);
}
}
fn borrow(v1:&Vec<i32>, v2:&Vec<i32>) -> i32{
99
}
通過上面的實際例子,我們就可以發現是使用借用的確是對所有權系統的一個很好的補充。當我們使用借用的時候,我們利用借用得到的變量對資源所做的改變會實際影響原先的變量,而且盡管我們利用借用所得到的變量會進入某些塊作用域中,但是在離開這個塊作用域的時候并不會使得借用變量所綁定的存儲在堆上的資源被銷毀。下面看個更加復雜的關于借用的例子:
fn main(){
let vec1:Vec<i32> = vec![0; 20];
let vec2:Vec<i32> = vec![0; 70];
let total:usize = fun2(&vec1, &vec2);
println!("{}", total);//90
for num1 in vec1{
print!("{} ", num1);//0 0 0 0 0.....
}
println!("");
for num2 in vec2{
print!("{} ", num2);//0 0 0 0 .....
}
}
fn fun1(v:&Vec<i32>) -> usize{
v.len()
}
fn fun2(v1:&Vec<i32>, v2:&Vec<i32>) -> usize{
let length1:usize = fun1(v1);
let length2:usize = fun1(v2);
length1 + length2
}
題外話:vector數據類型不僅索引的數據類型是usize,而且利用對象方法len()獲得長度其數據類型也為usize。這里的問題是這樣的:如果把上面的代碼改成下面的樣子的話也能夠成功運行:
fn main(){
let vec1:Vec<i32> = vec![0; 20];
let vec2:Vec<i32> = vec![0; 70];
let total:usize = fun2(&vec1, &vec2);
println!("{}", total);//90
for num1 in vec1{
print!("{} ", num1);//0 0 0 0...
}
println!("");
for num2 in vec2{
print!("{} ", num2);//0 0 0 0 0...
}
}
fn fun1(v:&Vec<i32>) -> usize{
v.len()
}
fn fun2(v1:&Vec<i32>, v2:&Vec<i32>) -> usize{
let length1:usize = fun1(&v1);
let length2:usize = fun1(&v2);
length1 + length2
}
通過上面的例子。我們能夠知道:對于fun2函數中的局部變量v1以及v2來說,他們都是引用數據類型的變量;但是為什么可以對一個引用再次利用&得到他的引用呢??
2.借用類型也是默認不可變的
對于一個借用數據類型的變量來說,默認情況下,我們不能夠對其進行任何會改變其值的行為,因為借用數據類型變量也是默認不可變的。但是如果你實在希望實現對借用類型進行值更改的操作的話,那么你只需要對其加上mut修飾就足夠了。但是對于希望函數的形參是一個可變的借用類型的話,那么不僅需要在聲明形參的時候像下面這樣聲明:variableName:&mut type,而且傳入的實參也得像下面這樣:&mut variableName。由此就要求variableName這個變量本身就得是可變的。下面舉一個例子:
fn main(){
let mut v1:Vec<i32> = vec![1,2,3];
let len:usize = borrow(&mut v1);
println!("{}", len);//4
for num in v1{
print!("{} ", num);//1 2 3 9
}
}
fn borrow(v:&mut Vec<i32>) -> usize{
v.push(9);
v.len()
}
3.關于借用所必須要注意的問題
- 1.不可變借用也好,可變借用也罷,他們都得必須位于比擁有者更小的作用域。舉一個例子:
//例子拋出錯誤:error: `num` does not live long enough
fn main(){
let borrow:&i32;
let num:i32 = 9;
borrow = #// 1 borrow occurs here
}//num dropped here while still borrowed
為什么借用必須位于比擁有者更小的作用域呢?這里做一個猜測:我們知道普通變量是存儲在棧里面的,而對于棧這種數據結構來說,先進后出是他的標志性特征,所以對于上面的例子來說borrow變量一定是后于num變量被銷毀的,在位置1處,borrow變量開始借用num,但是隨之就是main函數的結束,所以里面的變量都會被銷毀,根據銷毀順序那么也應該是先銷毀num,但是此時borrow還存在著,他跟num是綁定著的,所以將num銷毀的話,將會讓borrow找不著北 ,所以這種情況下會報錯。但是如果是先將borrow刪除的話,那么就是OK的。
- 2.在同一個作用域下,對同一個資源不能同時存在不可變引用和可變引用。
//拋出錯誤:error[E0502]: cannot borrow `source` as mutable because it is also borrowed as immutable
fn main(){
let mut source:i32 = 9;
let immutable_borrow:&i32 = &source;
let mutable_borrow:&mut i32 = &mut source;
}
從上面的例子,我們還能夠看出另外一點特性:那就是對于一個可變的變量來說,我們既可以為其創建不可變的引用;也可以為他創建可變的引用——當然了在同一個作用域中是不能夠同時出現的。但是對于一個不可變的變量來說,你就不能夠為其創建一個可變的引用。言歸正傳,回到拋出的錯誤:error[E0502]: cannot borrow source
as mutable because it is also borrowed as immutable,錯誤提示已經很明顯了,就是說對于同一個資源來說不能夠同時對其的可變引用和不可變引用。那么這是為什么呢?對于可變引用,他可以改變引用的資源內容,但是對于我們的不可變引用來說,很顯然他接受不到資源的實時變化,而對于這種情況顯然是注重安全的rust所不能夠接受的,所以說這兩種引用不可以在同一塊作用域對同一個資源進行綁定。
- 3.在同一個作用域下,對同一個資源只能夠有一個可變引用。
//拋出錯誤:error[E0499]: cannot borrow `num` as mutable more than once at a time
fn main(){
let mut num:i32 = 9;
let mutable_borrow1:&mut i32 = &mut num;
let mutable_borrow2:&mut i32 = &mut num;
}
從拋出的錯誤信息來看,我們也可以很直觀的看出那就是對于同一個資源來說在同一塊作用域不能夠有多個可變引用。就目前而言,我并知道為什么rust需要這樣設計,以后在做解答吧。
- 4.在同一個作用域下,雖然不能夠對同一個資源有多個可變引用,但是對同一個資源有多個不可變引用還是可以的。
fn main(){
let mut num:i32 = 9;
let immutable_borrow1:&i32 = #
let immutable_borrow2:&i32 = #
}
對于這種情況,只能夠用情理之中來形容,所以不解釋。
4.隱式的不可變借用
直接看代碼吧,反正目前為止也解釋不了,記住即可:
fn main(){
let mut num:i32 = 9;
println!("{}", num);//輸出9 位置1
}
別小看了上面那乍看起來貌似簡單的代碼,其實吧它上面隱藏了某些細節,就比如說位置1處的代碼,其實在那里的num訪問其實是一種不可變的引用?
可以看一個應用例子:
//error[E0502]: cannot borrow `num` as immutable because it is also borrowed as mutable
fn main(){
let mut num:i32 = 9;
let mutable_borrow1:&mut i32 = &mut num;//mutable borrow accurs here
println!("{}", num);//immutable borrow accurs here
}
上面這個例子拋出的錯誤信息也很明顯,說明了對于一條簡單的println!()語句中出現的變量來說,從更深層次角度來看是表明了這個變量是對資源的不可變式的引用。既然如此的話,由于在rust中規定了不能夠在同一個作用域中出現對同一塊資源即出現可變引用又出現不可變引用,所以這個例子會拋出錯誤。
當然,修改這個例子也很簡單,因為是由于在某一個時間段中即出現對同一個資源的可變引用又出現了不可變引用,所以我們對此做的補救措施就是將其中一個引用給銷毀,所以應該銷毀先出現的那個引用,怎么銷毀?利用一個塊作用域嘛,離開作用域后即自動被銷毀了。:
fn main(){
let mut num:i32 = 8;
{
let mutable_borrow1:&mut i32 = &mut num;
*mutable_borrow1 += 1;
}
println!("{}", num);//9
}
如上所示,即達到了我們所希望看到的結果。