文章目录
四、结构体
1、定义并实例化结构体
定义和实例化
定义
定义结构体,需要使用 struct
关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
实例化
通过为每个字段指定具体值来创建这个结构体的 实例。创建一个实例需要以结构体的名字开头,接着在大括号中使用 key: value
键-值对的形式提供字段。
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
获取数据
使用点号,如果结构体的实例是可变的,那么还可以使用点号来改变字段值。
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
变量与字段同名时的字段初始化简写语法
参数名与字段名都完全相同,我们可以使用 字段初始化简写语法(field init shorthand)
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
使用结构体更新语法从其他实例创建实例
..
语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值。
下面两份代码效果相同:
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
let user2 = User {
email: String::from("another@example.com"),
..user1
};
..user1
必须放在最后,以指定其余的字段应从 user1
的相应字段中获取其值。
!!!!注意!!!
结构更新语法就像带有 =
的赋值,因为它移动了数据,所以不能再使用 user1
(实质上只有username字段不能用,并且可以赋值一个String值让其再次可用。),因为 user1
的 username
字段中的 String
被移到 user2
中 。但是如果我们对 user2
的 String
都赋予新的 String
值,只对剩下的字段使用 user1
更新,则 user1
仍可以使用。因为剩下两个字段是 bool
和 int
类型,都实现了实现 Copy
trait 。这两个类型是使用克隆而不是移动。这样 user1
的所有字段的所有权都没有转移。
代码如下:
let user2 = User {
username: String::from("another"),
email: String::from("another@example.com"),
..user1
};
// 此时只有 user1.username 不可用
println!("user1 {} {} {}",user1.email,user1.sign_in_count,user1.active);
user1.username = String::from("username"); // 赋值之后, username 再次可用。
元组结构体
元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 1, 2);
let origin = Point(0, 1, 2);
println!("black {} {} {}",black.0,black.1,black.2);
println!("origin {} {} {}",origin.0,origin.1,origin.2);
注意 black
和 origin
值的类型不同,因为它们是不同的元组结构体的实例。
也可以使用 .
后跟索引来访问单独的值
类单元结构体
一个没有任何字段的结构体!它们被称为 类单元结构体(unit-like structs)因为它们类似于 ()
,即“元组类型”一节中提到的 unit 类型。
类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。
2、通过派生 trait 增加实用功能
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
增加属性来派生 Debug
trait,并使用调试格式打印 Rectangle
实例
另一种使用 Debug
格式打印数值的方法是使用 dbg!
宏。dbg!
宏接收一个表达式的所有权,打印出你代码中 dbg!
宏调用的文件和行号,以及该表达式的结果值,并返回该值的所有权。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
dbg!(&rect1);
}
3、方法语句
定义方法
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
impl
+ 结构体
块,方法签名的第一个参数必须是 self
,这里只需要读取数据,所以使用了引用,&self
相当于 self : &self
,这里 Rust 允许只用 self 缩写。
带有更多参数的方法
这里实现一个 can_hold
方法,传入另一个 Rectangle
,比较 self
是否能完全包含这个长方形。
#![allow(unused)]
fn main() {
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
关联函数
所有在 impl
块中定义的函数被称为 关联函数(associated functions),因为它们与 impl
后面命名的类型相关。我们可以定义不以 self
为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。
不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
let sq = Rectangle::square(3);
使用结构体名和 ::
语法来调用这个关联函数
多个 impl 块
每个结构体都允许拥有多个 impl
块。