OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
程式寫到一定時候,會想要開始組織它,以便之後再利用以及增加可讀性,這時候就會分散的資料包裝起來。結構,可以說是幾乎每個現代程式語言都會有的功能。
以下我們以三維向量的運算作為範例
如許多語言,定義一個結構(structure),會是以struct
或是class
開頭:
struct Vector3 {
x: f64,
y: f64,
z: f64,
}
實例化(Instantiate)的話:
let v = Vector3 {
x: 3.0,
y: 4.0,
z: 5.0,
};
又或著是可以用函式實例化:
fn create_vector3(x: f64, y: f64, z: f64) -> Vector3 {
Vector3 { x, y, z }
}
可以看到上面的用函式的方式,我們把解構的欄位省略了,這在Rsut也是合法的。
或是在Rust裡面,我們可以用元組來定義結構,我們把向量的結構改寫:
struct Vector3(f64, f64, f64);
實例化會變成這樣:
let v = Vector3(3.0, 4.0, 5.0);
PS: 我一直覺得tuple是個很神奇的東西,不知道還可以這樣用。
方法其實就是針對結構定義的函式,只是換了個名字。
如果要定義方法的話:
impl Vector3 {
fn length(&self) -> f64 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
}
我們把要定義的方法用impl
包起來,這表示區塊內都是某個結構的方法實作了,上面我們實作的就是Vector3
這個結構。
然後看到方法-length
裡面代的參數是&self
,這是self: &Self
的簡寫,Self
這個型別即是我們要實做那個結構型別的別名。
如果不是利用方法的方式,函式的寫法則是會長這樣:
fn length(v: &Vector3) -> f64 {
(v.x * v.x + v.y * v.y + v.z * v.z).sqrt()
}
最後,我們試試呼叫這個方法:
fn main() {
let v = Vector3 {
x: 3.0,
y: 4.0,
z: 5.0,
};
println!("The length of vector is {}", v.length());
}
可以注意到,我們對傳入的self
是&
借用的方式,確實在這個例子中我們並不想感變原始資料,但跟函式定義的方式一樣,我們使用可變引用:
impl Vector3 {
fn set_x(&mut self, x: f64) {
self.x = x;
}
}
測試一下結果...
impl Vector3 {
fn length(&self) -> f64 {
(self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}
fn set_x(&mut self, x: f64) {
self.x = x;
}
}
fn main() {
let mut v = Vector3 {
x: 1.0,
y: 4.0,
z: 5.0,
};
println!("The length of vector is {}", v.length());
v.set_x(3.0);
println!("The length of vector is {}", v.length());
}
定義在impl
區塊內的函式,如果不使用定義型別的話,則稱為關聯函式,在前面已經有看一個關聯函示了:
String::from("hello");
from
就是String
的關聯函式。
上面說到的,不使用定義型別的話,在寫法上指的是這樣:
impl Vector3 {
fn create(x: f64, y: f64, z: f64) -> Vector3 {
Vector3 { x, y, z }
}
}
這樣我們就定義了一個像是建構子(constructor)的關聯函式。
當然,也可以這樣寫,改寫一下length
:
impl Vector3 {
fn dot_product(v0: &Vector3, v1: &Vector3) -> f64 {
v0.x * v1.x + v0.y * v1.y + v0.z * v1.z
}
fn length(&self) -> f64 {
Vector3::dot_product(self, self).sqrt()
}