介紹Borrow是什麼東西前,我們先介紹什麼是引用。引用(references)就像是指向某個地址的指標,我們可以追蹤存取到該處儲存的資訊,而該地址仍被其他變數所擁有。和指標不一樣的是,引用保證所指向的特定型別的數值一定是有效的。所以Borrow就是,就是借出變數所有權,當你用完後就要歸還
接著,我們來看以下的例子:
fn main() {
let x = String::from("hello");
// &x建立一個指向x數值的引用,但沒有x的所有權,並且所指向的資料在引用不再使用後並不會被丟棄
let len = calculate_length(&x);
println!("'{}' 的長度為 {}。", x, len);
}
// 當函式使用引用作為參數時,我們不需要回傳數值來歸還所有權,因為沒有所有權,只是引用它的數值而已
//
// s是個String的引用
// 當s在離開作用域(也就是{}),因為它沒有它所指向的資料的所有權,所以不會有任何動作發生
fn calculate_length(s: &String) -> usize {
s.len()
}
那如果我們修改借用的東西呢?
fn main() {
let x = String::from("Hello");
test_change(&x);
println!("{}", x);
}
fn test_change(s: &String) {
s.push_str(", world");
}
編譯器就會出現下面錯誤,告訴你引用是不可變的,所以不允許修改value,來避免你進行修改
那如果我們想要修改引用呢?
我們就要用到可變引用
我們進行修改:
fn main() {
let mut x = String::from("Hello");
// 在呼叫函式的地方建立了一個可變引用 &mut x
test_change(&mut x);
println!("{}", x);
}
// 透過s來接收可變引用
fn test_change(s: &mut String) {
s.push_str(", world");
}
我們能發現確實被修改了
!可變引用的限制
同一時間中對一個特定資料只能有一個可變引用。
我們看一下下面例子
fn main() {
let mut s = String::from("hello");
let x1 = &mut s;
let x2 = &mut s;
println!("x1: {},x2: {}", x1, x2);
}
會發現編譯器告訴我們無法同時可變借用s超過一次。
第一次可變借用在x1且必須持續到它在完成println!,但在其產生到使用之間,我們卻嘗試建立另一個與x1相同資料的可變借用x2。所以編譯器告訴我們這是沒有辦法的。
多數語言允許任意改變值,為何Rust這麼麻煩?
可以讓Rust在compile time就防止資料競爭(data race)的可能。資料競爭會造成未定義行為(undefined behavior),而且在執行時你通常是在出現問題時,很難去發現改善的。
可能發生Data Race的行為:
在有指標的語言中,常常會有當資源(記憶體空間位置)已經被釋放了但指標卻還留著(仍然指向原本空間位置),而在經過一系列漫長的系統運作後。
原本的記憶體空間很可能已經被別人所有了,但是指標卻仍然指向這裡。而這種指標就稱為迷途指標,而這種行為則稱為迷途引用(類似多年未更新地圖,明明公園變停車場,但地圖上卻還是告訴你是公園的奇怪行為)
而rust在編譯時就會試圖預防這種問題發生,以下例子
fn main() {
let x = test_dangle();
print!("{}", x);
}
fn test_dangle() -> &String { // 回傳String的迷途引用
let s = String::from("hello");
&s // 回傳 String s 的引用
} // s在此離開scope並釋放記憶體空間位置,它的記憶體就消失了。
因為s是在test_dangle()內產生的,當dangle結束時,s會被釋放。但我們卻嘗試回傳引用,導致此引用會指向一個已經無效的String。Rust不允許我們這麼做,於是就出現錯誤。
那該如何解決? 直接回傳String就好
// 修改test_dangle
fn test_dangle() -> String {
let s = String::from("hello");
s
}
總結一下引用的規則:
今天大致介紹了什麼借用的概念,也就是什麼是引用。而關於rust的介紹先到這邊,之後我們就回到關於Tauri上面。