定義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.name
或p.0
會自動解除引用 - (3).比較操作符的兩端如果都是引用類型,則自動解除引用
- (4).能自動解除的引用包括普通引用
&x
、Box<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 ); }