结构体和枚举是为了充分利用Rust编译时的类型检查来在程序范围内创建新类型的基本组件。
struct 是一个允许我们命名并将多个相关值包装进一个有意义的组合的自定义类型。
有点类似python新加入的数据类(其实是python学人家(kotlin的吧 PEP数据类 不过python的是用类装饰器实现的语法糖而已,要配合类型标记使用。
定义结构体:
struct User { // struct 关键字 结构体名字
username: String, // 字段: 字段类型 逗号结尾
email: String,
sign_in_count: u64,
active: bool,
}
struct Color(u32, u32, u32); // 没有命名字段的元组结构体,记得python namedtuple么?
struct Haa{} // 没有任何字段的 类单元结构体 (unit like)
实例化结构体:
// 不可变的结构体
let user1 = User {
email: String::from("test@test.com"),
username: String::from("test"),
sign_in_count: 555,
active: true,
}
// 可变的结构体
let user2 = User{
email: String::from("hhh@hhh.net"),
username: String::from("+1s"),
active: false,
sign_in_count: 89,
}
// 初始化元祖结构体
let color = Color(1, 2, 3);
读取和修改结构体字段:
println!("user {} email: {}", user1.username, user1.email); // 使用.来读取结构体信息
// 如果结构体可变,使用.来修改结构体字段,必须整个结构体可变,不能只标记某个字段可变
user1.email = String::from("test@test22222.org"); // 失败,user1不可变
user2.email = String::from("gouliguojiashengsiyi@email.cc");// 正常
简化语法:
// 变量和字段名同名时,可以使用字段初始化简写语法
fn build_user(email: String, username: String) -> User {
User {
email, // 字段直接从同名参数拿
username,
active: true,
sign_in_count: 99,
}
}
fn build_user2(email: String, username: String) -> User {
User {
email,
username,
..user2 // 从user2里拿
}
}
let user3 = User {
email: "this@that.org",
..user1 // 从user1拿其他剩余字段
}
print 特殊定义:
// 通过派生trait增加实用功能
struct Rectangle {
width: u32,
height: u32,
}
let rec1 = Rectangle{
width: 12,
height: 22,
};
println("Rectangle: {}", Rectangle);
// `Rectangle` cannot be formatted with the default //formatter; try using
//`:?` instead if you are using a format string
// 在格式化字符串中使用 {:?} 来打印 Debug 格式的信息
// 再格式化字符串中使用 {:#?} 来打印出格式更好的输出
// {}, 默认打印Display格式的信息
println("Rectangle: {:?}", Rectangle); // error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
// 使用 #[derive(Debug)] 注解来打印Debug格式的信息
方法定义: 语法:
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle { // impl 关键字, 同一个结构体可以有多个impl语句
fn area(&self) -> u32 { // 定义方法 self指代结构体本身
self.width * self.height
}
fn canhold(&self, other: Rectangle) -> bool {
self.width > other.widht && self.height > other.height // 加入其他参数的方法
}
fn square(size: u32) -> Rectangle { // 不使用self参数的关联函数,类似py的静态方法
Rectangle {width: size, height: size}
}
}
fn main() {
let rect1 = Rectangle {width: 32, height: 12}; // 实例化结构体
println!("Area of {:#?} is: {}", rect1, rect1.area()); // 使用之前的格式化方法和调用实例自己的方法实现计算
}
枚举出所有可能的值
// 创建枚举类型
enum IpAddrKind {
V4,
V6,
}
// 创建枚举类型的实例(他们都是IpAddrKind类型的,因此可以当成一个类型,方便编译器检查
let v4 = IpAddrKind::V4;
let v6 = IpAddrKind::V6;
// 绑定具体类型到枚举类型
enum IpAddrKind2 {
V4(String),
V6(String),
}
// 初始化
let v42 = IpAddrKind::V4(String::from("10.10.20.1"));
let v62 = IpAddrKind::V6(String::from("10.20.30.40"));
// 可以内嵌许多类型
struct Ipv4Addr {
}
struct Ipv6Addr {
}
enum IpAddrKind3 {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
enum Test {
test1,
test2 { x: i32, y: i32 },
test3(i32, i32, i32)
}
impl 可以为枚举类型定义方法
enum Option<T> {
Some(T),
None,
}
使用Option比None好,因Option和T是不同的类型,编译器对option类型会执行严格的检查,确保不出现其他语言中None,null等空值错误。