当前位置: 首页 > article >正文

【Rust自学】5.2. struct使用例(加打印调试信息)

对不起我都写到第8章了才发现我忘记发这一篇了,现在补上,不过这可能导致专栏的文章顺序有一点问题,但也只能将就着了。

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

5.2.1. 例子需求

创建一个函数,计算长方形的面积,长和宽类型均为u32且面积类型为u32

5.2.2. 普通解法

最简单的解法就是定义这个函数有两个参数:一个长一个宽,都为&u32类型(例子中说了是u32类型,并且这个场景下不需要函数获得数据所有权,所以使用引用在数据类型前加&),在函数中返回长乘以宽的值就行。

fn main() {  
    let width = 30;  
    let length = 50;  
    println!("{}", area(&width, &length));  
}  
  
fn area(width: &u32, length: &u32) -> u32 {  
    width * length  
}

输出:

1500

5.2.3. 元组解法

普通解法本身没有问题,但在可维护性有一个问题:长和宽是独立的参数,程序中的任何地方都不清楚这些参数是相关的。将宽度和高度组合在一起会更具可读性和更易于管理。对于数据的整合,使用元组再好不过(因为都是同一数据类型,所以在这里使用数组也是可以的)。

fn main() {  
    let rectangle = (30,50);  
    println!("{}", area(&rectangle));  
}  
  
fn area(dim:&(u32,u32)) -> u32 {  
    dim.0 * dim.1  
}

输出:

1500

5.2.4. struct解法

元组解法虽然提升了可维护性,但代码的可读性变差了,因为如果不加注释没人知道元组的第一个数据是代表长还是代表宽(虽然对于计算面积来说无所谓,但是对于较大的项目来说很重要)。元组的元素是没有名字的,即使是元组结构体(上一篇文章中有讲),它里面的元素也是没有名字的。

那么那种数据结构可以把两个数据整合到一起并且分别赋名呢?没错,就是struct。

struct Rectangle {  
    width: u32,  
    length: u32,  
}  
  
fn main() {  
    let rectangle = Rectangle{  
        width: 30,  
        length: 50,  
    };  
    println!("{}", area(&rectangle));  
}  
  
fn area(dim:&Rectangle) -> u32 {  
    dim.width * dim.length  
}

5.2.5.打印结构体的调试信息

接着上面的代码,如果再加一行直接打印rectangle这个实例会怎么样呢?代码如下:

struct Rectangle {  
    width: u32,  
    length: u32,  
}  
  
fn main() {  
    let rectangle = Rectangle{  
        width: 30,  
        length: 50,  
    };  
    println!("{}", area(&rectangle));  
    println!("{}", rectangle);  //直接打印实例
}  
  
fn area(dim:&Rectangle) -> u32 {  
    dim.width * dim.length  
}

输出:

error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
  --> src/main.rs:12:20
   |
12 |     println!("{}", rectangle);
   |                    ^^^^^^^^^ `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)

先解释一下报错:println!这个宏它可以执行很多格式化的打印。占位符{}就是告诉println!来使用std::fmt::Display这个trait(理解成接口),类似于Python的toString,而在报错信息中提到的就是Rectangle并没有实现std::fmt::Display这个trait,也就不能打印。

实际上,目前所讲的基础数据类型,默认都实现了std::fmt::Display这个trait,因为它们的展示方式都比较单一,比如说把1打印出来,那程序只可能打印出阿拉伯数字1。但是对于Rectangle,它里面有2个字段,是要都打印,还是打印width,还是打印length呢?可能性太多了,所以Rust并没有为struct默认实现std::fmt::Display这个trait。

但如果我们继续往下看到这一行:

= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead

编译器提示我们可以使用{:?}或者是{:#?}来代替{}。那就试试第一种:

struct Rectangle {  
    width: u32,  
    length: u32,  
}  
  
fn main() {  
    let rectangle = Rectangle{  
        width: 30,  
        length: 50,  
    };  
    println!("{}", area(&rectangle));  
    println!("{:?}", rectangle);  //把`{}`改为`{:?}`
}  
  
fn area(dim:&Rectangle) -> u32 {  
    dim.width * dim.length  
}

还是报错了:

error[E0277]: `Rectangle` doesn't implement `Debug`
  --> src/main.rs:12:22
   |
12 |     println!("{:?}", rectangle);
   |                      ^^^^^^^^^ `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 {
   |

但报错信息变了,上一回是没有实现std::fmt::Display,这回是没有实现DebugDebugDisplay一样也是一种格式化方法。继续往下看到note这行:

= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`

编译提示我们添加#[derive(Debug)]到代码中或是手动实现Debug这个trait。这里使用前一种(后一种下一篇文章会讲):

#[derive(Debug)]  
struct Rectangle {  
    width: u32,  
    length: u32,  
}  
  
fn main() {  
    let rectangle = Rectangle{  
        width: 30,  
        length: 50,  
    };  
    println!("{}", area(&rectangle));  
    println!("{:?}", rectangle);  
}  
  
fn area(dim:&Rectangle) -> u32 {  
    dim.width * dim.length  
}

输出:

1500
Rectangle { width: 30, length: 50 }

这次就可以成功通过了。Rust本身包含了打印调试信息的功能(也就是debug信息的功能),但必须为自己代码中的结构体显式地选择这一功能,所以要在定义结构体前加上#[derive(Debug)]这个注解。这种输出把结构体的名字、字段的名字及值都显示出来了。

有的时候结构体里有很多的字段,这时候{:?}说打印出的横向排列的字段就没有那么易读。如果想要输出更加易读,那就把{:?}改为{:#?}

#[derive(Debug)]  
struct Rectangle {  
    width: u32,  
    length: u32,  
}  
  
fn main() {  
    let rectangle = Rectangle{  
        width: 30,  
        length: 50,  
    };  
    println!("{}", area(&rectangle));  
    println!("{:#?}", rectangle);  
}  
  
fn area(dim:&Rectangle) -> u32 {  
    dim.width * dim.length  
}

输出:

1500
Rectangle {
    width: 30,
    length: 50,
}

这个输出中字段就是纵向排列,对于有很多字段的结构体来说更加易读。

实际上Rust提供了很多trait让我们可以进行derive(派生),这些trait可以为自定义类型添加很多功能。所有的trait和它们的行为都可以在官方指南中找到,我把网址链接附在这里。

在上边的代码中就是让Rectangle这个struct派生于Debug这个trait,所以在打印时就可以使用调试模式。

再举个例子,假设你有一个表示点坐标的结构体:

#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point1 = Point { x: 1, y: 2 };
    let point2 = point1.clone();
    println!("{:?}", point1); // 使用 Debug 特质打印 Point
    assert_eq!(point1, point2); // 使用 PartialEq 特质比较两个 Point
}

在这个例子中:

  • #[derive(Debug)]允许你使用{:?}格式化规范来打印Point结构体的实例。
  • #[derive(Clone)]允许你创建一个Point实例的副本。
  • #[derive(PartialEq)]允许你比较两个Point实例是否相等。

http://www.kler.cn/a/458766.html

相关文章:

  • 安全见闻(一)
  • TypeScript 后端开发中的热重载编译处理
  • goView二开低代码平台1.0
  • 计算机网络-L2TP VPN基础实验配置
  • 《HelloGitHub》第 105 期
  • 2024年RAG:回顾与展望
  • 使用 Spring Boot 和 GraalVM 的原生镜像
  • Ubuntu 22.04 升级 24.04 问题记录
  • 使用 OpenCV 在图像中添加文字
  • oscp备战系列-Kioptrix2014
  • Oracle Managed Files(OMF)
  • sqlite3 python 如何使用
  • 文件上传绕过最新版安全狗
  • Android 屏幕适配autosize
  • springboot514基于SpringBoot的家电销售展示平台(论文+源码)_kaic
  • kkFileView集成springboot:使用自定义预览接口(非minio预览接口),发现无法预览资源
  • nginx 基于 geoip 模块限制地区访问
  • Linux安装Docker完整教程
  • YoloV9改进策略:Head改进|DynamicHead,利用注意力机制统一目标检测头部|即插即用
  • 大模型时代的NL2SQL初探
  • 二战-从头开始
  • 面试知识点汇总_03
  • HarmonyOS Next 应用元服务开发-应用接续动态配置迁移按需迁移页面
  • NVIDIA - 人形机器人扼要
  • tcp_rcv_synsent_state_process函数
  • mysql日志(