diff --git a/en/src/std/String.md b/en/src/std/String.md index 2c0923c4d..77e311b02 100644 --- a/en/src/std/String.md +++ b/en/src/std/String.md @@ -1 +1 @@ -# String +# String TODO diff --git a/zh-CN/src/SUMMARY.md b/zh-CN/src/SUMMARY.md index e12bfec9c..6924c8114 100644 --- a/zh-CN/src/SUMMARY.md +++ b/zh-CN/src/SUMMARY.md @@ -45,41 +45,48 @@ - [模块 Module](crate-module/module.md) - [使用use引入模块及受限可见性](crate-module/use-pub.md) - [注释和文档](comments-docs.md) -- [格式化输出](formatted-output.md) +- [格式化输出](formatted-output/intro.md) + - [println! 和 format!](formatted-output/println.md) + - [Debug 和 Display](formatted-output/debug-display.md) + - [格式化](formatted-output/formatting.md) - [生命周期](lifetime/intro.md) - [生命周期基础](lifetime/basic.md) - [&'static 和 T: 'static](lifetime/static.md) - [深入生命周期](lifetime/advance.md) -- [函数式编程: 闭包、迭代器 todo](functional-programing/intro.md) +- [函数式编程: 闭包、迭代器](functional-programing/intro.md) - [闭包 Closure](functional-programing/closure.md) - [迭代器 Iterator](functional-programing/iterator.md) -- [newtype 和 Sized todo](newtype-sized.md) -- [智能指针 todo](smart-pointers/intro.md) +- [newtype 和 DST](newtype-sized.md) +- [智能指针 TODO](smart-pointers/intro.md) - [Box](smart-pointers/box.md) - [Deref](smart-pointers/deref.md) - [Drop](smart-pointers/drop.md) - [Rc and Arc](smart-pointers/rc-arc.md) - [Cell and RefCell](smart-pointers/cell-refcell.md) -- [Weak 和循环引用todo](weak.md) -- [自引用 todo](self-referential.md) -- [多线程 todo](threads/intro.md) +- [Weak 和循环引用 TODO](weak.md) +- [自引用 TODO](self-referential.md) +- [多线程 TODO](threads/intro.md) - [多线程基础](threads/basic-using.md) - [消息传递](threads/message-passing.md) - [线程同步:锁、Condvar和信号量](threads/sync.md) - [线程同步:Atomic](threads/atomic.md) - [Send 和 Sync](threads/send-sync.md) -- [全局变量 todo](global-variables.md) -- [错误处理 todo](errors.md) +- [全局变量 TODO](global-variables.md) +- [错误处理 TODO](errors.md) - [Unsafe doing](unsafe/intro.md) - [内联汇编](unsafe/inline-asm.md) -- [macro 宏 todo](macro.md) -- [测试 todo](tests/intro.md) +- [macro 宏 TODO](macro.md) +- [测试 TODO](tests/intro.md) - [编写测试及控制执行](tests/write-tests.md) - [基准性能测试 Benchmark](tests/benchmark.md) - [单元测试及集成测试](tests/unit-integration.md) - [断言 Assertions](tests/assertions.md) -- [Async/Await 异步编程 todo](async/intro.md) +- [Async/Await 异步编程 TODO](async/intro.md) - [async 和 await!](async/async-await.md) - [Future](async/future.md) - [Pin 和 Unpin](async/pin-unpin.md) - [Stream 流处理](async/stream.md) +- [标准库 TODO](std/intro.md) + - [String](std/String.md) +- [与编译器战斗](fight-compiler/intro.md) + - [借用](fight-compiler/borrowing.md) diff --git a/zh-CN/src/crate-module/module.md b/zh-CN/src/crate-module/module.md index 008671313..bc52b0423 100644 --- a/zh-CN/src/crate-module/module.md +++ b/zh-CN/src/crate-module/module.md @@ -135,32 +135,32 @@ pub mod back_of_house { ```rust,editable // in src/lib.rs -// IMPLEMENT... +// 待实现... ``` ```rust,editable // in src/back_of_house.rs -// IMPLEMENT... +// 待实现... ``` ```rust,editable // in src/front_of_house/mod.rs -// IMPLEMENT... +// 待实现... ``` ```rust,editable // in src/front_of_house/hosting.rs -// IMPLEMENT... +// 待实现... ``` ```rust,editable // in src/front_of_house/serving.rs -// IMPLEMENT... +// 待实现... ``` ### 从二进制包中访问库包的代码 diff --git a/zh-CN/src/fight-compiler/borrowing.md b/zh-CN/src/fight-compiler/borrowing.md new file mode 100644 index 000000000..8223636fd --- /dev/null +++ b/zh-CN/src/fight-compiler/borrowing.md @@ -0,0 +1,31 @@ +# 借用 + +1. 🌟🌟 +```rust,editable +// 不删除任何代码,修复错误 +struct test { + list: Vec, + a: i32 +} + +impl test { + pub fn new() -> Self { + test { list:vec![1,2,3,4,5,6,7], a:0 } + } + + pub fn run(&mut self) { + for i in self.list.iter() { + self.do_something(*i) + } + + } + + pub fn do_something(&mut self, n: i32) { + self.a = n; + } +} + +fn main() {} +``` +> 参考答案:(solutions 路径),仅在需要时查看。 + diff --git a/zh-CN/src/fight-compiler/intro.md b/zh-CN/src/fight-compiler/intro.md new file mode 100644 index 000000000..9626b8107 --- /dev/null +++ b/zh-CN/src/fight-compiler/intro.md @@ -0,0 +1,5 @@ +# 与编译器战斗 +在日常编码中与编译器斗智斗勇很常见,尤其是对 Rust 还不熟悉的时候。 + +本章给出一些练习,帮助你尽量避免这些状况,降低学习曲线。 + diff --git a/zh-CN/src/formatted-output/debug-display.md b/zh-CN/src/formatted-output/debug-display.md new file mode 100644 index 000000000..fee22b017 --- /dev/null +++ b/zh-CN/src/formatted-output/debug-display.md @@ -0,0 +1,149 @@ +# Debug 与 Display +想让类型可打印,需要实现 `std::fmt` 的格式化 trait:`std::fmt::Debug` 或 `std::fmt::Display`。 + +标准库内置类型会自动实现,其他类型需手动实现。 + +## Debug +`Debug` 很直接:几乎所有类型都能 `derive` 得到 `std::fmt::Debug`;`Display` 则必须手写实现。 + +实现了 `Debug` 的类型必须用 `{:?}` 占位符打印。 + +```rust +// 既不能 Display 也不能 Debug +struct UnPrintable(i32); + +// 通过 derive 获取 Debug +#[derive(Debug)] +struct DebugPrintable(i32); +``` + +1. 🌟 +```rust,editable + +/* 填空并修复错误 */ +struct Structure(i32); + +fn main() { + // std 里的类型都已实现 fmt::Debug + println!("__ months in a year.", 12); + + println!("Now __ will print!", Structure(3)); +} +``` + +2. 🌟🌟 `fmt::Debug` 让类型可打印,但不够优雅;能否换个占位符(不是 `{}`)让输出更美观? +```rust,editable +#[derive(Debug)] +struct Person { + name: String, + age: u8 +} + +fn main() { + let person = Person { name: "Sunface".to_string(), age: 18 }; + + /* 让它输出: + Person { + name: "Sunface", + age: 18, + } + */ + println!("{:?}", person); +} +``` + +3. 🌟🌟 也可以手写 `Debug` 实现 +```rust,editable + +#[derive(Debug)] +struct Structure(i32); + +#[derive(Debug)] +struct Deep(Structure); + + +fn main() { + // derive 没法控制输出格式,如果只想打印一个 `7` 呢? + + /* 让它输出: Now 7 will print! */ + println!("Now {:?} will print!", Deep(Structure(7))); +} +``` + +## Display +`Debug` 简单易用,但有时我们要自定义展示格式,这时就需要 `Display`。 + +`Display` 不能 derive,只能手写实现;占位符是 `{}` 而不是 `{:?}`。 + +4. 🌟🌟 +```rust,editable + +/* 补全实现 */ +use std::fmt; + +struct Point2D { + x: f64, + y: f64, +} + +impl fmt::Display for Point2D { + /* 待实现... */ +} + +impl fmt::Debug for Point2D { + /* 待实现... */ +} + +fn main() { + let point = Point2D { x: 3.3, y: 7.2 }; + assert_eq!(format!("{}",point), "Display: 3.3 + 7.2i"); + assert_eq!(format!("{:?}",point), "Debug: Complex { real: 3.3, imag: 7.2 }"); + + println!("Success!"); +} +``` + + +### `?` 运算符 + +为包含多个字段的结构体实现 `Display` 时,每个 `write!` 都返回 `fmt::Result`,需要在同一函数里处理。 + +`?` 运算符可以帮助我们简化这些错误处理。 + +5. 🌟🌟 +```rust,editable + +/* 让它运行 */ +use std::fmt; + +struct List(Vec); + +impl fmt::Display for List { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // 取出内部的 vec + let vec = &self.0; + + write!(f, "[")?; + + // 枚举迭代,得到索引 count 与值 v + for (count, v) in vec.iter().enumerate() { + // 不是第一个元素就加逗号 + if count != 0 { write!(f, ", ")?; } + write!(f, "{}", v)?; + } + + // 收尾 + write!(f, "]") + } +} + +fn main() { + let v = List(vec![1, 2, 3]); + assert_eq!(format!("{}",v), "[0: 1, 1: 2, 2: 3]"); + println!("Success!"); +} +``` + + +> 参考答案:(solutions 路径),仅在需要时查看。 + diff --git a/zh-CN/src/formatted-output.md b/zh-CN/src/formatted-output/formatting.md similarity index 99% rename from zh-CN/src/formatted-output.md rename to zh-CN/src/formatted-output/formatting.md index ec45857ac..ffbcb12a5 100644 --- a/zh-CN/src/formatted-output.md +++ b/zh-CN/src/formatted-output/formatting.md @@ -182,3 +182,4 @@ fn main() { ``` > 你可以在[这里](https://github.com/sunface/rust-by-practice/blob/master/solutions/formatted-output/formatting.md)找到答案(在 solutions 路径下) + diff --git a/zh-CN/src/formatted-output/intro.md b/zh-CN/src/formatted-output/intro.md new file mode 100644 index 000000000..07fbb3f95 --- /dev/null +++ b/zh-CN/src/formatted-output/intro.md @@ -0,0 +1,56 @@ +# 格式化输出 + +```rust,editable,ignore,mdbook-runnable +fn main() { + // `{}` 会被参数自动替换并转成字符串 + println!("{} days", 31); + + // 默认 31 是 i32,可用后缀改类型,比如 31i64 + + // 可以使用位置参数 + println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); + + // 也可以使用具名参数 + println!("{subject} {verb} {object}", + object="the lazy dog", + subject="the quick brown fox", + verb="jumps over"); + + // `:` 后可指定特殊格式 + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + + // 右对齐并指定宽度,输出 " 1"(5 个空格 + "1") + println!("{number:>width$}", number=1, width=6); + + // 用 0 填充,输出 "000001" + println!("{number:0>width$}", number=1, width=6); + + // Rust 会检查参数个数是否匹配 + println!("My name is {0}, {1} {0}", "Bond"); + // FIXME ^ 补上缺失的参数 "James" + + // 定义一个包含 i32 的结构体 + #[allow(dead_code)] + struct Structure(i32); + + // 自定义类型需要实现格式化,否则不会工作 + println!("This struct `{}` won't print...", Structure(3)); + // FIXME ^ 注释掉这一行 + + // 1.58+ 支持直接捕获外部变量 + // 同样输出 " 1"(5 个空格 + "1") + let number: f64 = 1.0; + let width: usize = 6; + println!("{number:>width$}"); +} +``` + +[`std::fmt`][fmt] 提供了一组控制文本展示的 [`traits`][traits],两类常用基石: + +* `fmt::Debug`:使用 `{:?}`,偏调试输出 +* `fmt::Display`:使用 `{}`,偏用户友好输出 + +这里用 `fmt::Display` 是因为标准库已为这些类型实现它;自定义类型要打印则需要额外步骤。 + +为类型实现 `fmt::Display` 也会自动实现 [`ToString`],可用于把类型 [转换][convert] 成 [`String`][string]。 + diff --git a/zh-CN/src/formatted-output/println.md b/zh-CN/src/formatted-output/println.md new file mode 100644 index 000000000..932a2690f --- /dev/null +++ b/zh-CN/src/formatted-output/println.md @@ -0,0 +1,40 @@ +# println! 与 format! +[`std::fmt`][fmt] 中有一组用于打印的宏,例如: + +* `format!`:写入到 [`String`][string] +* `print!`:同 `format!`,但输出到 stdout +* `println!`:`print!` 并追加换行 +* `eprint!`:输出到 stderr +* `eprintln!`:`eprint!` 并追加换行 + +所有这些宏的格式解析方式一致,且编译期检查格式正确性。 + +## `format!` +1.🌟 +```rust,editable + +fn main() { + let s1 = "hello"; + /* 填空 */ + let s = format!(__); + assert_eq!(s, "hello, world!"); +} +``` + +## `print!`、`println!` +2.🌟 +```rust,editable + +fn main() { + /* 填空,让输出变为: + Hello world, I am + Sunface! + */ + __("hello world, "); + __("I am"); + __("Sunface!"); +} +``` + +> 参考答案在这里(solutions 路径):,仅在需要时再看哦 :) + diff --git a/zh-CN/src/functional-programing/intro.md b/zh-CN/src/functional-programing/intro.md index 6f326b86e..66190577c 100644 --- a/zh-CN/src/functional-programing/intro.md +++ b/zh-CN/src/functional-programing/intro.md @@ -1 +1,4 @@ -# Functional programing +# 函数式编程 +学习资料: +- 英文版:[Rust Book 13](https://doc.rust-lang.org/book/ch13-00-functional-features.html) +- 中文版:[Rust 语言圣经 - 函数式编程:闭包和迭代器](https://course.rs/advance/functional-programing/intro.html) diff --git a/zh-CN/src/functional-programming/closure.md b/zh-CN/src/functional-programming/closure.md deleted file mode 100644 index fb73740a7..000000000 --- a/zh-CN/src/functional-programming/closure.md +++ /dev/null @@ -1,51 +0,0 @@ -# Closure - -下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案: - -```rust -struct Cacher -where - T: Fn(E) -> E, - E: Copy -{ - query: T, - value: Option, -} - -impl Cacher -where - T: Fn(E) -> E, - E: Copy -{ - fn new(query: T) -> Cacher { - Cacher { - query, - value: None, - } - } - - fn value(&mut self, arg: E) -> E { - match self.value { - Some(v) => v, - None => { - let v = (self.query)(arg); - self.value = Some(v); - v - } - } - } -} -fn main() { - -} - -#[test] -fn call_with_different_values() { - let mut c = Cacher::new(|a| a); - - let v1 = c.value(1); - let v2 = c.value(2); - - assert_eq!(v2, 1); -} -``` \ No newline at end of file diff --git a/zh-CN/src/functional-programming/intro.md b/zh-CN/src/functional-programming/intro.md deleted file mode 100644 index dca041e2d..000000000 --- a/zh-CN/src/functional-programming/intro.md +++ /dev/null @@ -1 +0,0 @@ -# Functional Programming diff --git a/zh-CN/src/newtype-sized.md b/zh-CN/src/newtype-sized.md index fc3f074c6..913ebe5f1 100644 --- a/zh-CN/src/newtype-sized.md +++ b/zh-CN/src/newtype-sized.md @@ -1 +1,203 @@ # newtype and Sized + +## Newtype +孤儿规则要求:只有当 trait 或类型其一属于当前 crate 时,才能为类型实现该 trait。 + +**newtype 模式** 通过定义一个 **元组结构体** 新类型,帮助绕开这一限制。 + +1. 🌟 +```rust,editable +use std::fmt; + +/* 定义 Wrapper 类型 */ +__; + +// Display 是外部 trait +impl fmt::Display for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[{}]", self.0.join(", ")) + } +} + +fn main() { + // Vec 是外部类型,不能直接为 Vec 实现外部 trait Display + let w = Wrapper(vec![String::from("hello"), String::from("world")]); + println!("w = {}", w); +} +``` + +2. 🌟 隐藏原类型的方法 +```rust,editable +/* 让代码通过编译 */ +struct Meters(u32); + +fn main() { + let i: u32 = 2; + assert_eq!(i.pow(2), 4); + + let n = Meters(i); + // `pow` 定义在 u32 上,直接调用会报错 + assert_eq!(n.pow(2), 4); +} +``` + +3. 🌟🌟 `newtype` 能在编译期保证传入的值类型正确 +```rust,editable +/* 让它工作 */ +struct Years(i64); + +struct Days(i64); + +impl Years { + pub fn to_days(&self) -> Days { + Days(self.0 * 365) + } +} + + +impl Days { + pub fn to_years(&self) -> Years { + Years(self.0 / 365) + } +} + +// 检查年龄(单位:年),必须接收 Years +fn old_enough(age: &Years) -> bool { + age.0 >= 18 +} + +fn main() { + let age = Years(5); + let age_days = age.to_days(); + println!("Old enough {}", old_enough(&age)); + println!("Old enough {}", old_enough(&age_days)); +} +``` + +4. 🌟🌟 +```rust,editable +use std::ops::Add; +use std::fmt::{self, format}; + +struct Meters(u32); +impl fmt::Display for Meters { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "There are still {} meters left", self.0) + } +} + +impl Add for Meters { + type Output = Self; + + fn add(self, other: Meters) -> Self { + Self(self.0 + other.0) + } +} +fn main() { + let d = calculate_distance(Meters(10), Meters(20)); + assert_eq!(format!("{}",d), "There are still 30 meters left"); +} + +/* 实现 calculate_distance */ +fn calculate_distance +``` + +## 类型别名 (Type alias) +类型别名可以提升代码可读性。 + +```rust +type Thunk = Box; + +let f: Thunk = Box::new(|| println!("hi")); + +fn takes_long_type(f: Thunk) { + // --snip-- +} + +fn returns_long_type() -> Thunk { + // --snip-- +} +``` + +```rust +type Result = std::result::Result; +``` + +与 newtype 不同,类型别名不会生成新类型,因此下面的代码合法: +```rust +type Meters = u32; + +let x: u32 = 5; +let y: Meters = 5; + +println!("x + y = {}", x + y); +``` + +5. 🌟 +```rust,editable +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +/* 填空 */ +__ + +fn main() { + // 可以用别名访问枚举值,避免冗长名字 + let x = Operations::Add; +} +``` + +6. 🌟🌟 Rust 有一些保留的别名,其中一个可在 `impl` 中使用。 +```rust,editable +enum VeryVerboseEnumOfThingsToDoWithNumbers { + Add, + Subtract, +} + +impl VeryVerboseEnumOfThingsToDoWithNumbers { + fn run(&self, x: i32, y: i32) -> i32 { + match self { + __::Add => x + y, + __::Subtract => x - y, + } + } +} +``` + +## DST 与不定长类型 +概念较复杂,这里不展开,可参考 [The Book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=DST#dynamically-sized-types-and-the-sized-trait)。 + +7. 🌟🌟🌟 动态长度数组属于 DST,无法直接使用 +```rust,editable +/* Make it work with const generics */ +fn my_function(n: usize) -> [u32; usize] { + [123; n] +} + +fn main() { + let arr = my_function(); + println!("{:?}",arr); +} +``` + +8. 🌟🌟 Slice 本身是 unsized,但它的引用是定长的。 +```rust,editable +/* Make it work with slice references */ +fn main() { + let s: str = "Hello there!"; + + let arr: [u8] = [1, 2, 3]; +} +``` + +9. 🌟🌟 Trait 也是 unsized 类型 +```rust,editable +/* 用两种方式让它工作 */ +use std::fmt::Display; +fn foobar(thing: Display) {} + +fn main() { +} +``` diff --git a/zh-CN/src/std/String.md b/zh-CN/src/std/String.md new file mode 100644 index 000000000..967e752a5 --- /dev/null +++ b/zh-CN/src/std/String.md @@ -0,0 +1,2 @@ +# String TODO + diff --git a/zh-CN/src/std/intro.md b/zh-CN/src/std/intro.md new file mode 100644 index 000000000..29d2da781 --- /dev/null +++ b/zh-CN/src/std/intro.md @@ -0,0 +1,2 @@ +# 标准库 TODO +