5分钟速读之Rust权威指南(七)

487 阅读3分钟

结构体

和元组一样,结构体中的数据可以拥有不同的类型。而和元组不一样的是,结构体需要给每个数据赋予名字以便清楚地表明它们的意义,可以理解为JS中的对象。

定义结构体

在创建结构体之前需要先声明结构体和成员的类型,可以理解为TS中的Interface:

struct User {
	name: String,
	age: i32,
}

创建结构体:

// 使用mut关键字来使结构体可变,意味着其中的所有数据都会可变
let mut user = User {
	name: String::from("xiao_ming"),
	age: 10,
};

// 取值
println!("{}", user.name); // "xiao_ming"
println!("{}", user.age); // 10

// 修改字段
user.age = 11
println!("{}", user.age); // 11

使用工厂模式创建结构体:

fn user_factory(name: String) -> User {
	User {
		name, // 属性名和属性值相同时,可以简写
		age: 10,
	}
}

let mut user = user_factory(String::from("xiao_ming"));

println!("{}", user.name); // "xiao_ming"
println!("{}", user.age); // 10

结构体更新语法,类似于JS中的对象合并运算符:

let user = User {
  name: String::from("xiao_ming"),
  age: 11,
}
let user2 = User {
	age: 10,
	..user // 注意这里的user只能放在其他字段的末尾
};
println!("{}", user2.name); // "xiao_ming"
println!("{}", user2.age); // 10

上面age输出的是user2的10,而不是user的11,说明结构体更新语法只在user中提取user2中没有的内容,并不会全部使用user进行覆盖。

元组结构体

元组结构体同样是struct关键字来定义:

struct Color(u8, u8, u8);
struct Point(i32, i32, i32);

定义结构体数据:

let black = Color(0, 0, 0);
let point = Point(100, 100, 100);

println!("r:{} g:{} b:{}", black.0, black.1, black.2); // r: 0 g: 0 b: 0
println!("x:{} y:{} z:{}", point.0, point.1, point.2); // x: 100 y: 100 z: 100

空结构体

rust允许创建没有任何字段的结构体,因为这种结构体与空元组()十分相似,所以也被称为空结构体:

struct Empty;

计算长方形面积的案例:

定义一个area函数用于计算长方形面积,使用width和height两个参数表示长方形:

fn area(width: u32, height: u32) -> u32 {
	width * height
}
println!("area: {}", area(100, 100)); // area: 10000

使用元组结构体形式:

// 定义一个元组类型的结构体
struct Rect (u32, u32);

// 将函数参数改为接收一个Rect类型的参数
fn area(rect: &Rect) -> u32 {
	rect.0 * rect.1
}

// 创建一个Rect
let rect = Rect(100, 100);
println!("area: {}", area(rect)); // area: 10000

使用结构体形式:

// 定义一个普通结构体
struct Rect {
	width: u32,
	height: u32,
}
fn area(rect: &Rect) -> u32 {
	rect.width * rect.height
}
let rect = Rect {
	width: 100,
	height: 100,
};
println!("area: {}", area(rect)); // area: 10000

通过派生trait增加实用功能

上面有一个问题没有介绍,比如我们尝试直接打印完整结构体:

struct User {
	name: String,
	age: i32,
}
let user = {
  name: String::from("xiao_ming"),
  age: 10
};
println!("{}", user); // 报错,User无法使用默认格式化程序进行格式化

解决这个问题需要两步,第一步,需要添加注解来派生Debug trait:

#[derive(Debug)]
struct User {
	name: String,
	age: i32,
}

第二步,添加:?到花括号中,它会告知println! 当前的结构体需要使用名为Debug的格式化输出:

println!("{:?}", user); // User { name: "xiao_ming", age: 10 }

使用:#?占位符来增加易读性:

println!("{:?}", user);
// User {
//   name: "xiao_ming",
//   age: 10
// }

实际上,rust提供了许多可以通过derive注解来派生的trait,它们可以为自定义的类型增加许多有用的功能,更多信息可以查阅官方文档。