Rust中的引用類型

本節簡單介紹Rust中的引用,混個臉熟,後面會專門詳細介紹引用以及引用更細節更底層的內容。

Rust中,使用&T表示類型T的引用類型(reference type)。

例如,&String表示String的引用類型,&i32表示i32的引用類型,&&i32表示i32引用的引用類型。

引用類型是一種數據類型,它表示其所保存的值是一個引用

值的引用寫法和引用類型的寫法類似。例如&33表示的是33這個值的引用。

引用,通常來說是指向其他數據的一個指針或一個胖指針(有額外元數據的指針)。例如&33表示的是一個指向數據值33的一個指針。

因此,引用類型保存值的引用

例如:


#![allow(unused)]
fn main() {
let n: &i32 = &33_i32;
}

這裡變量n的類型是引用類型&i32,它所保存的值必須是i32類型數據的引用,例如上面的&33_i32就是33_i32的引用。

可以將保存了引用的變量賦值給其他變量,這樣就有多個變量擁有同一份數據的引用。

fn main(){
  let n = 33;
  let n_ref1 = &n;     // n_ref1指向33
  let n_ref2 = n_ref1; // n_ref2也指向33
}

可以使用std::ptr::eq()來判斷兩個引用是否指向同一個地址,即判斷所指向的數據是否是同一份數據。

fn main(){
  let n = 33;
  let n_ref1 = &n;
  let n_ref2 = n_ref1;
  println!("{}", std::ptr::eq(n_ref1, n_ref2)); // true
}

可變引用

直接使用&創建出來的引用是隻讀的,這意味著可以通過該引用去讀取其指向的數據,但是不能通過引用去修改指向的數據。

如果想要通過引用去修改源數據,需要使用&mut v來創建可修改源數據v的可變引用

注意,想要通過&mut引用去修改源數據,要求原變量是可變的。這很容易理解,&mut是一個對源數據的引用,如果源數據本身就不允許修改,當然也無法通過&mut去修改這份數據。

因此,使用&mut的步驟大致如下:


#![allow(unused)]
fn main() {
let mut x = xxxx;
let x_ref = &mut x;
}

例如,下面聲明的變量n是不可變的,即使創建&mut n,也無法修改原始數據。實際上,這會導致編譯錯誤。

fn main(){
  let n = 33;
  let n_ref = &mut n;   // 編譯錯誤
}

因此,改為如下代碼可編譯通過:

fn main(){
  let mut n = 33;
  let n_ref = &mut n;
}

解引用

解引用表示解除引用,即通過引用獲取到該引用所指向的原始值

解引用使用*T表示,其中T是一個引用(如&i32)。

例如:

fn main(){
  let s = String::from("junma");
  let s_ref = &s;   // s_ref是指向"junma"的一個引用
  
  // *s_ref表示通過引用s_ref獲取其指向的"junma"
  // 因此s和*s_ref都指向同一個"junma",它們是同一個東西
  assert_eq!(s, *s_ref);  // true
}

再例如:

fn main(){
  let mut n = 33;
  let n_ref = &mut n;
  n = *n_ref + 1;
  println!("{}", n);
}

Rust絕大多數時候不會自動地解除引用。但在某些環境下,Rust會自動進行解引用。

自動解引用的情況有(結論先總結在此,混臉熟,以後涉及到時再來):

  • (1).使用.操作符時(包括取屬性值和方法調用),會隱式地儘可能解除或創建多層引用
  • (2).使用比較操作符時,若比較的兩邊是相同類型的引用,則會自動解除引用到它們的值然後比較

對於(1),Rust會自動分析func()的參數,並在需要的時候自動創建或自動解除引用。例如以abc.func()有可能會自動轉換為&abc.func(),反之,&abc.func()也有可能會自動轉換為abc.func()

對於(2),例如有引用類型的變量n,那麼n > &30*n > 30的效果是一樣的。