add #[derive(debug)] to struct.md

pull/494/merge
sunface 3 years ago
parent aac4a1f9ea
commit 237e39c97b

@ -264,7 +264,6 @@
- [C-表达式](appendix/expressions.md)
- [D-派生特征 trait](appendix/derive.md)
- [E-prelude 模块 todo](appendix/prelude.md)
<!-- - [F-难点索引](appendix/difficulties.md) -->
- [F-Rust 版本说明](appendix/rust-version.md)
- [G-Rust 更新版本列表](appendix/rust-versions/intro.md)
- [1.58](appendix/rust-versions/1.58.md)

@ -254,3 +254,138 @@ help: consider introducing a named lifetime parameter
未来在[生命周期](../../advance/lifetime/basic.md)中会讲到如何修复这个问题以便在结构体中存储引用,不过在那之前,我们会避免在结构体中使用引用类型。
## 使用 `#[derive(Debug)]` 来打印结构体的信息
在前面的代码中我们使用 `#[derive(Debug)]` 对结构体进行了标记,这样才能使用 `println("{:?}", s)` 的方式对其进行打印输出,如果不加,看看会发生什么:
```rust
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {}", rect1);
}
```
首先可以观察到,上面使用了 `{}` 而不是之前的 `{:}`,运行后报错:
```shell
error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
```
提示我们结构体 `Rectangle` 没有实现 `Display` 特征,这是因为如果我们使用 `{}` 来格式化输出,那对应的类型就必须实现 `Display` 特征,以前学习的基本类型,都默认实现了该特征:
```rust
fn main() {
let v = 1;
let b = true;
println!("{}, {}",v,b);
}
```
上面代码不会报错,那么结构体为什么不默认实现 `Display` 特征呢原因在于结构体较为复杂例如考虑以下问题你想要逗号对字段进行分割吗需要括号吗加在什么地方所有的字段都应该显示类似的还有很多由于这种复杂性Rust 不希望猜测我们想要的是什么,而是把选择权交给我们自己来实现:如果要用 `{}` 的方式打印结构体,那就自己实现 `Display` 特征。
接下来继续阅读报错:
```shell
= 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
```
上面提示我们使用 `{:?}` 来试试,这个方式我们在本文的前面也见过,下面来试试:
```rust
println!("rect1 is {:?}", rect1);
```
可是依然无情报错了:
```shell
error[E0277]: `Rectangle` doesn't implement `Debug`
```
好在,聪明的编译器又一次给出了提示:
```shell
= help: the trait `Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`
```
让我们实现 `Debug` 特征Oh No就是不想实现 `Display` 特征,才用的 `{:?}`,怎么又要实现 `Debug`,但是仔细看,提示中有一行: `add #[derive(Debug)] to Rectangle` 哦?这不就是我们前文一直在使用的吗?
首先Rust 默认不会为我们实现 `Debug`,为了实现,有两种方式可以选择:
- 手动实现
- 使用 `derive` 派生实现
后者简单的多,但是也有限制,具体见[附录](https://course.rs/appendix/derive.html),这里我们就不再深入讲解,来看看该如何使用:
```rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("rect1 is {:?}", rect1);
}
```
此时运行程序,就不再有错误,输出如下:
```shell
$ cargo run
rect1 is Rectangle { width: 30, height: 50 }
```
这个输出格式看上去也不赖嘛,虽然未必是最好的。这种格式是 Rust 自动为我们提供的实现,看上基本就跟结构体的定义形式一样。
当结构体较大时,我们可能希望能够有更好的输出表现,此时可以使用 `{:#?}` 来替代 `{:?}`,输出如下:
```shell
rect1 is Rectangle {
width: 30,
height: 50,
}
```
此时结构体的输出跟我们创建时候的代码几乎一模一样了!当然,如果大家还是不满足,那最好还是自己实现 `Display` 特征,以向用户更美的展示你的私藏结构体。关于格式化输出的更多内容,我们强烈推荐看看这个[章节](https://course.rs/basic/formatted-output.html#debug-特征)。
还有一个简单的输出 debug 信息的方法,那就是使用 [`dbg!` 宏](https://doc.rust-lang.org/std/macro.dbg.html),它会拿走表达式的所有权,然后打出相应的文件名、行号等 debug 信息,当然还有我们需要的表达式的求值结果。**除次之外,它最终还会把表达式值的所有权返回!**
> `dbg!` 输出到的是标准错误输出 `stderr`,而 `println!` 输出到标准输出 `stdout`
下面的例子中清晰的展示了 `dbg!` 如何在打印出信息的同时,还把表达式的值赋给了 `width`:
```rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
dbg!(&rect1);
}
```
最终的 debug 输出如下:
```shell
$ cargo run
[src/main.rs:10] 30 * scale = 60
[src/main.rs:14] &rect1 = Rectangle {
width: 60,
height: 50,
}
```
可以看到,我们想要的 debug 信息几乎都有了:代码所在的文件名、行号、表达式以及表达式的值,简直完美!
Loading…
Cancel
Save