Rust Deref trait


Deref <T> trait用於自定義解除參照運算子(*)的行為。
如果實現Deref <T>特徵,則可以將智慧指標視為參考。 因此,在參照上工作的程式碼也可以用在智慧指標上。

常規參照

常規參照是一種指向某個值的指標,該值儲存在其他地方。下面來看一個簡單的例子來建立i32型別值的參照,然後使用參照運算子和this參照。

fn main()  
{  
  let a = 20;  
  let b = &a;  
  if a==*b  
  {  
    println!("a and *b are equal");  
  }  

  else  
  {  
    println!("they are not equal");  
  }  
}

執行上面範例程式碼,得到以下結果 -

a and *b are equal

在上面的例子中,a儲存i32型別值20,而b包含a變數的參照。 如果使用* b,那麼它代表值20。因此,可以比較變數a* b,它將返回真值。 如果使用&b而不是* b,則編譯器會丟擲錯誤「無法將{integer}與{&integer}進行比較」。

Box <T>作為參照

Box <T>指標可用作參照。

下面來看一個簡單的例子:

fn main()  
{  
  let a = 11;  
  let b = Box::new(a);  
  print!("Value of *b is {}",*b);  
}

輸出結果如下所示 -

Value of *b is 11

在上面的範例中,Box <T>的行為與常規參照類似。 它們之間的唯一區別是b包含指向資料的框,而不是通過使用&運算子參照該值。

智慧指標作為參照

現在,建立類似於Box <T>型別的智慧指標,它們的行為與常規參照有一些不同。

Box <T>可以定義為具有一個元素的元組結構,例如MyBox <T>
建立元組結構後,在MyBox <T>型別上定義函式。

下面來看一個簡單的例子:

struct MyBox<T>(T);  
impl<T> MyBox<T>  
{  
  fn example(y : T)->MyBox<T>  
  {  
    MyBox(y)  
  }  
}  
fn main()  
{  
  let a = 8;  
  let b = MyBox::example(a);  
  print!("Value of *b is {}",*b);  
}

執行上面範例程式碼,得到以下結果 -

在上面的例子中,建立了智慧指標b,但它不能被解除參照。 因此得出結論,無法取消類似於Box <T>型別的自定義指標的參照。

實現Deref Trait

  • Deref Trait在標準庫中定義,該庫用於實現名為deref的方法。
  • deref方法借用self並返回對內部資料的參照。

下面來看一個簡單的例子:

struct MyBox<T>  
{  
  a : T,  
}  
use :: std::ops::Deref;  
impl<T> Deref for MyBox<T>  
{  
  type Target = T;  
  fn deref(&self) ->&T  
  {  
    &self.a  
  }  
}  
fn main()  
{  
  let b = MyBox{a : 10};  
  print!("{}",*(b.deref()));  
}

執行上面範例程式碼,得到以下結果 -

10

程式說明

  • Deref traitMyBox型別上實現。
  • Deref trait實現deref()方法,deref()方法返回a變數的參照。
  • type Target = T;Deref trait的關聯型別。關聯型別用於宣告泛型型別引數。
  • 建立了MyBox型別的範例 - b
  • 通過使用MyBox型別的範例b.deref()呼叫deref()方法,然後取消參照從deref()方法返回的參照。

Deref強制

  • Deref強制是將實現Deref trait的參照轉換為Deref可以將原始型別轉換為的參照的過程。
  • Deref強制是對函式和方法的引數執行的。
  • 當將特定型別的參照傳遞給與函式定義中的引數型別不匹配的函式時,Deref強制自動發生。

下面來看一個簡單的例子:

struct MyBox<T>(T);  
use :: std::ops::Deref;  
impl<T> MyBox<T>  
{  
  fn hello(x:T)->MyBox<T>  
  {  
    MyBox(x)  
  }  
}  
impl<T> Deref for MyBox<T>  
{  
  type Target = T;  
  fn deref(&self) ->&T  
  {  
    &self.0  
  }  
}  
fn print(m : &i32)  
{  
  print!("{}",m);  
}  
fn main()  
{  
  let b = MyBox::hello(5);  

  print(&b);  
}

執行上面範例程式碼,得到以下結果 -

5

在上面的例子中,使用引數&b呼叫print(&b)函式,它是&Box <i32>的參照。 在這種情況下,實現Deref trait,通過Deref強制過程將&Box <i32>轉換為&i32

Derif強制與可變性的相互作用

到目前為止,使用Deref Trait覆蓋不可變參照上的*運算子,可以使用DerefMut Trait覆蓋可變參照上的*運算子。

Rust在以下三種情況下執行Deref強制:

  • 當T:Deref <Target = U>其中TU是不可變參照時,則&T轉換為&U型別。
  • 當T:DerefMut <Target = U>,其中TU是可變參照時,則&mut T被轉換為&mut U
  • 當T:Deref <Target = U>,其中T是可變參照而U是不可變參照,則&mut T被轉換為&U