结构体的定义与实例化
举例如下
1 2 3 4 5 6
| struct User { active: bool, username: String, email: String, sign_in_count: u64, }
|
实例化如下
1 2 3 4 5 6 7 8 9 10 11 12
| let user1 = User { active: true, username: String::from("name"), email: String::from("email@email.com"), sign_in_count: 1, };
let mut user2 = User { } user2.email = String::from("xxx");
|
作为函数返回值如下
1 2 3 4 5 6 7 8
| fn build_user(email: String, username: String) -> User { User { active: true, username: username, email: email, sign_in_count: 1, } }
|
如果函数参数和字段名相同,则可以使用简化写法如下
1 2 3 4 5 6 7 8
| fn build_user(email: String, username: String) -> User { User { active: true, username, email, sign_in_count: 1, } }
|
使用结构体更新语法从其他实例创建
1 2 3 4 5 6
| let user2 = User { active: user1.active, username: user1.username, email: String::from("another@example.com"), sign_in_count: user1.sign_in_count, };
|
也可使用简化写法,使用..
来指定剩余没有变化的值,例如
1 2 3 4
| let user2 = User { email: String::from("another@example.com"), ..user1 };
|
..user1
必须放在最后
请注意,转移与克隆的规则在此处仍然适用,例如用user1
的username
与email
字段更新user2
之后,这两个字段被转移到user2
中,其余值则拷贝了一份,此时user1
不能再使用。
而如果user2
的那两个字段使用String::from()
来进行赋值,其余从user1
更新,那二者都可使用
元组结构体/匿名结构体
类似如下形式,使用时同元组,使用下标
1 2 3 4 5 6 7
| struct Color(i32, i32, i32); struct Point(i32, i32, i32);
fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
|
没有字段名称的类单元结构体
即元组类型中的unit
类型,类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。形式如下:
1 2 3 4 5
| struct AlwaysEqual;
fn main() { let subject = AlwaysEqual; }
|
结构体数据的所有权
在上面的示例中,User
结构体中的字符串类型使用了String
而非&str
,这是因为想要这个结构体拥有它所有的数据,由此,只要整个结构体是有效的,那其中的数据也一定有效。
也可以是结构体存储被其他对象拥有的数据的引用(切片类型),不过需要用上生命周期标识符。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的
结构体的示例程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct Rectangle { width: u32, height: u32, }
fn main() { let rect1 = Rectangle { width: 30, height: 50, };
println!( "The area of the rectangle is {} square pixels.", area(&rect1) ); }
fn area(rectangle: &Rectangle) -> u32 { rectangle.width * rectangle.height }
|
通过派生trait
增加实用功能
在上面实现的结构体,无法直接通过println!
,但可通过以下方式进行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #[derive(Debug)] struct Rectangle { width: u32, height: u32, }
fn main() { let rect1 = Rectangle { width: 30, height: 50, };
println!("rect1 is {:?}", rect1); dbg!(&rect1); }
|
方法语句
关键字为impl
,impl
块中所有内容都将与结构体类型相关联,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #[derive(Debug)] struct Rectangle { width: u32, height: u32, }
impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn width(&self) -> bool { self.width > 0 } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } }
fn main() { let rect1 = Rectangle { width: 30, height: 50, };
println!( "The area of the rectangle is {} square pixels.", rect1.area() ); }
|
在函数area
中,&self
实际上是self: &self
的缩写,他会借用调用对象的值,而不获取他的所有权
关联函数
所有在impl
块中定义的函数被称为 关联函数 (静态成员函数),因为其与impl
后面命名的类型先关。可以定义不以self
为第一参数的关联函数,因此他不是方法,不是方法的关联函数通常会被用作返回一个结构体新实例的构造函数,例如
1 2 3 4 5 6 7 8 9 10
| impl Rectangle { fn square(size: u32) -> Self { Self { width: size, height: size, } } }
let sq = Rectangle::square(3);
|
使用::
来使用结构体的关联函数,::
语法用于关联函数和模块创建的命名空间