Rust 作為一種系統級編程語言, 與C/C++語言一樣,它們都有非常高效的內存讀寫操作,因此三者都非常適合資源有限的嵌入式平臺的開發。 在很多編程語言中都有結構體這個概念,常常用于面向對象開發, 提高代碼內聚, 降低耦合。Rust的結構體與C的結構體有非常多的相似之處,甚至可以使用#[repor(C)]
屬性, 直接與C/C++的結構體進行互操作。讓 Rust 調用 C 庫使用方法和 C
與 C++
代碼互相調用一樣簡單。但 Rust 的結構題體與 C 的結構體也有非常多的不同之處,有更多的特性,用于簡化編程,簡化抽象邏輯。
定義方法
Rust 結構體的預定義關鍵字與 C
一樣,都使用 struct
關鍵字開頭。但Rust 定義的后面通常無需添加分號;
,除非結構體為空。各子屬性的后面使用逗號分隔。 Rust 的結構體定義方法如下
struct Point {
x: i32,
y: i32,
}
// 空結構體定義方法
struct EmptyStruct;
構造與初始化
Rust 的結構體沒有要求必須有構造函數,但可以通過關聯函數(如 new)初始化:
#[derive(Default)]
struct Point {
x: i32,
y: i32,
}
impl Point {
pubfn new(x: i32, y: i32) -> Point {
Point { x, y } // 等價于 Point { x: x, y: y }
}
pubfn Point(point: Point) -> Point {
Point {
x: point.x,
..point // 等價于 Point { x: point.x, y: point.y }
}
}
}
需要注意的是,Rust 的字段必須都初始化后才能使用,否則編譯會報錯,來保證字段的安全性。可使用 #[derive(Default)]
注解來為結構體添加默認值。
內部字段的訪問
Rust 的結構體的內部字段可使用可見性修飾符,用于控制字段的訪問權限。Rust 的結構體支持以下可見性修飾符:
: 默認可見性,字段只能在當前模塊內訪問。
pub
:公共可見性,字段可以被外部訪問。pub(crate)
:當前 crate 內可見,字段可以被當前 crate 內的代碼訪問。pub(super)
:父級模塊可見,字段可以被父級模塊內的代碼訪問。
內存排列
Rust 的結構體的內存排列與 C
結構體的內存排列不同。Rust 的結構體的內存排列是按照字段的定義順序進行排列的,而 C
結構體的內存排列是按照字段的聲明順序進行排列的。 Rust 的結構體的內存排列如下:
struct Display {
x: i32,
width: u8,
y: i32,
height: u8,
}
以上代碼在編譯后的內存順序可能自動調整為:
struct Display {
x: i32, // offset 0, size 4
y: i32, // offset 4, size 4
width: u8, // offset 8, size 1
height: u8, // offset 9, size 1
}
如果需要保持字段的內存順序與C語言一致,可以使用#[repr(C)]
屬性,這樣就可以保持字段的內存順序與C語言一致, 在與C交互時則會完全兼容。 如下:
#[repr(C)]
struct Display {
x: i32, // offset 0, size 4
width: u8, // offset 4, size 1
y: i32, // offset 8, size 4
height: u8, // offset 12, size 1
}
方法
Rust 的支持為結構體定義方法,方法與函數的定義類似,只是方法定義在結構體的內部。方法的定義格式如下:
struct Point {
x: i32,
y: i32,
}
impl Point {
fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy) asf64
}
pubfn new(x: i32, y: i32) -> Point {
Point { x, y }
}
pub(crate) fn get_x(&self) -> i32 {
self.x
}
pub(super) fn get_y(&self) -> i32 {
self.y
}
}
如上所示,使用 impl
關鍵字定義結構體的方法,
- 內部的第一個參數如果為
self
,表示當前結構體的實例中可使用, 當所有權會被移動。 - 如果方法的第一個參數的類型為
&self
,表示當前結構體的實例的引用,結構體變量所有權不會移動,無法修改內部屬性的值。 - 方法的第一個參數的類型為
&mut self
,表示當前結構體的實例的可變引用,當前函數可修改內部屬性的值。 - 如果函數的第一個參數非
self
、&self
、&mut self
,則表示當前函數為靜態方法, 可直接通過結構體名調用。
Rust 也能使用 impl trait_name for struct_name
來為結構體實現 trait, 如下:
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.x, self.y)
}
}
以上代碼定義了一個 Point
結構體,并為其實現了 Display
trait, 這樣就可以使用 println!
宏來打印 Point
結構體的實例。
let p = Point { x: 1, y: 2 };
// 代碼會輸出 `(1,2)`。
println!("{}", p);
所有權與生命周期
Rust 的結構體可以包含引用(&T 或 &mut T),但必須指定生命周期:
struct Bar<'a> {
data: &'a str, // 必須標注生命周期
}
范型化
Rust 的結構體支持模板化, 可以使用泛型來定義結構體, 如下:
struct Point {
x: T,
y: T,
}
以上代碼定義了一個 Point
結構體, 其中 x
和 y
都是泛型類型 T
, 可以是任意類型。這樣就可以使用 Point
結構體來表示任意類型的點, 適應不同的場景。
let p = Point { x: 1, y: 2 };
let p = Point { x: 1.0, y: 2.0 };
模式匹配
Rust 的結構體支持模式匹配, 可以使用模式匹配來解構結構體, 如下:
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
match p {
Point { x, y } => println!("x={}, y={}", x, y),
_ => println!("other"),
}
}
以上代碼定義了一個 Point
結構體, 并在 main
函數中使用模式匹配來解構 Point
結構體的實例, 解構后可以使用 x
和 y
變量來訪問結構體的內部屬性。
析構函數
Rust 的結構體都有析構函數, 析構函數在結構體被釋放時自動調用, 可以用于釋放資源, 如下:
struct Foo {
data: Vec,
}
impl Drop for Foo {
fn drop(&mut self) {
println!("Foo is dropped");
}
}
結構體變量在釋放時默認會為內部的字段執行析構函數,避免內存泄漏,保證內存安全。
總結
Rust 的結構體與 C 的結構體有非常多的不同之處, 但 Rust 的結構體也有非常多的優點,用于提高內存安全和簡化編程, 如:
- 支持范型化, 可以使用泛型來定義不同屬性類型的結構體
- 支持模式匹配, 可以使用模式匹配來解構結構體
- 支持析構函數, 可以使用析構函數來釋放資源
- 支持生命周期, 可以使用生命周期來指定結構體的引用的生命周期
- 支持引用, 可以使用引用來指向結構體的內部屬性
- 支持方法, 可以使用方法來定義結構體的行為
- 支持可見性修飾符, 可以使用可見性修飾符來控制字段的訪問權限
- 支持內存排列, 可以使用內存排列來指定結構體的內存布局
- 支持構造函數, 可以使用構造函數來初始化結構體的實例
- 支持靜態方法, 可以使用靜態方法來定義結構體的行為
- 支持兼容C, 可以使用
#[repr(C)]
屬性來與C語言交互, 適應多語言編程