系列文章目录
【跟小嘉学 Rust 编程】一、Rust 编程基础
【跟小嘉学 Rust 编程】二、Rust 包管理工具使用
【跟小嘉学 Rust 编程】三、Rust 的基本程序概念
【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念
【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据
前言
本章节讲解一种自定义数据类型:结构体,它允许将多个相关的值打包在一起命名,如果你熟悉面向对象语言,那么结构体就像类一样。
主要教材参考 《The Rust Programming Language》
一、定义和实例化结构
1.1、定义结构
要定义结构体,使用关键字 struct 并为结构命名。结构体的名称应该描述组合在一起的数据的重要性。另外打括号包括起来的数据名称和类型,我们称之为字段。
例子:user 结构定义
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64
}
1.2、实例化结构
我们在定义结构后要使用它,需要为每个字段指定具体的值,从而创建该结构的实例,然后添加包含键值对的花括号,其中键就是字段的名称,值就是我们希望存储在这些字段中的数据。
我们不必按照在结构体中声明字段的顺序指定字段。结构定义就像该类型的通用模板,实例用特定的数据填充该模板,以创建该类型的值。
范例:结构的实例化
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64
}
fn main() {
let user = User{
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
}
1.3、访问和修改结构体变量的成员
我们要读或写对象的成员,可以使用 变量.字段
的形式来来问。如果要想修改结构体成员的值,我们必须 使用mut 标记结构体。
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64
}
fn main() {
let mut user = User{
active: true,
username: String::from("someusername123"),
email: String::from("someone@example.com"),
sign_in_count: 1,
};
println!(" user's email is {}", user.email);
user.email = String::from("anotheremail@example.com");
println!(" user's active is {}", user.active);
println!(" user's username is {}", user.username);
println!(" user's email is {}", user.email);
println!(" user's sign_in_count is {}", user.sign_in_count);
}
1.4、函数与结构
1.4.1、使用函数实例化用户
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64
}
fn build_user(email: String, username: String) -> User {
User{
active: true,
username: username,
email: email,
sign_in_count: 1
}
}
fn main() {
let email:String = String::from("xiaojia@example.com");
let username:String = String::from("xiaojia");
let mut user:User = build_user(email, username);
println!(" user's email is {}", user.email);
user.email = String::from("anotheremail@example.com");
println!(" user's active is {}", user.active);
println!(" user's username is {}", user.username);
println!(" user's email is {}", user.email);
println!(" user's sign_in_count is {}", user.sign_in_count);
}
1.4.2、结构体支持精简的实例化
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
1.5、用一个结构体去创建另一个结构体
1.5.1、用结构体成员实例化结构
let email:String = String::from("xiaojia@example.com");
let username:String = String::from("xiaojia");
let user01:User = build_user(email, username);
let user02:User = User {
active: user01.active,
username: user01.username,
email: String::from("another@example.com"),
sign_in_count: user01.sign_in_count
};
1.5.2、结构体更新(struct update)的语法
let email:String = String::from("xiaojia@example.com");
let username:String = String::from("xiaojia");
let user01:User = build_user(email, username);
let user02:User = User {
email: String::from("another@example.com"),
..user01
};
结构体更新语法使用 = 就像赋值,这是因为它移动数据,在创建 user02 之后,我们不能再将 user01 作为一个整体使用,因为 user01 的 username 被移到了 user02 中了。
但是 active 和 sign_in_count 值,在创建 user02 之后 在 user01 之中仍然有效。
1.6、使用未命名字段的元组创建不同的类型(Tuple struct)
tuple- struct 是 Rust 对 struct 关键字的一个创新,可以将 Tuple Struct 理解为非匿名的tuple。
#[allow(dead_code)]
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// 使用 模式解构 方式来获取值,我们称之为 newtype parttern
let Point(x, y, z) = origin;
// 使用索引方式来获取值
let length = origin.0;
let width = origin.1;
let heigth = origin.2;
}
1.7、Unit-Like Struct
在 Rust 中可以定义没有任何字段的 struct,叫做 Unit-Like Struct。 适用于需要在某个类型上实现某个 trait,但是在里面又没有想要存储的数据。
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
1.8、Struct 数据的所有权
Struct 里面如果不是存放的引用,那么该 struct 实例拥有其所有的数据,只要 struct 实例是有效的,那么里面的字段数据也是有效的;
struct 里面也可以存放引用,但这需要使用生命周期
- 生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的;
- 如果 struct 里面存储引用,而不是用生命周期,就会报错;
error[E0106]: missing lifetime specifier
--> src/main.rs:3:13
|
3 | username: &str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 ~ struct User<'a> {
2 | active: bool,
3 ~ username: &'a str,
|
二、使用结构的例子
2.1、简单的长方形面积的例子
struct Rectangle{
width: u32,
length: u32,
}
fn main() {
let rect = Rectangle{
width: 30,
length: 50,
};
println!("{}", area(&rect));
}
fn area(rect : & Rectangle) -> u32 {
rect.width * rect.length
}
2.2、用 派生的Trait 添加功能
2.2.1、使用 println!
宏来打印结构信息
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {}", rect1);
}
此时结构体不能直接打印 结构体的内容。
error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
--> src/main.rs:12:27
|
12 | println!("rect1 is {}", rect1);
| ^^^^^ `Rectangle` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
根据提示我们用 {:?}
或 {:#?}
格式来替代或者 实现 std::fmt::Display
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:?}", rect1);
}
此时,还是会有如下的错误提示:
error[E0277]: `Rectangle` doesn't implement `Debug`
--> src/main.rs:12:29
|
12 | println!("rect1 is {:?}", rect1);
| ^^^^^ `Rectangle` cannot be formatted using `{:?}`
|
= help: the trait `Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `Rectangle` with `#[derive(Debug)]`
|
1 + #[derive(Debug)]
2 | struct Rectangle {
上述提示我们使用 #[derive(Debug)]
来派生或者手动实现 Debug。
#[allow(unused)] // 允许变量不使用 抑制警告
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:?}", rect1);
}
如果你的struct 很复杂,可以是用 {:#?}
可以格式化显示结构体
println!("rect1 is {:#?}", rect1);
2.2.2、使用 dbg!
宏来打印结构信息
我们使用 println!
宏来打印调试信息的时候,并不会打印文件名,所在行号,如果要调试的时候方便可以使用 dbg!
打印 结构体的信息,需要注意的是 该宏需要实现 Debug 接口。
#[allow(unused)]
#[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.1、结构普通方法
使用 impl 关键字可以为结构定义方法,方法的第一个参数是 &self
或者 self
相当于别的语言的 this 指针。
#[allow(unused)]
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle{
fn area(&self)-> u32 {
self.width * self.height
}
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
dbg!(&rect1.area());
}
在调用方法时, Rust 会根据情况自动添加 & 、&mut 或者* ,以便 object 可以匹配方法的签名。
3.2、关联函数(Associated Functions)
例如我们可以在 impl 代码块里面定义不把 self 作为第一个参数的函数,它们叫做关联函数:例如 String:from();
#[allow(unused)]
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle{
fn area(&self)-> u32 {
self.width * self.height
}
fn square(size: u32) -> Self{
Self {
width: size,
height: size,
}
}
}
fn main() {
let s = Rectangle::square(20);
dbg!(s.area());
dbg!(s);
}
3.3、Multiple impl Blocks
一个结构可以有多个 impl 块。文章来源:https://www.toymoban.com/news/detail-497661.html
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
}
}
总结
以上就是今天要讲的内容文章来源地址https://www.toymoban.com/news/detail-497661.html
- 本章我们讲解了如何定义结构、如何使用结构、如何定义方法、关联函数等。
到了这里,关于【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!