
感谢大家关注!
▼▼▼
“关于Rust的前景、优势等不再赘述,这篇主要讲Rust的Struct特性,与C++中的Class有相似性。”
本系列将以“笔记”作为撰文形式,追求简洁明了,概念随时配合代码。如果行文过程中没有讲清楚的地方,欢迎后台私信。如有需要(请后台告诉我),Rust笔记系列更新完成后会再写一份详细的Rust图文教程。
01
—
总览
与tuple的区别是:structs不需要依赖于数据类型中的顺序来access,而是通过键值对。
02
—
定义struct和构造实例
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn structs_test() {
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("some_username"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("another@example.com"); // 获取struct中某个filed可以用点符号。
}
在struct里,我们把数据名和它的类型称为filed。每个filed都是一组键值对。如果要对struct变量改值,需要将整个struct变量设为mut,而不能只对某个filed设为mut。注意:struct类型在同一namespace中只能定义一次。
如果某函数参数的变量名与struct的filed name一样,那么就不用再写filed name了,即:
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: false,
sign_in_count: 2,
}
}
// 在调用函数中
fn structs_test() {
let user2 = build_user(String::from("test@example.com"), String::from("test"));
println!("email: {}, username: {}, sing_in_count: {}, activate: {}",
user2.email, user2.username, user2.sign_in_count, user2.active);
// 输出:email: test@example.com, username: test, sing_in_count: 2, activate: false
}
除了对struct直接赋值之外,我们还可以使用struct更新的语法。使用..user1
的方法给新的struct变量把剩余未指明的field内容补充为与user1
相同的值。其中user1
是一个struct变量。如下:
fn update_struct(user1: User, email: String, username: String) -> User {
User {
email,
username,
..user1
}
}
// 在调用函数中
fn structs_test() {
let user3 = update_struct(user1, String::from("update@example.com"), String::from("update"));
println!("email: {}, username: {}, sing_in_count: {}, activate: {}",
user3.email, user3.username, user3.sign_in_count, user3.active);
// 输出:email: update@example.com, username: update, sing_in_count: 1, activate: true
}
还可以使用tuple structs,即长得像tuple的structs。其用法与tuple大致相似:都可以通过点符号access,但是不能把tuple structs赋值给tuple。另外,哪怕两个struct的组成元素的类型和值完全相同,这也是两个不同的struct。如下:
fn tuple_structs() {
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(1, 2, 3);
let origin = Point(1, 2, 3);
// 只能传入一个struct Color的变量
fn test_tuple_structs(tuple_structs: Color) {
// let (x, y, z) = tuple_structs; error: expected struct `Color`, found tuple
println!("The second value in tuple structs is: {}", tuple_structs.1);
// 输出:The second value in tuple structs is: 2
}
test_tuple_structs(black);
// test_tuple_structs(origin); error: expected struct `Color`, found struct `Point`
}
最后,我们还可以声明一个没有filed的struct,称为unit-like structs:
struct UnitStruct {}
let unit_struct = UnitStruct {};
上述例子中的string全是用了自有ownership的String,这也就保证了整个struct的lifetimes与每个filed的lifetimes保持一致。当然,任何一个filed可以使用引用reference,其ownership被其他struct外部的变量持有。但是这时要求指定其lifetimes,否则就会报错:
struct UserRef {
username: &str, // error: expected named lifetime parameter
sign_in_count: u64,
active: bool,
}
03
—
Structs的打印
rust对struct的打印函数进行了默认支持,但是我们需要告诉编译器我们想要什么样的打印方式。
首先在struct定义时添加#[derive(Debug)]
标记,明确使用打印调试信息的功能。
之后在println!
函数的format中指明打印格式,如{:#?}
或者{:?}
。
// 添加这个标记明确使用打印调试信息的功能
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// 在调用函数中
fn structs_test() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:#?}", rect1);
println!("rect1 is {:?} in another format", rect1);
}
输出如下:
rect1 is Rectangle {
width: 30,
height: 50,
}
rect1 is Rectangle { width: 30, height: 50 } in another format
04
—
Structs的方法(Method)
struct可以通过impl关键字定义method,类似c++里class的成员函数。这里的self类似c++里class的this指针。
如果method的参数不止self,新加的参数需要添加到self之后。
如果参数列表中没有self,这种函数称为associated function而不是method。比如之前经常出现的String::from()
函数。这种函数的好处是不需要某个具体的Rectangle实例就可以调用。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 如果要在函数中改变参数的值,入参改为&mut self
// 如果需要把self转为其他内容,并希望阻止其调用函数保留原值,这时才改为去掉引用的self
fn area(&self) -> u32 {
self.width * self.height
}
// 其他参数必须加到self之后
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// 参数列表中可以没有self,这种称为associated function,不是method。通常用来构造一个新的实例。
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
// 在调用函数中
fn structs_test() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("The area of the rectangle is {}", rect1.area());
let rect2 = Rectangle {
width: 40,
height: 60,
};
let rect3 = Rectangle {
width: 10,
height: 30,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
let sq = Rectangle::square(30);
println!("sq is {:#?}", sq);
}
输出为:
The area of the rectangle is 1500
Can rect1 hold rect2? false
Can rect1 hold rect3? true
sq is Rectangle {
width: 30,
height: 30,
}
这些method可以处于不同的impl里面(一般涉及generic types和traits时会这样写),比如:
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
以上是Rust的Struct特性,配套代码见:
https://github.com/ds1231h/RustSimpleCodesrc/structs.rs
Rust的安装、编译与运行参加本系列第一篇:
想要学习最硬核的技术干货吗?
了解更多有趣的硬核干货
▼▼▼
撰文:沛沛




