结构体是 Rust 中最常用的,同样使用起来也相对更加复杂。
简介
在 Rust 中结构体分成了三类:A) Regular 最常见的结构体,含字段及其类型;B) Tuple 字段名替换为了数字,允许析构方式获取;C) Unit 大小为零,仅作为标记,通常用于实现某个 trait 接口,但是无需成员变量。
与元组不同,结构体提供了字段名称,可以通过字段名直接进行访问,这与其它语言类似,只是为了方便使用提供了很多扩展。
struct Rectangle {
width: u64,
height: u64,
name: String,
}
// 定义函数使用字段初始化简写创建
fn build_rectangle(width: u64, height: u64) -> Rectangle {
Rectangle {
width,
height,
name: String::from("Rectangle"),
}
}
fn main() {
let rect1 = Rectangle {
width: 20,
height: 10,
name: String::from("Rectangle"),
};
// 使用结构体更新语法从一个变量定义新变量
let rect2 = Rectangle {
name: String::from("NewRect"),
..rect1
};
}
结构体更新语法允许通过 ..
定义剩余未显示设置的字段,不过仍然需要注意所有权的转移,上述的 name
变量是移动,而剩余则是复制,那么新老变量都可以继续使用。
关联函数
Rust 中的结构体定义和成员函数实现是分开的,这样成员函数可以根据功能点在不同的文件中实现,从业务逻辑上进行划分。有些函数与对象强相关,但是又不需要关联实例对象,例如构造器。
pub struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
pub fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle::new(30, 50);
println!("Height={}, Width={}", rect.height, rect.width);
}
构造器是最常见的,Rust 并没有将 new
作为关键字,但是约定俗成将其作为构造器。因为没有 self
作为参数,也就不需要 rect.area()
这种通过对象的方式访问,函数又与结构体强相关,被称为关联函数,例如 String::from()
也是。
getter
结构体中成员变量默认是私有的,可以通过 pub
声明为公开,而且 Rust 允许成员变量和成员函数名称相同,这样就可以实现一个简单的 getter
访问器。
pub struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
pub fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
pub fn width(&self) -> u32 {
return self.width;
}
pub fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle::new(30, 50);
println!("Area={}, Width={}", rect.area(), rect.width());
}
在库的相同模块中是可以直接访问私有变量的,如果是不同的模块之间就需要使用 getter
才可以了。
self
其中 &self
是 self: &Self
的简写,其中 Self
代指对应的结构体,而 self
就表示对应的实例了。另外,使用 self
时仍然存在所有权的概念:
&self
向函数传递的是一个引用,不会发生对象所有权的转移,通常也被称为借用。&mut self
表示可变引用,在该函数结束之前其它代码是无法使用的,在该函数内可以修改。self
向函数传递的是一个引用,会发生所有权转移,对象所有权会传递到函数中,使用较少。
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle { width: 30, height: 50 };
println!("The area is {}", rect.area());
}
高级用法
生命周期
如果在结构体中引用某个外部变量,需要保证被引用的变量生命周期要长于结构体对象。
struct Option {
width: u64,
height: u64,
}
struct Rectangle<'a> {
config: &'a Option,
width: u64,
height: u64,
name: String,
}
impl<'a> Rectangle<'a> {
pub fn new(opt: &'a Option) -> Rectangle {
Rectangle {
config: opt,
width: opt.width,
height: opt.height,
name: String::from("Rectangle"),
}
}
pub fn area(&self) -> u64 {
self.height * self.width
}
}
fn main() {
let opt = Option{height: 10, width: 20};
let rec = Rectangle::new(&opt);
println!("{} area is {:?}, config width={}", rec.name, rec.area(), rec.config.width);
}
结合泛型
与常规的 Rust 泛型 类似,还可以通过 trait bounds
指定支持特定的 trait
才行。
struct Rectangle<T> {
width: T,
height: T,
}
impl<T> Rectangle<T>
where
T: std::ops::Mul<Output = T> + Copy,
{
fn area(&self) -> T {
self.width * self.height
}
}
fn main() {
let rect = Rectangle {
width: 30,
height: 50,
};
println!("The area is {}", rect.area());
}
注意,需要增加 where
指定约束,否则可能会导致如下报错。
consider restricting type parameter `T`: `: std::ops::Mul<Output = T>`