[易學易懂系列|rustlang語言|零基礎|快速入門|(4)|借用Borrowing]


[易學易懂系列|rustlang語言|零基礎|快速入門|(4)]

Borrowing

繼續講講另一個重要的概念:借用(borrowing),

什么是借用?

我們先來看前一文章([易學易懂系列|rustlang語言|零基礎|快速入門|(3)])的代碼 :

 let a = [1, 2, 3];

let b = a;

println!("{:?} {:?}", a, b); *// [1, 2, 3] [1, 2, 3]*

let a = vec![1, 2, 3];

let b = a;

println!("{:?} {:?}", a, b); *// Error; use of moved value: `a`*

我們從上篇文章知道,第二段代碼會報錯,那怎么才能不報錯呢?

我們改成以下代碼:

 let a = vec![1, 2, 3];

let b = **&**a;//這里加了一個符號:**&**,表示借用

println!("{:?} {:?}", a, b); *// correct*

現在可以順利通過傲嬌的編程器女王的檢查了!這就是“借用”的功效!

這里就出來一個rust語言的概念,叫借用(borrowing)。

來看下定義:

英語: Borrow (verb) To receive something with the promise of returning it.

翻譯成中文:出來混,借了東西,遲早要還的!

那借用又分兩類型:

1.共享借用(Shared Borrowing (&T)

數據可以借用給一個或多個用戶(或線程),但只准一個用戶修改。

2.可變借用(Mutable Borrowing (&mut T)

數據可以借用給一個用戶,並只准這個用戶修改,同時不准其他用戶訪問。

借用規則如下 :

1.數據同一時間,只能是其中一種借用,要么是共享借用(Shared Borrowing (&T)),要么是可變借用(Mutable Borrowing (&mut T))。

2.借用概念適用於復制類型(Copy type )和移動類型( Move type )。

請看如下代碼:

fn main() {
 let mut a = vec![1, 2, 3];
 let b = &mut a;  // &mut borrow of `a` starts here
                  // ⁝
 // some code     // ⁝
 // some code     // ⁝
}                  // &mut borrow of `a` ends here


fn main() {
 let mut a = vec![1, 2, 3];
 let b = &mut a;  // &mut borrow of `a` starts here
 // some code

 println!("{:?}", a); // trying to access `a` as a shared borrow, so giving an error
}                  // &mut borrow of `a` ends here


fn main() {
 let mut a = vec![1, 2, 3];
{
   let b = &mut a;  // &mut borrow of `a` starts here
   // any other code
}                  // &mut borrow of `a` ends here

 println!("{:?}", a); // allow borrowing `a` as a shared borrow
}

從上面代碼,我們可以看出,借用,也是有“生命周期”的。

像這段代碼 :

fn main() {  
   let mut a = vec![1, 2, 3];
   let b = &mut a; // &mut borrow of `a` starts here // some code

   println!("{:?}", a); // trying to access `a` as a shared borrow, so giving                         //an error
} // &mut borrow of `a` ends here

為什么會報錯?因為當最后一行代碼:

 println!("{:?}", a);

要訪問a時,a對數據的所有權,已經借用給b了。a已經沒有數據所有權。所以報錯。

那要怎么辦?

加上大括號“{}”。

如下 :

 let mut a = vec![1, 2, 3];
{
   let b = &mut a;  // &mut borrow of `a` starts here
   // any other code
}                  // &mut borrow of `a` ends here

 println!("{:?}", a); // allow borrowing `a` as a shared borrow

加上{}后,把借用,限定在大括號內,大括號結束后,b會把所有權還給a。

這時,a對數據有了所有權,就可以訪問數據了!

那共享借用和可變借用有什么區別呢,請看代碼如下 :

共享借用:

fn main() {
   let a = [1, 2, 3];
   let b = &a;
   println!("{:?} {}", a, b[0]); // [1, 2, 3] 1
}


fn main() {
   let a = vec![1, 2, 3];
   let b = get_first_element(&a);

   println!("{:?} {}", a, b); // [1, 2, 3] 1
}

fn get_first_element(a: &Vec<i32>) -> i32 {
   a[0]
}

第一段代碼:

fn main() {
   let a = [1, 2, 3];
   let b = &a;
   println!("{:?} {}", a, b[0]); // [1, 2, 3] 1
}

這里a借用給了b,為什么a還可以訪問呢?因為a的類型是數組,是基本類型。這是復制類型,共享借用,只借用復制數據。所以,原來a還是擁有對原始數據的所有權。

第二段代碼:

fn main() {
   let a = vec![1, 2, 3];
   let b = get_first_element(&a);

   println!("{:?} {}", a, b); // [1, 2, 3] 1
}

fn get_first_element(a: &Vec<i32>) -> i32 {
   a[0]
}

這里定義一個函數,get_first_element,返回值為數組中的第一個值。b從函數中得到值1。沒有什么問題。

現在我們修改一下函數get_first_element的代碼,如下 :

fn get_first_element(a: &Vec<i32>) -> i32 {

a[0]=9;

a[0]

}

這時,傲嬌的編譯器女王,又扔出一個錯誤給你:

fn get_first_element(a: &Vec<i32>) -> i32 { 
  | --------- help: consider changing this to be a mutable reference: `&mut std::vec::Vec`
  | a[0]=9; |
    ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable

這些錯誤信息很清楚地告訴你:

你現在是共享借用,共享借用只能共享數據,不能修改!(這里的真實含義是:共享借用了數據,沒有所有權,如果沒有所有權,就沒有修改權,只有擁有所有權,才有修改權!)

那要修改怎么辦?用可變借用啊!

把代碼修改為如下就可以了:

fn main() {  
let mut a = vec![1, 2, 3];
let b = get_first_element(&mut a);//從函數get_first_element返回后,把數據所有權還給a


println!("{:?} {}", a, b); // [9, 2, 3] 9


}

fn get_first_element(g: &mut Vec<i32>) -> i32 { //開始借用 a的數據
 g[0]=9;
 g[0]

}//結束借用 a的數據,返回到main函數后,把數據所有權還給a

以上代碼,已經把不可變變量a改為可變變量,共享變量&a改為可變共享&mut a。

在上面的代碼中,a所綁定的數據的所有權(ownership)已經移動move),也就是說數據所有權(ownership)已經從a轉交到函數get_first_element的參數變量g,在函數get_first_element內,修改了數據的內容,然后返回main函數,並把數據所有權還給綁定變量a,這時數據內容已經更新。

所以打印結果為:

[9, 2, 3] 9

再看看可變借用完整例子:

fn main() {
   let mut a = [1, 2, 3];
   let b = &mut a;
   b[0] = 4;
   println!("{:?}", b); // [4, 2, 3]
}


fn main() {
   let mut a = [1, 2, 3];
  {
       let b = &mut a;
       b[0] = 4;
  }

   println!("{:?}", a); // [4, 2, 3]
}


fn main() {
   let mut a = vec![1, 2, 3];
   let b = change_and_get_first_element(&mut a);

   println!("{:?} {}", a, b); // [4, 2, 3] 4
}

fn change_and_get_first_element(a: &mut Vec<i32>) -> i32 {
   a[0] = 4;
   a[0]
}

所以,我們結合所有權(Ownership)借用(Borrowing)兩個概念來理解。

得出來一個重要結論:

1.沒有所有權,就沒有修改權,只有擁有所有權,才有修改權

2.共享借用,不會轉交數據所有權,所以借用者,沒有修改權,借用者歸還數據所有權后,數據內容不變。

3.可變借用,會轉交數據所有權,所以借用者,擁有修改權,借用者歸還數據所有權后,數據內容可能已經改變

4.這里的“借用”,其實可以跟“引用”划上等號,共享借用,也就是共享引用(或不可變引用),可變借用,也就是可變引用,但它們都是跟數據所有權結合一起的。

以上,希望對你有用。

如果遇到什么問題,歡迎加入:rust新手群,在這里我可以提供一些簡單的幫助,加微信:360369487,注明:博客園+rust

本人精通java高並發,DDD,微服務等技術實踐,以及python,golang技術棧。 本人姓名郭瑩城,坐標深圳,前IBM架構師、咨詢師、敏捷開發技術教練,前IBM區塊鏈研究小組成員、十四年架構設計工作經驗,《區塊鏈核心技術與應用》作者之一, 現有成熟團隊提供區塊鏈開發相關業務(公鏈,交易所,錢包,Dapp,智能合約)。 工作微信&QQ:360369487,交易所開發與區塊鏈錢包開發業務,加我注明:博客園+開發,想學習golang和rust的同學,也可以加我微信,備注:博客園+golang或博客園+rust,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM