定義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 {}
中。如下:
所有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類型。因此,下面幾種定義方法的方式是等價的:
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
時,下面代碼是等價的,但第一行看起來簡潔的多:
關聯函數(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參數。例如: