龙空技术网

Rust结构体的基本使用

微牛哥自学考清北 210

前言:

而今兄弟们对“结构体的使用方法”可能比较关心,看官们都想要分析一些“结构体的使用方法”的相关资讯。那么小编在网摘上收集了一些对于“结构体的使用方法””的相关知识,希望同学们能喜欢,兄弟们快快来了解一下吧!

为了了解何时使用结构体,让我们编写一个计算矩形面积的程序。我们将从单个变量开始,然后重构程序,直到我们改为使用结构。

让我们用Cargo创建一个新的二进制项目,称为矩形,它将采用以像素为单位指定的矩形的宽度和高度,并计算该矩形的面积。清单5-8显示了一个简短的程序,该程序在我们的项目src / main.rs中具有一种完全相同的方法。

文件名:src / main.rs

fn main() {    let width1 = 30;    let height1 = 50;    println!(        "The area of the rectangle is {} square pixels.",        area(width1, height1)    );}fn area(width: u32, height: u32) -> u32 {    width * height}

清单5-8:计算由单独的width和height变量指定的矩形的面积

现在,使用cargo run以下命令运行该程序:

$ cargo run   Compiling structs v0.1.0 ()    Finished dev [unoptimized + debuginfo] target(s) in 0.42s     Running `target/debug/structs`The area of the rectangle is 1500 square pixels.

即使清单5-8可以工作并且通过在area每个维度上调用函数来找出矩形的面积,我们也可以做得更好。宽度和高度彼此相关,因为它们一起描述了一个矩形。

此代码的问题在以下签名中显而易见area:

fn area(width: u32, height: u32) -> u32 {

该area函数应该计算一个矩形的面积,但是我们编写的函数有两个参数。参数是相关的,但是在我们程序的任何地方都没有表达。将宽度和高度组合在一起将更易读,更易管理。在第3章的“元组类型”部分中,我们已经讨论了使用元组的一种方法。

用元组重构

清单5-9显示了使用元组的程序的另一个版本。

文件名:src / main.rs

fn main() {    let rect1 = (30, 50);    println!(        "The area of the rectangle is {} square pixels.",        area(rect1)    );}fn area(dimensions: (u32, u32)) -> u32 {    dimensions.0 * dimensions.1}

清单5-9:用元组指定矩形的宽度和高度

在某种程度上,该程序更好。元组让我们增加了一些结构,现在我们只传递一个参数。但是以另一种方式,此版本不太明确:元组未命名其元素,因此我们的计算变得更加混乱,因为我们必须索引元组的各个部分。

我们是否要混合宽度和高度以进行面积计算并不重要,但是如果我们想在屏幕上绘制矩形,那就很重要了!我们必须记住,这width是元组索引0,height也是元组索引1。如果其他人正在编写此代码,则他们必须弄清楚这一点并牢记在心。忘记或混淆这些值并导致错误很容易,因为我们没有在代码中传达数据的含义。

结构重构:添加更多含义

我们使用结构通过标记数据来添加含义。我们可以将正在使用的元组转换为具有整体名称和部分名称的数据类型,如清单5-10所示。

文件名:src / main.rs

struct Rectangle {    width: u32,    height: u32,}fn main() {    let rect1 = Rectangle {        width: 30,        height: 50,    };    println!(        "The area of the rectangle is {} square pixels.",        area(&rect1)    );}fn area(rectangle: &Rectangle) -> u32 {    rectangle.width * rectangle.height}

清单5-10:定义一个Rectangle结构

在这里,我们定义了一个结构并将其命名Rectangle。在大括号内,我们将字段定义为width和height,两个字段都具有type u32。然后在中main,我们创建了Rectangle 一个宽度为30高度为50的特定实例。

area现在,我们的函数由一个参数定义,我们将rectangle其命名为 ,其类型是structRectangle 实例的不可变借项。如第4章所述,我们想借用该结构而不是对其进行所有权。这样,可以main保留其所有权并可以继续使用rect1,这就是我们&在函数签名中使用以及调用函数的原因。

该area函数访问 实例的width和height字段Rectangle。area现在,我们的函数签名确切说明了我们的意思:Rectangle使用width和height字段计算的面积。这传达了的宽度和高度都彼此相关,并且它给描述性的名称到值,而不是使用的元组索引值0 和1。为了清楚起见,这是一个胜利。

添加具有衍生特征的有用功能

能够Rectangle在调试程序时打印的实例并查看其所有字段的值,这是很好的。清单5-11尝试使用println!前面各章中使用的宏。但是,这不起作用。

文件名:src / main.rs

struct Rectangle {    width: u32,    height: u32,}fn main() {    let rect1 = Rectangle {        width: 30,        height: 50,    };    println!("rect1 is {}", rect1);}

清单5-11:尝试打印Rectangle 实例

编译此代码时,此核心消息出现错误:

error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`

该println!宏可以做多种格式,默认情况下,大括号告诉println!使用的格式化知Display:输出用于直接终端用户消费。到目前为止,我们已经看到的基本类型Display是默认实现的,因为您只想向用户显示一种1或任何其他基本类型。但是使用结构时,println!格式化输出的方式 就不太清楚了,因为存在更多的显示可能性:是否要使用逗号?您是否要打印大括号?是否应显示所有字段?由于存在这种歧义,Rust不会尝试猜测我们想要什么,并且结构没有提供的实现Display。

如果我们继续阅读错误,则会发现以下有用的注释:

   = 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

试试吧!该println!宏调用现在的样子println!("rect1 is {:?}", rect1);。将说明符:?放在大括号内表示 println!我们要使用称为的输出格式Debug。该Debug特征使我们能够以对开发人员有用的方式打印结构,以便在调试代码时可以看到其值。

进行此更改即可编译代码。德拉特!我们仍然会收到错误消息:

error[E0277]: `Rectangle` doesn't implement `std::fmt::Debug`

但是,再次,编译器给了我们一个有用的注释:

   = help: the trait `std::fmt::Debug` is not implemented for `Rectangle`   = note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug`

Rust确实包含用于打印调试信息的功能,但是我们必须明确选择启用该功能以用于我们的结构。为此,我们#[derive(Debug)]在结构定义之前添加注释,如清单5-12所示。

文件名:src / main.rs

#[derive(Debug)]struct Rectangle {    width: u32,    height: u32,}fn main() {    let rect1 = Rectangle {        width: 30,        height: 50,    };    println!("rect1 is {:?}", rect1);}

清单5-12:添加注释以导出Debug 特征并Rectangle使用调试格式打印实例

现在,当我们运行该程序时,将不会出现任何错误,并且我们将看到以下输出:

$ cargo run   Compiling structs v0.1.0 ()    Finished dev [unoptimized + debuginfo] target(s) in 0.48s     Running `target/debug/structs`rect1 is Rectangle { width: 30, height: 50 }

真好!这不是最漂亮的输出,但是它显示了此实例的所有字段的值,这在调试期间肯定会有所帮助。当我们有更大的结构时,具有易于阅读的输出很有用;在这种情况下,我们可以在字符串中使用{:#?}代替。在示例中使用样式时,输出将如下所示:{:?}println!{:#?}

$ cargo run   Compiling structs v0.1.0 ()    Finished dev [unoptimized + debuginfo] target(s) in 0.48s     Running `target/debug/structs`rect1 is Rectangle {    width: 30,    height: 50,}

Rust为我们提供了许多与derive注释一起使用的特征,这些特征可以为我们的自定义类型添加有用的行为。这些特征及其行为列在附录C中。我们将在第10章中介绍如何通过自定义行为实现这些特征,以及如何创建自己的特征。

我们的area函数非常具体:它仅计算矩形的面积。将此行为与我们的Rectangle 结构更紧密地绑定将是有帮助的,因为它不适用于任何其他类型。让我们看一下如何通过将area函数变成在我们的类型上定义的area 方法来继续重构该代码Rectangle。

标签: #结构体的使用方法