From 68eb5a1e517932db45cf80eee607aa8aa411cba1 Mon Sep 17 00:00:00 2001 From: sunface Date: Tue, 15 Mar 2022 17:22:52 +0800 Subject: [PATCH] add chapter [Debug and Display] --- ChangeLog.md | 1 + solutions/formatted-output/debug-display.md | 78 ++++++++++ src/SUMMARY.md | 5 +- src/formatted-output.md | 1 - src/formatted-output/debug-display.md | 154 ++++++++++++++++++++ src/formatted-output/formatting.md | 1 + src/formatted-output/intro.md | 78 ++++++++++ src/formatted-output/println.md | 1 + 8 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 solutions/formatted-output/debug-display.md delete mode 100644 src/formatted-output.md create mode 100644 src/formatted-output/debug-display.md create mode 100644 src/formatted-output/formatting.md create mode 100644 src/formatted-output/intro.md create mode 100644 src/formatted-output/println.md diff --git a/ChangeLog.md b/ChangeLog.md index e4cc55d..5dc3a22 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -3,6 +3,7 @@ ### 2022-03-15 - Add [Comments and Docs](https://practice.rs/comments-docs.html) +- Add [Fighting with Compiler](https://practice.rs/fight-compiler/intro.html) ### 2022-03-14 diff --git a/solutions/formatted-output/debug-display.md b/solutions/formatted-output/debug-display.md new file mode 100644 index 0000000..0859ad9 --- /dev/null +++ b/solutions/formatted-output/debug-display.md @@ -0,0 +1,78 @@ +1. +```rust +#[derive(Debug)] +struct Structure(i32); + +fn main() { + // Types in std and Rust have implemented the fmt::Debug trait + println!("{:?} months in a year.", 12); + + println!("Now {:?} will print!", Structure(3)); +} +``` +2. +```rust +#[derive(Debug)] +struct Person { + name: String, + age: u8 +} + +fn main() { + let person = Person { name: "Sunface".to_string(), age: 18 }; + + println!("{:#?}", person); +} +``` + +3. +```rust +use std::fmt; + +struct Structure(i32); + +struct Deep(Structure); +impl fmt::Debug for Deep { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.0.0) + } +} + +fn main() { + // The problem with `derive` is there is no control over how + // the results look. What if I want this to just show a `7`? + + /* Make it output: Now 7 will print! */ + println!("Now {:?} will print!", Deep(Structure(7))); +} +``` + +4 +```rust +use std::fmt; + +struct Point2D { + x: f64, + y: f64, +} + +impl fmt::Display for Point2D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Display: {} + {}i", self.x, self.y) + } +} + +impl fmt::Debug for Point2D { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Debug: Complex {{ real: {:?}, imag: {:?} }}", self.x, self.y) + } +} + +fn main() { + + let point = Point2D { x: 3.3, y: 7.2 }; + println!("{}", point); + + println!("{:?}", point); +} +``` \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 1ac15c1..b7777c9 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -44,7 +44,10 @@ - [Module](crate-module/module.md) - [Advanced use and pub](crate-module/use-pub.md) - [Comments and Docs](comments-docs.md) -- [Formatted output](formatted-output.md) +- [Formatted output](formatted-output/intro.md) + - [Debug and Display](formatted-output/debug-display.md) + - [println!, eprintln! and format!](formatted-output/println.md) + - [formating](formatted-output/formatting.md) - [Lifetime TODO](lifetime/intro.md) - [basic](lifetime/basic.md) - [&'static and T: 'static](lifetime/static.md) diff --git a/src/formatted-output.md b/src/formatted-output.md deleted file mode 100644 index 5cd8a3b..0000000 --- a/src/formatted-output.md +++ /dev/null @@ -1 +0,0 @@ -# Formatted output diff --git a/src/formatted-output/debug-display.md b/src/formatted-output/debug-display.md new file mode 100644 index 0000000..db3df10 --- /dev/null +++ b/src/formatted-output/debug-display.md @@ -0,0 +1,154 @@ +# Debug and Display +All types which want to be printable must implement the `std::fmt` formatting trait: `std::fmt::Debug` or `std::fmt::Display`. + +Automatic implementations are only provided for types such as in the `std` library. All others have to be manually implemented. + +## Debug +The implementation of `Debug` is very straightfoward: All types can `derive` the `std::fmt::Debug` implementation. This is not true for `std::fmt::Display` which must be manually implemented. + +`{:?}` must be used to print out the type which has implemented the `Debug` trait. + +```rust +// This structure cannot be printed either with `fmt::Display` or +// with `fmt::Debug`. +struct UnPrintable(i32); + +// To make this struct printable with `fmt::Debug`, we can derive the automatic implementations provided by Rust +#[derive(Debug)] +struct DebugPrintable(i32); +``` + +1. 🌟 +```rust,editable + +/* Fill in the blanks and Fix the errors */ +struct Structure(i32); + +fn main() { + // Types in std and Rust have implemented the fmt::Debug trait + println!("__ months in a year.", 12); + + println!("Now __ will print!", Structure(3)); +} +``` + +2. 🌟🌟 So `fmt::Debug` definitely makes one type printable, but sacrifices some elegance. Maybe we can get more elegant by replacing `{:?}` with something else( but not `{}` !) +```rust,editable +#[derive(Debug)] +struct Person { + name: String, + age: u8 +} + +fn main() { + let person = Person { name: "Sunface".to_string(), age: 18 }; + + /* Make it output: + Person { + name: "Sunface", + age: 18, + } + */ + println!("{:?}", person); +} +``` + +3. 🌟🌟 We can also manually implement `Debug` trait for our types +```rust,editable + +#[derive(Debug)] +struct Structure(i32); + +#[derive(Debug)] +struct Deep(Structure); + + +fn main() { + // The problem with `derive` is there is no control over how + // the results look. What if I want this to just show a `7`? + + /* Make it output: Now 7 will print! */ + println!("Now {:?} will print!", Deep(Structure(7))); +} +``` + +## Display +Yeah, `Debug` is simple and easy to use. But sometimes we want to customize the output appearance of our type. This is where `Display` really shines. + +Unlike `Debug`, there is no way to derive the implementation of the `Display` trait, we have to manually implement it. + +Anotherthing to note: the placefolder for `Display` is `{}` not `{:?}`. + +4. 🌟🌟 +```rust,editable +use std::fmt; + +struct Point2D { + x: f64, + y: f64, +} + +impl fmt::Display for Point2D { + /* Implement.. */ +} + +impl fmt::Debug for Point2D { + /* Implement.. */ +} + +fn main() { + + let point = Point2D { x: 3.3, y: 7.2 }; + /* Make it output: + Display: 3.3 + 7.2i + */ + println!("{}", point); + + /* Make it output: + Debug: Complex { real: 3.3, imag: 7.2 } + */ + println!("{:?}", point); +} +``` + + +### Example + +Implementing `fmt::Display` for a structure whose elements must be handled separately is triky. The problem is each `write!` generates a `fmt::Result` which must be handled in the same place. + +Fortunately, Rust provides the `?` operator to help us eliminate some unnecessary codes for deaing with `fmt::Result`. + +```rust,editable +use std::fmt; // Import the `fmt` module. + +// Define a structure named `List` containing a `Vec`. +struct List(Vec); + +impl fmt::Display for List { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // Extract the value using tuple indexing, + // and create a reference to `vec`. + let vec = &self.0; + + write!(f, "[")?; + + // Iterate over `v` in `vec` while enumerating the iteration + // count in `count`. + for (count, v) in vec.iter().enumerate() { + // For every element except the first, add a comma. + // Use the ? operator to return on errors. + if count != 0 { write!(f, ", ")?; } + write!(f, "{}", v)?; + } + + // Close the opened bracket and return a fmt::Result value. + write!(f, "]") + } +} + +fn main() { + let v = List(vec![1, 2, 3]); + println!("{}", v); +} +``` + diff --git a/src/formatted-output/formatting.md b/src/formatted-output/formatting.md new file mode 100644 index 0000000..76c191f --- /dev/null +++ b/src/formatted-output/formatting.md @@ -0,0 +1 @@ +# formating diff --git a/src/formatted-output/intro.md b/src/formatted-output/intro.md new file mode 100644 index 0000000..c8b71b8 --- /dev/null +++ b/src/formatted-output/intro.md @@ -0,0 +1,78 @@ +# Formatted output + +Printing is handled by a series of [`macros`][macros] defined in [`std::fmt`][fmt] +some of which include: + +* `format!`: write formatted text to [`String`][string] +* `print!`: same as `format!` but the text is printed to the console (io::stdout). +* `println!`: same as `print!` but a newline is appended. +* `eprint!`: same as `format!` but the text is printed to the standard error (io::stderr). +* `eprintln!`: same as `eprint!`but a newline is appended. + +All parse text in the same fashion. As a plus, Rust checks formatting +correctness at compile time. + +```rust,editable,ignore,mdbook-runnable +fn main() { + // In general, the `{}` will be automatically replaced with any + // arguments. These will be stringified. + println!("{} days", 31); + + // Without a suffix, 31 becomes an i32. You can change what type 31 is + // by providing a suffix. The number 31i64 for example has the type i64. + + // There are various optional patterns this works with. Positional + // arguments can be used. + println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); + + // As can named arguments. + println!("{subject} {verb} {object}", + object="the lazy dog", + subject="the quick brown fox", + verb="jumps over"); + + // Special formatting can be specified after a `:`. + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + + // You can right-align text with a specified width. This will output + // " 1". 5 white spaces and a "1". + println!("{number:>width$}", number=1, width=6); + + // You can pad numbers with extra zeroes. This will output "000001". + println!("{number:0>width$}", number=1, width=6); + + // Rust even checks to make sure the correct number of arguments are + // used. + println!("My name is {0}, {1} {0}", "Bond"); + // FIXME ^ Add the missing argument: "James" + + // Create a structure named `Structure` which contains an `i32`. + #[allow(dead_code)] + struct Structure(i32); + + // However, custom types such as this structure require more complicated + // handling. This will not work. + println!("This struct `{}` won't print...", Structure(3)); + // FIXME ^ Comment out this line. + + // For Rust 1.58 and above, you can directly capture the argument from + // surrounding variable. Just like the above, this will output + // " 1". 5 white spaces and a "1". + let number: f64 = 1.0; + let width: usize = 6; + println!("{number:>width$}"); +} +``` + +[`std::fmt`][fmt] contains many [`traits`][traits] which govern the display +of text. The base form of two important ones are listed below: + +* `fmt::Debug`: Uses the `{:?}` marker. Format text for debugging purposes. +* `fmt::Display`: Uses the `{}` marker. Format text in a more elegant, user +friendly fashion. + +Here, we used `fmt::Display` because the std library provides implementations +for these types. To print text for custom types, more steps are required. + +Implementing the `fmt::Display` trait automatically implements the +[`ToString`] trait which allows us to [convert] the type to [`String`][string]. diff --git a/src/formatted-output/println.md b/src/formatted-output/println.md new file mode 100644 index 0000000..153c635 --- /dev/null +++ b/src/formatted-output/println.md @@ -0,0 +1 @@ +# println!, eprintln! and format!