定義Struct的方法

Struct就像面向對象的類一樣,Rust允許為Struct定義實例方法和關聯方法,實例方法可被所有實例對象訪問調用,關聯方法類似於其他語言中的類方法或靜態方法。

定義Struct的方法的語法為impl Struct_Name {},所有方法定義在大括號中。

定義Struct的實例方法

實例方法是所有實例對象可訪問、調用的方法。

例如:

struct Rectangle{ width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn perimeter(&self) -> u32 { (self.width + self.height) * 2 } } fn main() { let rect1 = Rectangle{width: 30, height: 50}; println!("{},{}", rect1.area(), rect1.perimeter()); }

也可以將方法定義在多個impl Struct_Name {}中。如下:

#![allow(unused)] fn main() { impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn perimeter(&self) -> u32 { (self.width + self.height) * 2 } } impl Rectangle { fn include(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } } }

所有Struct的實例方法的第一個參數都是self(的不同形式)。self表示調用方法時的Struct實例對象(如rect1.area()時,self就是rect1)。有如下幾種self形式:

  • fn f(self):當obj.f()時,轉移obj的所有權,調用f方法之後,obj將無效
  • fn f(&self):當obj.f()時,借用而非轉移obj的只讀權,方法內部不可修改obj的屬性,調用f方法之後,obj依然可用
  • fn f(&mut self):當obj.f()時,借用obj的可寫權,方法內部可修改obj的屬性,調用f方法之後,obj依然可用

定義方法時很少使用第一種形式fn f(self),因為這會使得調用方法後對象立即消失。但有時候也能派上場,例如可用於替換對象:調用方法後原對象消失,但返回另一個替換後的對象。

如果仔細觀察的話,會發現方法的第一個參數self(或其他形式)沒有指定類型。實際上,在方法的定義中,self的類型為Self(首字母大寫)。例如,為Rectangle定義方法時,Self類型就是Rectangle類型。因此,下面幾種定義方法的方式是等價的:

#![allow(unused)] fn main() { fn f(self) fn f(self: Self) fn f(&self) fn f(self: &Self) fn f(&mut self) fn f(self: &mut Self) }

Rust的自動引用和解引用

在C/C++語言中,有兩個不同的運算符來調用方法:.直接在對象上調用方法,->在一個對象指針上調用方法,這時需要先解引用(dereference)指針。

換句話說,如果obj是一個指針,那麼obj->something()就像(*obj).something()一樣。更典型的是Perl,Perl的對象總是引用類型,因此它調用方法時總是使用obj->m()形式。

Rust不會自動引用或自動解除引用,但有例外:當使用.運算符和比較操作符(如= > >=)時,Rust會自動創建引用和解引用,並且會盡可能地解除多層引用:

  • (1).方法調用v.f()會自動解除引用或創建引用
  • (2).屬性訪問p.namep.0會自動解除引用
  • (3).比較操作符的兩端如果都是引用類型,則自動解除引用
  • (4).能自動解除的引用包括普通引用&xBox<T>Rc<T>

對於(1),方法調用時的自動引用和自動解除引用,它是這樣工作的:當使用ob.something()調用方法時,Rust會根據所調用方法的簽名進行推斷(即根據方法的接收者self參數的形式進行推斷),然後自動為object添加&, &mut來創建引用或添加*來自動解除引用,其目的是讓obj與方法簽名相匹配。

也就是說,當distance方法的第一個形參是&self&mut self時,下面代碼是等價的,但第一行看起來簡潔的多:

#![allow(unused)] fn main() { p1.distance(&p2); (&p1).distance(&p2); }

關聯函數(associate functions)

關聯函數是指第一個參數不是self(的各種形式)但和Struct有關聯關係的函數。關聯方法類似於其他語言中類方法或靜態方法的概念。

調用關聯方法的語法StructName::func()。例如,String::from()就是在調用String的關聯方法from()。

例如,可以定義一個專門用於構造實例對象的關聯函數new。

struct Rectangle { width: u32, height: u32, } impl Rectangle { // 關聯方法new:構造Rectangle的實例對象 fn new(width: u32, height: u32) -> Rectangle { Rectangle { width, height } } } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { // 調用關聯方法 let rect1 = Rectangle::new(30, 50); let rect2 = Rectangle::new(20, 50); println!("{}", rect1.area()); println!("{}", rect2.area()); }

實際上,實例方法也屬於關聯方法,也可以採用關聯方法的形式去調用,只不過這時需要手動傳遞第一個self參數。例如:

#![allow(unused)] fn main() { // 調用Rectangle的area方法,並傳遞參數&self Rectangle::area( &rect1 ); }