iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0
Modern Web

30天學習Tauri系列 第 7

7.Rust-lang (四)

  • 分享至 

  • xImage
  •  

Borrowing

介紹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,來避免你進行修改
https://ithelp.ithome.com.tw/upload/images/20220922/20108931qPyOxPKbSC.jpg

可變引用

那如果我們想要修改引用呢?
我們就要用到可變引用

我們進行修改:

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");
}

我們能發現確實被修改了
https://ithelp.ithome.com.tw/upload/images/20220922/201089317r6mwr4yF4.jpg

!可變引用的限制
同一時間中對一個特定資料只能有一個可變引用。

我們看一下下面例子

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。所以編譯器告訴我們這是沒有辦法的。
https://ithelp.ithome.com.tw/upload/images/20220922/20108931ToRNVU3lte.jpg

多數語言允許任意改變值,為何Rust這麼麻煩?
可以讓Rust在compile time就防止資料競爭(data race)的可能。資料競爭會造成未定義行為(undefined behavior),而且在執行時你通常是在出現問題時,很難去發現改善的。

可能發生Data Race的行為:

  • 同時有兩個以上的指標存取同個data
  • 至少有一個指標在寫入資料
  • 沒有針對data的同步存取機制行為

迷途引用

在有指標的語言中,常常會有當資源(記憶體空間位置)已經被釋放了但指標卻還留著(仍然指向原本空間位置),而在經過一系列漫長的系統運作後。
原本的記憶體空間很可能已經被別人所有了,但是指標卻仍然指向這裡。而這種指標就稱為迷途指標,而這種行為則稱為迷途引用

(類似多年未更新地圖,明明公園變停車場,但地圖上卻還是告訴你是公園的奇怪行為)

而rust在編譯時就會試圖預防這種問題發生,以下例子

fn main() {
    let x = test_dangle(); 
    print!("{}", x);
}

fn test_dangle() -> &String { // 回傳String的迷途引用
    let s = String::from("hello");

    &s // 回傳 String s 的引用
} // s在此離開scope並釋放記憶體空間位置,它的記憶體就消失了。

https://ithelp.ithome.com.tw/upload/images/20220922/20108931h7lqvJ8Z9Q.jpg

因為s是在test_dangle()內產生的,當dangle結束時,s會被釋放。但我們卻嘗試回傳引用,導致此引用會指向一個已經無效的String。Rust不允許我們這麼做,於是就出現錯誤。

那該如何解決? 直接回傳String就好

// 修改test_dangle
fn test_dangle() -> String { 
    let s = String::from("hello");

    s 
} 

總結一下引用的規則:

  • 任何時候,只能有一個可變引用,或者是可以有任意數量的不可變引用。
  • 引用必須永遠有效

今天大致介紹了什麼借用的概念,也就是什麼是引用。而關於rust的介紹先到這邊,之後我們就回到關於Tauri上面。


迷途指標


上一篇
6.Rust-lang (三)
下一篇
8.Tauri Splashscreen啟動頁面
系列文
30天學習Tauri30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言