Photo by Raitis Raitums
在上一篇裡的最後一個程式碼有提到因為所有權,導致我們在實作 function 及使用上都造成一定程度的不方便。
fn get_len(word: String) -> (String,usize) {
let length = word.len();
(word,length)
}
fn main() {
let s2 = String::from("HELLO");
let (s2,s2_len)= get_len(s2);
println!("s2:{} ,length={} ", s2,s2_len);
}
那我們該如何改善這段程式碼呢? 首先我們希望把 -> (String, usize)
變成 -> usize
就好,畢竟我們只是要計算字數而已。 再來就不用 shaowing
變數,也能夠直接在 println!
裡繼續使用 s2
Borrowing
我們只需要將程式碼稍稍改寫:
fn get_len(word: &String) -> usize {
let length = word.len();
length
}
fn main() {
let s2 = String::from("HELLO");
let s2_len = get_len(&s2);
println!("s2:{} ,length={} ", s2,s2_len);
}
當我們使用 &
運算子時意思是我們要去取 reference,所以 &s2
就是去取得 s2
的 reference。 而所謂 rust 中的 reference,我覺得比較像 c++中的 pointer(指標),感覺像是指向變數記憶體位置的一個代稱。
而當我們將 &s2
傳入到 get_len
時,因為我們是將 s2
的 reference 傳入而不是將他的變數傳進去,所以並不會發生 move,而這個行為就被稱為 borrowing(借用)。
可以發現我們不用再多 return s2
本身,只需要將 word 的 type 改為 &String
來表示我們要接收的一是個 string reference。 而在 rust 中我們在操作 reference type 大多數時刻,都跟我們在操作一般的型別一樣。我們可以直接 word.len()
但在某些情況下我們可會需要使用到 *
這個運算子來 dereference (解參考)
Mutable reference
在 rust 中我們如果讓一個變數是可以被更改的都需要加上 mut
這個關鍵字,而 reference 也是一樣,如果我要建立一個 mutable reference 我們就必須使用 &mut
fn push_string(word: &mut String){
word.push_str("test");
}
fn main() {
let mut a = String::from("test");
push_string(&mut a);
println!("{}",a) // testtest
}
從上面的例子得知,我們必須將參數 type 改為 &mut
String 且我們也必須在傳入函數時使用 &mut
,需要注意的一點是必須是 let mut
的變數才能建立 &mut
。
在 rust 中為了避免 Data race 所以每個變數只能擁有一個 mutable reference
// This code does not compile
fn push_string(word: &mut String){
word.push_str("test");
}
fn main() {
let mut a = String::from("test");
let immut_a = &a;
let mut_a = &mut a;
push_string(mut_a);
println!("{} , {}",a ,immut_a)
}
編譯器會很貼心地告訴我們,哪裡重複宣告了 mutable reference
error[E0499]: cannot borrow `a` as mutable more than once at a time
--> src/main.rs:10:17
|
9 | let mut_a = &mut a;
| ------ first mutable borrow occurs here
10 | let mut_b = &mut a;
| ^^^^^^ second mutable borrow occurs here
...
13 | push_string(mut_a);
| ----- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
而且也不能同時擁有 mutable reference 和 immutable reference
// This code doesn't compile
fn push_string(word: &mut String){
word.push_str("test");
}
fn main() {
let mut a = String::from("test");
let immut_a = &a;
let mut_a = &mut a;
push_string(mut_a);
println!("{} , {}",a ,immut_a)
}
error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable
--> src/main.rs:10:17
|
9 | let immut_a = &a;
| -- immutable borrow occurs here
10 | let mut_a = &mut a;
| ^^^^^^ mutable borrow occurs here
...
14 | println!("{} , {}",a ,immut_a)
| ------- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error
之所以不能允許同時擁有 mutable 及 immutable 的 reference 是因為這樣子那個宣告成 immutable reference 就不能確保他是 immutable 的了。
但 rust 的編譯器有辦法判斷出各個 reference 的作用範圍,所以這樣子是可以的:
fn push_string(word: &mut String){
word.push_str("test");
}
fn main() {
let mut a = String::from("test");
let immut_a = &a;
println!("{} , {}",a ,immut_a);
let mut_a = &mut a;
push_string(mut_a);
println!("{}",mut_a);
}
在第一次 println!
後再也沒用到 immut_a
所以之後再宣告一個 mutable reference 是可以的。
參考資料: