From 7eb8be8ce8f0d5f8424a4210ee966e49298a5428 Mon Sep 17 00:00:00 2001 From: SketchK Date: Wed, 27 Sep 2023 12:00:49 +0800 Subject: [PATCH] translate chaptor 1-13 & 57-60 to chinese-cn version --- po/zh-CN.po | 18944 ++++++++++++++++++++++++++------------------------ 1 file changed, 9787 insertions(+), 9157 deletions(-) diff --git a/po/zh-CN.po b/po/zh-CN.po index 284f8ded20a..80b071495d6 100644 --- a/po/zh-CN.po +++ b/po/zh-CN.po @@ -1,137 +1,138 @@ msgid "" msgstr "" "Project-Id-Version: Comprehensive Rust 🦀\n" -"POT-Creation-Date: 2023-09-18\n" -"PO-Revision-Date: 2023-06-12 21:28-0700\n" -"Last-Translator: Zhengping Jiang \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2023-09-27 11:21+0800\n" +"Last-Translator: SketchK \n" "Language-Team: Language zh-Hans\n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: zh-Hans\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Poedit 3.3.2\n" -#: src/SUMMARY.md:3 src/welcome.md:1 +#: src/SUMMARY.md:4 src/index.md:1 msgid "Welcome to Comprehensive Rust 🦀" msgstr "欢迎来到 Comprehensive Rust 🦀" -#: src/SUMMARY.md:4 src/running-the-course.md:1 +#: src/SUMMARY.md:5 src/running-the-course.md:1 msgid "Running the Course" msgstr "授课" -#: src/SUMMARY.md:5 src/running-the-course/course-structure.md:1 +#: src/SUMMARY.md:6 src/running-the-course/course-structure.md:1 msgid "Course Structure" msgstr "课程结构" -#: src/SUMMARY.md:6 src/running-the-course/keyboard-shortcuts.md:1 +#: src/SUMMARY.md:7 src/running-the-course/keyboard-shortcuts.md:1 msgid "Keyboard Shortcuts" msgstr "键盘快捷键" -#: src/SUMMARY.md:7 src/running-the-course/translations.md:1 +#: src/SUMMARY.md:8 src/running-the-course/translations.md:1 msgid "Translations" msgstr "翻译" -#: src/SUMMARY.md:8 src/cargo.md:1 +#: src/SUMMARY.md:9 src/cargo.md:1 msgid "Using Cargo" msgstr "使用 Cargo" -#: src/SUMMARY.md:9 +#: src/SUMMARY.md:10 msgid "Rust Ecosystem" msgstr "Rust 生态系统" -#: src/SUMMARY.md:10 +#: src/SUMMARY.md:11 msgid "Code Samples" msgstr "代码示例" -#: src/SUMMARY.md:11 +#: src/SUMMARY.md:12 msgid "Running Cargo Locally" msgstr "在本地运行 Cargo" -#: src/SUMMARY.md:14 +#: src/SUMMARY.md:15 msgid "Day 1: Morning" msgstr "第一天:早上" -#: src/SUMMARY.md:18 src/SUMMARY.md:75 src/SUMMARY.md:128 src/SUMMARY.md:185 -#: src/SUMMARY.md:211 src/SUMMARY.md:259 +#: src/SUMMARY.md:19 src/SUMMARY.md:79 src/SUMMARY.md:134 src/SUMMARY.md:192 +#: src/SUMMARY.md:218 src/SUMMARY.md:268 msgid "Welcome" msgstr "欢迎" -#: src/SUMMARY.md:19 src/welcome-day-1/what-is-rust.md:1 +#: src/SUMMARY.md:20 src/welcome-day-1/what-is-rust.md:1 msgid "What is Rust?" msgstr "什么是 Rust?" -#: src/SUMMARY.md:20 src/hello-world.md:1 +#: src/SUMMARY.md:21 src/hello-world.md:1 msgid "Hello World!" msgstr "Hello World!" -#: src/SUMMARY.md:21 src/hello-world/small-example.md:1 +#: src/SUMMARY.md:22 src/hello-world/small-example.md:1 msgid "Small Example" msgstr "简短示例" -#: src/SUMMARY.md:22 src/why-rust.md:1 +#: src/SUMMARY.md:23 src/why-rust.md:1 msgid "Why Rust?" msgstr "为什么选择 Rust?" -#: src/SUMMARY.md:23 src/why-rust/compile-time.md:1 +#: src/SUMMARY.md:24 src/why-rust/compile-time.md:1 msgid "Compile Time Guarantees" msgstr "编译期保障" -#: src/SUMMARY.md:24 src/why-rust/runtime.md:1 +#: src/SUMMARY.md:25 src/why-rust/runtime.md:1 msgid "Runtime Guarantees" msgstr "运行时保障" -#: src/SUMMARY.md:25 src/why-rust/modern.md:1 +#: src/SUMMARY.md:26 src/why-rust/modern.md:1 msgid "Modern Features" msgstr "现代特性" -#: src/SUMMARY.md:26 src/basic-syntax.md:1 +#: src/SUMMARY.md:27 src/basic-syntax.md:1 msgid "Basic Syntax" msgstr "基本语法" -#: src/SUMMARY.md:27 src/basic-syntax/scalar-types.md:1 +#: src/SUMMARY.md:28 src/basic-syntax/scalar-types.md:1 msgid "Scalar Types" msgstr "标量类型" -#: src/SUMMARY.md:28 src/basic-syntax/compound-types.md:1 +#: src/SUMMARY.md:29 src/basic-syntax/compound-types.md:1 msgid "Compound Types" msgstr "复合类型" -#: src/SUMMARY.md:29 src/basic-syntax/references.md:1 +#: src/SUMMARY.md:30 src/basic-syntax/references.md:1 msgid "References" msgstr "引用" -#: src/SUMMARY.md:30 src/basic-syntax/references-dangling.md:1 +#: src/SUMMARY.md:31 src/basic-syntax/references-dangling.md:1 msgid "Dangling References" msgstr "悬垂引用" -#: src/SUMMARY.md:31 src/basic-syntax/slices.md:1 +#: src/SUMMARY.md:32 src/basic-syntax/slices.md:1 msgid "Slices" msgstr "切片" -#: src/SUMMARY.md:32 +#: src/SUMMARY.md:33 msgid "String vs str" msgstr "String 和 str" -#: src/SUMMARY.md:33 src/basic-syntax/functions.md:1 +#: src/SUMMARY.md:34 src/basic-syntax/functions.md:1 msgid "Functions" msgstr "函数" -#: src/SUMMARY.md:34 src/basic-syntax/rustdoc.md:1 +#: src/SUMMARY.md:35 src/basic-syntax/rustdoc.md:1 msgid "Rustdoc" msgstr "Rustdoc" -#: src/SUMMARY.md:35 src/SUMMARY.md:82 src/basic-syntax/methods.md:1 +#: src/SUMMARY.md:36 src/SUMMARY.md:102 src/basic-syntax/methods.md:1 #: src/methods.md:1 msgid "Methods" msgstr "方法" -#: src/SUMMARY.md:36 +#: src/SUMMARY.md:37 msgid "Overloading" msgstr "重载" -#: src/SUMMARY.md:37 src/SUMMARY.md:66 src/SUMMARY.md:90 src/SUMMARY.md:119 -#: src/SUMMARY.md:148 src/SUMMARY.md:177 src/SUMMARY.md:204 src/SUMMARY.md:225 -#: src/SUMMARY.md:251 src/SUMMARY.md:273 src/SUMMARY.md:293 +#: src/SUMMARY.md:38 src/SUMMARY.md:71 src/SUMMARY.md:105 src/SUMMARY.md:125 +#: src/SUMMARY.md:154 src/SUMMARY.md:184 src/SUMMARY.md:211 src/SUMMARY.md:232 +#: src/SUMMARY.md:260 src/SUMMARY.md:282 src/SUMMARY.md:303 #: src/exercises/android/morning.md:1 src/exercises/bare-metal/morning.md:1 #: src/exercises/bare-metal/afternoon.md:1 #: src/exercises/concurrency/morning.md:1 @@ -139,1036 +140,1027 @@ msgstr "重载" msgid "Exercises" msgstr "习题" -#: src/SUMMARY.md:38 src/exercises/day-1/implicit-conversions.md:1 +#: src/SUMMARY.md:39 src/exercises/day-1/implicit-conversions.md:1 msgid "Implicit Conversions" msgstr "隐式类型转换" -#: src/SUMMARY.md:39 +#: src/SUMMARY.md:40 msgid "Arrays and for Loops" msgstr "数组与 for 循环" -#: src/SUMMARY.md:41 +#: src/SUMMARY.md:42 msgid "Day 1: Afternoon" msgstr "第 1 天:下午" -#: src/SUMMARY.md:43 src/basic-syntax/variables.md:1 +#: src/SUMMARY.md:44 src/SUMMARY.md:295 src/control-flow.md:1 +msgid "Control Flow" +msgstr "控制流" + +#: src/SUMMARY.md:45 src/control-flow/blocks.md:1 +msgid "Blocks" +msgstr "块" + +#: src/SUMMARY.md:46 +msgid "if expressions" +msgstr "if 表达式" + +#: src/SUMMARY.md:47 +msgid "for expressions" +msgstr "for 表达式" + +#: src/SUMMARY.md:48 +msgid "while expressions" +msgstr "while 表达式" + +#: src/SUMMARY.md:49 +msgid "break & continue" +msgstr "break & continue" + +#: src/SUMMARY.md:50 +msgid "loop expressions" +msgstr "loop 表达式" + +#: src/SUMMARY.md:52 src/basic-syntax/variables.md:1 msgid "Variables" msgstr "变量" -#: src/SUMMARY.md:44 src/basic-syntax/type-inference.md:1 +#: src/SUMMARY.md:53 src/basic-syntax/type-inference.md:1 msgid "Type Inference" msgstr "类型推导" -#: src/SUMMARY.md:45 +#: src/SUMMARY.md:54 msgid "static & const" msgstr "静态与常量" -#: src/SUMMARY.md:46 src/basic-syntax/scopes-shadowing.md:1 +#: src/SUMMARY.md:55 src/basic-syntax/scopes-shadowing.md:1 msgid "Scopes and Shadowing" msgstr "作用域和隐藏 (Shadowing)" -#: src/SUMMARY.md:47 src/memory-management.md:1 +#: src/SUMMARY.md:56 src/enums.md:1 +msgid "Enums" +msgstr "枚举" + +#: src/SUMMARY.md:57 src/enums/variant-payloads.md:1 +msgid "Variant Payloads" +msgstr "变体载荷" + +#: src/SUMMARY.md:58 src/enums/sizes.md:1 +msgid "Enum Sizes" +msgstr "枚举大小" + +#: src/SUMMARY.md:60 src/control-flow/novel.md:1 +msgid "Novel Control Flow" +msgstr "其他控制流" + +#: src/SUMMARY.md:61 +msgid "if let expressions" +msgstr "if let 表达式" + +#: src/SUMMARY.md:62 +msgid "while let expressions" +msgstr "while let 表达式" + +#: src/SUMMARY.md:63 +msgid "match expressions" +msgstr "match 表达式" + +#: src/SUMMARY.md:65 src/SUMMARY.md:73 src/pattern-matching.md:1 +msgid "Pattern Matching" +msgstr "模式匹配" + +#: src/SUMMARY.md:66 src/pattern-matching/destructuring-enums.md:1 +msgid "Destructuring Enums" +msgstr "解构枚举" + +#: src/SUMMARY.md:67 src/pattern-matching/destructuring-structs.md:1 +msgid "Destructuring Structs" +msgstr "解构结构体" + +#: src/SUMMARY.md:68 src/pattern-matching/destructuring-arrays.md:1 +msgid "Destructuring Arrays" +msgstr "解构数组" + +#: src/SUMMARY.md:69 src/pattern-matching/match-guards.md:1 +msgid "Match Guards" +msgstr "" + +#: src/SUMMARY.md:72 src/exercises/day-1/luhn.md:1 +#: src/exercises/day-1/solutions-afternoon.md:3 +msgid "Luhn Algorithm" +msgstr "Luhn 算法" + +#: src/SUMMARY.md:75 +msgid "Day 2: Morning" +msgstr "第二天:上午" + +#: src/SUMMARY.md:81 src/memory-management.md:1 msgid "Memory Management" msgstr "内存管理" -#: src/SUMMARY.md:48 +#: src/SUMMARY.md:82 msgid "Stack vs Heap" msgstr "栈 vs 堆" -#: src/SUMMARY.md:49 src/memory-management/stack.md:1 +#: src/SUMMARY.md:83 msgid "Stack Memory" msgstr "栈内存" -#: src/SUMMARY.md:50 src/memory-management/manual.md:1 +#: src/SUMMARY.md:84 src/memory-management/manual.md:1 msgid "Manual Memory Management" msgstr "手动内存管理" -#: src/SUMMARY.md:51 src/memory-management/scope-based.md:1 +#: src/SUMMARY.md:85 src/memory-management/scope-based.md:1 msgid "Scope-Based Memory Management" msgstr "基于作用域的内存管理" -#: src/SUMMARY.md:52 +#: src/SUMMARY.md:86 msgid "Garbage Collection" msgstr "垃圾回收" -#: src/SUMMARY.md:53 +#: src/SUMMARY.md:87 msgid "Rust Memory Management" msgstr "Rust 内存管理" -#: src/SUMMARY.md:54 src/memory-management/comparison.md:1 -msgid "Comparison" -msgstr "比较" - -#: src/SUMMARY.md:55 src/ownership.md:1 +#: src/SUMMARY.md:88 src/ownership.md:1 msgid "Ownership" msgstr "所有权" -#: src/SUMMARY.md:56 src/ownership/move-semantics.md:1 +#: src/SUMMARY.md:89 src/ownership/move-semantics.md:1 msgid "Move Semantics" msgstr "移动语义" -#: src/SUMMARY.md:57 src/ownership/moved-strings-rust.md:1 +#: src/SUMMARY.md:90 src/ownership/moved-strings-rust.md:1 msgid "Moved Strings in Rust" msgstr "Rust 中移动的字符串" -#: src/SUMMARY.md:58 src/ownership/double-free-modern-cpp.md:1 +#: src/SUMMARY.md:91 msgid "Double Frees in Modern C++" msgstr "现代 C++ 中的双重释放" -#: src/SUMMARY.md:59 src/ownership/moves-function-calls.md:1 +#: src/SUMMARY.md:92 src/ownership/moves-function-calls.md:1 msgid "Moves in Function Calls" msgstr "函数调用中的移动" -#: src/SUMMARY.md:60 src/ownership/copy-clone.md:1 +#: src/SUMMARY.md:93 src/ownership/copy-clone.md:1 msgid "Copying and Cloning" msgstr "复制和克隆" -#: src/SUMMARY.md:61 src/ownership/borrowing.md:1 +#: src/SUMMARY.md:94 src/ownership/borrowing.md:1 msgid "Borrowing" msgstr "借用" -#: src/SUMMARY.md:62 src/ownership/shared-unique-borrows.md:1 +#: src/SUMMARY.md:95 src/ownership/shared-unique-borrows.md:1 msgid "Shared and Unique Borrows" msgstr "共享和唯一的借用" -#: src/SUMMARY.md:63 src/ownership/lifetimes.md:1 +#: src/SUMMARY.md:96 src/ownership/lifetimes.md:1 msgid "Lifetimes" msgstr "生命周期" -#: src/SUMMARY.md:64 src/ownership/lifetimes-function-calls.md:1 +#: src/SUMMARY.md:97 src/ownership/lifetimes-function-calls.md:1 msgid "Lifetimes in Function Calls" msgstr "函数调用中的生命周期" -#: src/SUMMARY.md:65 src/ownership/lifetimes-data-structures.md:1 +#: src/SUMMARY.md:98 src/ownership/lifetimes-data-structures.md:1 msgid "Lifetimes in Data Structures" msgstr "数据结构中的生命周期" -#: src/SUMMARY.md:67 src/exercises/day-1/book-library.md:1 -#: src/exercises/day-1/solutions-afternoon.md:3 -msgid "Designing a Library" -msgstr "设计一个库" - -#: src/SUMMARY.md:68 src/exercises/day-1/iterators-and-ownership.md:1 -msgid "Iterators and Ownership" -msgstr "迭代器和所有权" - -#: src/SUMMARY.md:71 -msgid "Day 2: Morning" -msgstr "第二天:上午" - -#: src/SUMMARY.md:76 src/structs.md:1 +#: src/SUMMARY.md:99 src/structs.md:1 msgid "Structs" msgstr "结构体" -#: src/SUMMARY.md:77 src/structs/tuple-structs.md:1 +#: src/SUMMARY.md:100 src/structs/tuple-structs.md:1 msgid "Tuple Structs" msgstr "元组结构体" -#: src/SUMMARY.md:78 src/structs/field-shorthand.md:1 +#: src/SUMMARY.md:101 src/structs/field-shorthand.md:1 msgid "Field Shorthand Syntax" msgstr "字段简写语法" -#: src/SUMMARY.md:79 src/enums.md:1 -msgid "Enums" -msgstr "枚举" - -#: src/SUMMARY.md:80 src/enums/variant-payloads.md:1 -msgid "Variant Payloads" -msgstr "变体载荷" - -#: src/SUMMARY.md:81 src/enums/sizes.md:1 -msgid "Enum Sizes" -msgstr "枚举大小" - -#: src/SUMMARY.md:83 src/methods/receiver.md:1 +#: src/SUMMARY.md:103 src/methods/receiver.md:1 msgid "Method Receiver" msgstr "方法接收者" -#: src/SUMMARY.md:84 src/SUMMARY.md:159 src/SUMMARY.md:272 +#: src/SUMMARY.md:104 src/SUMMARY.md:166 src/SUMMARY.md:281 #: src/methods/example.md:1 src/concurrency/shared_state/example.md:1 msgid "Example" msgstr "示例" -#: src/SUMMARY.md:85 src/pattern-matching.md:1 -msgid "Pattern Matching" -msgstr "模式匹配" - -#: src/SUMMARY.md:86 src/pattern-matching/destructuring-enums.md:1 -msgid "Destructuring Enums" -msgstr "解构枚举" - -#: src/SUMMARY.md:87 src/pattern-matching/destructuring-structs.md:1 -msgid "Destructuring Structs" -msgstr "解构结构体" - -#: src/SUMMARY.md:88 src/pattern-matching/destructuring-arrays.md:1 -msgid "Destructuring Arrays" -msgstr "解构数组" - -#: src/SUMMARY.md:89 src/pattern-matching/match-guards.md:1 -msgid "Match Guards" -msgstr "" +#: src/SUMMARY.md:106 src/exercises/day-2/book-library.md:1 +msgid "Storing Books" +msgstr "存储书籍" -#: src/SUMMARY.md:91 src/exercises/day-2/health-statistics.md:1 +#: src/SUMMARY.md:107 src/exercises/day-2/health-statistics.md:1 msgid "Health Statistics" msgstr "健康统计" -#: src/SUMMARY.md:92 src/exercises/day-2/solutions-morning.md:3 -msgid "Points and Polygons" -msgstr "点和多边形" - -#: src/SUMMARY.md:94 +#: src/SUMMARY.md:109 msgid "Day 2: Afternoon" msgstr "第二天:下午" -#: src/SUMMARY.md:96 src/SUMMARY.md:286 src/control-flow.md:1 -msgid "Control Flow" -msgstr "控制流" - -#: src/SUMMARY.md:97 src/control-flow/blocks.md:1 -msgid "Blocks" -msgstr "块" - -#: src/SUMMARY.md:98 -msgid "if expressions" -msgstr "if 表达式" - -#: src/SUMMARY.md:99 -msgid "if let expressions" -msgstr "if let 表达式" - -#: src/SUMMARY.md:100 -msgid "while expressions" -msgstr "while 表达式" - -#: src/SUMMARY.md:101 -msgid "while let expressions" -msgstr "while let 表达式" - -#: src/SUMMARY.md:102 -msgid "for expressions" -msgstr "for 表达式" - -#: src/SUMMARY.md:103 -msgid "loop expressions" -msgstr "loop 表达式" - -#: src/SUMMARY.md:104 -msgid "match expressions" -msgstr "match 表达式" - -#: src/SUMMARY.md:105 -msgid "break & continue" -msgstr "break & continue" - -#: src/SUMMARY.md:106 src/std.md:1 +#: src/SUMMARY.md:111 src/std.md:1 msgid "Standard Library" msgstr "标准库" -#: src/SUMMARY.md:107 +#: src/SUMMARY.md:112 msgid "Option and Result" msgstr "Option 和 Result" -#: src/SUMMARY.md:108 src/std/string.md:1 +#: src/SUMMARY.md:113 src/std/string.md:1 msgid "String" msgstr "String" -#: src/SUMMARY.md:109 +#: src/SUMMARY.md:114 msgid "Vec" msgstr "Vec" -#: src/SUMMARY.md:110 +#: src/SUMMARY.md:115 msgid "HashMap" msgstr "哈希表" -#: src/SUMMARY.md:111 +#: src/SUMMARY.md:116 msgid "Box" msgstr "Box" -#: src/SUMMARY.md:112 +#: src/SUMMARY.md:117 msgid "Recursive Data Types" msgstr "递归数据类型" -#: src/SUMMARY.md:113 src/std/box-niche.md:1 +#: src/SUMMARY.md:118 src/std/box-niche.md:1 msgid "Niche Optimization" msgstr "小众优化" -#: src/SUMMARY.md:114 +#: src/SUMMARY.md:119 msgid "Rc" msgstr "Rc" -#: src/SUMMARY.md:115 src/modules.md:1 +#: src/SUMMARY.md:120 +msgid "Cell/RefCell" +msgstr "" + +#: src/SUMMARY.md:121 src/modules.md:1 msgid "Modules" msgstr "模块" -#: src/SUMMARY.md:116 src/modules/visibility.md:1 +#: src/SUMMARY.md:122 src/modules/visibility.md:1 msgid "Visibility" msgstr "可见性" -#: src/SUMMARY.md:117 src/modules/paths.md:1 +#: src/SUMMARY.md:123 src/modules/paths.md:1 msgid "Paths" msgstr "路径" -#: src/SUMMARY.md:118 src/modules/filesystem.md:1 +#: src/SUMMARY.md:124 src/modules/filesystem.md:1 msgid "Filesystem Hierarchy" msgstr "文件系统层级结构" -#: src/SUMMARY.md:120 src/exercises/day-2/luhn.md:1 -#: src/exercises/day-2/solutions-afternoon.md:3 -msgid "Luhn Algorithm" -msgstr "Luhn 算法" +#: src/SUMMARY.md:126 src/exercises/day-2/iterators-and-ownership.md:1 +msgid "Iterators and Ownership" +msgstr "迭代器和所有权" -#: src/SUMMARY.md:121 src/exercises/day-2/strings-iterators.md:1 -#: src/exercises/day-2/solutions-afternoon.md:97 +#: src/SUMMARY.md:127 src/exercises/day-2/strings-iterators.md:1 +#: src/exercises/day-2/solutions-afternoon.md:3 msgid "Strings and Iterators" msgstr "字符串和迭代器" -#: src/SUMMARY.md:124 +#: src/SUMMARY.md:130 msgid "Day 3: Morning" msgstr "第三天:上午" -#: src/SUMMARY.md:129 src/generics.md:1 +#: src/SUMMARY.md:135 src/generics.md:1 msgid "Generics" msgstr "泛型" -#: src/SUMMARY.md:130 src/generics/data-types.md:1 +#: src/SUMMARY.md:136 src/generics/data-types.md:1 msgid "Generic Data Types" msgstr "通用数据类型" -#: src/SUMMARY.md:131 src/generics/methods.md:1 +#: src/SUMMARY.md:137 src/generics/methods.md:1 msgid "Generic Methods" msgstr "泛型方法" -#: src/SUMMARY.md:132 src/generics/monomorphization.md:1 +#: src/SUMMARY.md:138 src/generics/monomorphization.md:1 msgid "Monomorphization" msgstr "单态化" -#: src/SUMMARY.md:133 src/traits.md:1 +#: src/SUMMARY.md:139 src/traits.md:1 msgid "Traits" msgstr "特征" -#: src/SUMMARY.md:134 src/traits/trait-objects.md:1 +#: src/SUMMARY.md:140 src/traits/trait-objects.md:1 msgid "Trait Objects" msgstr "特征(Trait)对象" -#: src/SUMMARY.md:135 src/traits/deriving-traits.md:1 +#: src/SUMMARY.md:141 src/traits/deriving-traits.md:1 msgid "Deriving Traits" msgstr "派生特征" -#: src/SUMMARY.md:136 src/traits/default-methods.md:1 +#: src/SUMMARY.md:142 src/traits/default-methods.md:1 msgid "Default Methods" msgstr "默认方法" -#: src/SUMMARY.md:137 src/traits/trait-bounds.md:1 +#: src/SUMMARY.md:143 src/traits/trait-bounds.md:1 msgid "Trait Bounds" msgstr "特征边界" -#: src/SUMMARY.md:138 +#: src/SUMMARY.md:144 msgid "impl Trait" msgstr "impl Trait" -#: src/SUMMARY.md:139 src/traits/important-traits.md:1 +#: src/SUMMARY.md:145 src/traits/important-traits.md:1 msgid "Important Traits" msgstr "重要特征" -#: src/SUMMARY.md:140 +#: src/SUMMARY.md:146 msgid "Iterator" msgstr "迭代器" -#: src/SUMMARY.md:141 src/traits/from-iterator.md:1 +#: src/SUMMARY.md:147 src/traits/from-iterator.md:1 msgid "FromIterator" msgstr "FromIterator" -#: src/SUMMARY.md:142 +#: src/SUMMARY.md:148 msgid "From and Into" msgstr "" -#: src/SUMMARY.md:143 +#: src/SUMMARY.md:149 msgid "Read and Write" msgstr "读取和写入" -#: src/SUMMARY.md:144 +#: src/SUMMARY.md:150 msgid "Drop" msgstr "Drop" -#: src/SUMMARY.md:145 +#: src/SUMMARY.md:151 msgid "Default" msgstr "Default" -#: src/SUMMARY.md:146 +#: src/SUMMARY.md:152 msgid "Operators: Add, Mul, ..." msgstr "运算符:Add、Mul..." -#: src/SUMMARY.md:147 +#: src/SUMMARY.md:153 msgid "Closures: Fn, FnMut, FnOnce" msgstr "闭包:Fn、FnMut、FnOnce" -#: src/SUMMARY.md:149 src/exercises/day-3/simple-gui.md:1 +#: src/SUMMARY.md:155 src/exercises/day-3/simple-gui.md:1 #: src/exercises/day-3/solutions-morning.md:3 msgid "A Simple GUI Library" msgstr "一个简单的 GUI 库" -#: src/SUMMARY.md:151 +#: src/SUMMARY.md:156 src/exercises/day-3/solutions-morning.md:145 +msgid "Points and Polygons" +msgstr "点和多边形" + +#: src/SUMMARY.md:158 msgid "Day 3: Afternoon" msgstr "第三天:下午" -#: src/SUMMARY.md:153 src/error-handling.md:1 +#: src/SUMMARY.md:160 src/error-handling.md:1 msgid "Error Handling" msgstr "错误处理" -#: src/SUMMARY.md:154 src/error-handling/panics.md:1 +#: src/SUMMARY.md:161 src/error-handling/panics.md:1 msgid "Panics" msgstr "Panics" -#: src/SUMMARY.md:155 +#: src/SUMMARY.md:162 msgid "Catching Stack Unwinding" msgstr "捕获堆栈展开" -#: src/SUMMARY.md:156 +#: src/SUMMARY.md:163 msgid "Structured Error Handling" msgstr "结构化错误处理" -#: src/SUMMARY.md:157 +#: src/SUMMARY.md:164 msgid "Propagating Errors with ?" -msgstr "使用 ? 传播错误" +msgstr "传播错误可以使用 ?" -#: src/SUMMARY.md:158 src/error-handling/converting-error-types.md:1 +#: src/SUMMARY.md:165 src/error-handling/converting-error-types.md:1 #: src/error-handling/converting-error-types-example.md:1 msgid "Converting Error Types" msgstr "转换错误类型" -#: src/SUMMARY.md:160 src/error-handling/deriving-error-enums.md:1 +#: src/SUMMARY.md:167 src/error-handling/deriving-error-enums.md:1 msgid "Deriving Error Enums" msgstr "派生错误枚举" -#: src/SUMMARY.md:161 src/error-handling/dynamic-errors.md:1 +#: src/SUMMARY.md:168 src/error-handling/dynamic-errors.md:1 msgid "Dynamic Error Types" msgstr "动态错误类型" -#: src/SUMMARY.md:162 src/error-handling/error-contexts.md:1 +#: src/SUMMARY.md:169 src/error-handling/error-contexts.md:1 msgid "Adding Context to Errors" msgstr "为错误添加背景信息" -#: src/SUMMARY.md:163 src/testing.md:1 +#: src/SUMMARY.md:170 src/testing.md:1 msgid "Testing" msgstr "测试" -#: src/SUMMARY.md:164 src/testing/unit-tests.md:1 +#: src/SUMMARY.md:171 src/testing/unit-tests.md:1 msgid "Unit Tests" msgstr "单元测试" -#: src/SUMMARY.md:165 src/testing/test-modules.md:1 +#: src/SUMMARY.md:172 src/testing/test-modules.md:1 msgid "Test Modules" msgstr "测试模块" -#: src/SUMMARY.md:166 src/testing/doc-tests.md:1 +#: src/SUMMARY.md:173 src/testing/doc-tests.md:1 msgid "Documentation Tests" msgstr "文档测试" -#: src/SUMMARY.md:167 src/testing/integration-tests.md:1 +#: src/SUMMARY.md:174 src/testing/integration-tests.md:1 msgid "Integration Tests" msgstr "集成测试" -#: src/SUMMARY.md:168 src/bare-metal/useful-crates.md:1 +#: src/SUMMARY.md:175 src/bare-metal/useful-crates.md:1 msgid "Useful crates" msgstr "" -#: src/SUMMARY.md:169 src/unsafe.md:1 +#: src/SUMMARY.md:176 src/unsafe.md:1 msgid "Unsafe Rust" msgstr "不安全 Rust" -#: src/SUMMARY.md:170 src/unsafe/raw-pointers.md:1 +#: src/SUMMARY.md:177 src/unsafe/raw-pointers.md:1 msgid "Dereferencing Raw Pointers" msgstr "解引用裸指针" -#: src/SUMMARY.md:171 src/unsafe/mutable-static-variables.md:1 +#: src/SUMMARY.md:178 src/unsafe/mutable-static-variables.md:1 msgid "Mutable Static Variables" msgstr "可变的静态变量" -#: src/SUMMARY.md:172 src/unsafe/unions.md:1 +#: src/SUMMARY.md:179 src/unsafe/unions.md:1 msgid "Unions" msgstr "联合体" -#: src/SUMMARY.md:173 src/unsafe/calling-unsafe-functions.md:1 +#: src/SUMMARY.md:180 src/unsafe/calling-unsafe-functions.md:1 msgid "Calling Unsafe Functions" msgstr "调用 Unsafe 函数" -#: src/SUMMARY.md:174 src/unsafe/writing-unsafe-functions.md:1 +#: src/SUMMARY.md:181 src/unsafe/writing-unsafe-functions.md:1 msgid "Writing Unsafe Functions" msgstr "编写 Unsafe 函数" -#: src/SUMMARY.md:175 +#: src/SUMMARY.md:182 msgid "Extern Functions" msgstr "外部函数" -#: src/SUMMARY.md:176 src/unsafe/unsafe-traits.md:1 +#: src/SUMMARY.md:183 src/unsafe/unsafe-traits.md:1 msgid "Implementing Unsafe Traits" msgstr "实现 Unsafe Trait" -#: src/SUMMARY.md:178 src/exercises/day-3/safe-ffi-wrapper.md:1 +#: src/SUMMARY.md:185 src/exercises/day-3/safe-ffi-wrapper.md:1 #: src/exercises/day-3/solutions-afternoon.md:3 msgid "Safe FFI Wrapper" msgstr "" -#: src/SUMMARY.md:181 src/SUMMARY.md:249 -#: src/running-the-course/course-structure.md:16 src/bare-metal/android.md:1 +#: src/SUMMARY.md:188 src/SUMMARY.md:258 src/bare-metal/android.md:1 msgid "Android" msgstr "Android" -#: src/SUMMARY.md:186 src/android/setup.md:1 +#: src/SUMMARY.md:193 src/android/setup.md:1 msgid "Setup" msgstr "设置" -#: src/SUMMARY.md:187 src/android/build-rules.md:1 +#: src/SUMMARY.md:194 src/android/build-rules.md:1 msgid "Build Rules" msgstr "构建规则" -#: src/SUMMARY.md:188 +#: src/SUMMARY.md:195 msgid "Binary" msgstr "可执行文件" -#: src/SUMMARY.md:189 +#: src/SUMMARY.md:196 msgid "Library" msgstr "库" -#: src/SUMMARY.md:190 src/android/aidl.md:1 +#: src/SUMMARY.md:197 src/android/aidl.md:1 msgid "AIDL" msgstr "" -#: src/SUMMARY.md:191 +#: src/SUMMARY.md:198 msgid "Interface" msgstr "接口" -#: src/SUMMARY.md:192 +#: src/SUMMARY.md:199 msgid "Implementation" msgstr "" -#: src/SUMMARY.md:193 +#: src/SUMMARY.md:200 msgid "Server" msgstr "服务器" -#: src/SUMMARY.md:194 src/android/aidl/deploy.md:1 +#: src/SUMMARY.md:201 src/android/aidl/deploy.md:1 msgid "Deploy" msgstr "部署" -#: src/SUMMARY.md:195 +#: src/SUMMARY.md:202 msgid "Client" msgstr "客户端" -#: src/SUMMARY.md:196 src/android/aidl/changing.md:1 +#: src/SUMMARY.md:203 src/android/aidl/changing.md:1 msgid "Changing API" msgstr "更改 API" -#: src/SUMMARY.md:197 src/SUMMARY.md:240 src/android/logging.md:1 +#: src/SUMMARY.md:204 src/SUMMARY.md:248 src/android/logging.md:1 #: src/bare-metal/aps/logging.md:1 msgid "Logging" msgstr "日志记录" -#: src/SUMMARY.md:198 src/android/interoperability.md:1 +#: src/SUMMARY.md:205 src/android/interoperability.md:1 msgid "Interoperability" msgstr "" -#: src/SUMMARY.md:199 +#: src/SUMMARY.md:206 msgid "With C" msgstr "与 C 语言交互" -#: src/SUMMARY.md:200 +#: src/SUMMARY.md:207 msgid "Calling C with Bindgen" msgstr "使用Bindgen调用C语言" -#: src/SUMMARY.md:201 +#: src/SUMMARY.md:208 msgid "Calling Rust from C" msgstr "从C语言调用Rust语言" -#: src/SUMMARY.md:202 src/android/interoperability/cpp.md:1 +#: src/SUMMARY.md:209 src/android/interoperability/cpp.md:1 msgid "With C++" msgstr "与 C++ 交互" -#: src/SUMMARY.md:203 +#: src/SUMMARY.md:210 msgid "With Java" msgstr "与 Java 交互" -#: src/SUMMARY.md:207 +#: src/SUMMARY.md:214 msgid "Bare Metal: Morning" msgstr "" -#: src/SUMMARY.md:212 +#: src/SUMMARY.md:219 msgid "no_std" msgstr "" -#: src/SUMMARY.md:213 +#: src/SUMMARY.md:220 msgid "A Minimal Example" msgstr "" -#: src/SUMMARY.md:214 +#: src/SUMMARY.md:221 msgid "alloc" msgstr "" -#: src/SUMMARY.md:215 src/bare-metal/microcontrollers.md:1 +#: src/SUMMARY.md:222 src/bare-metal/microcontrollers.md:1 msgid "Microcontrollers" msgstr "微控制器" -#: src/SUMMARY.md:216 src/bare-metal/microcontrollers/mmio.md:1 +#: src/SUMMARY.md:223 src/bare-metal/microcontrollers/mmio.md:1 msgid "Raw MMIO" msgstr "" -#: src/SUMMARY.md:217 +#: src/SUMMARY.md:224 msgid "PACs" msgstr "" -#: src/SUMMARY.md:218 +#: src/SUMMARY.md:225 msgid "HAL Crates" msgstr "" -#: src/SUMMARY.md:219 +#: src/SUMMARY.md:226 msgid "Board Support Crates" msgstr "" -#: src/SUMMARY.md:220 +#: src/SUMMARY.md:227 msgid "The Type State Pattern" msgstr "" -#: src/SUMMARY.md:221 +#: src/SUMMARY.md:228 msgid "embedded-hal" msgstr "" -#: src/SUMMARY.md:222 +#: src/SUMMARY.md:229 msgid "probe-rs, cargo-embed" msgstr "" -#: src/SUMMARY.md:223 src/bare-metal/microcontrollers/debugging.md:1 +#: src/SUMMARY.md:230 src/bare-metal/microcontrollers/debugging.md:1 msgid "Debugging" msgstr "Debugging" -#: src/SUMMARY.md:224 src/SUMMARY.md:242 +#: src/SUMMARY.md:231 src/SUMMARY.md:251 msgid "Other Projects" msgstr "" -#: src/SUMMARY.md:226 src/exercises/bare-metal/compass.md:1 +#: src/SUMMARY.md:233 src/exercises/bare-metal/compass.md:1 #: src/exercises/bare-metal/solutions-morning.md:3 msgid "Compass" msgstr "" -#: src/SUMMARY.md:228 +#: src/SUMMARY.md:235 msgid "Bare Metal: Afternoon" msgstr "" -#: src/SUMMARY.md:230 +#: src/SUMMARY.md:237 msgid "Application Processors" msgstr "" -#: src/SUMMARY.md:231 +#: src/SUMMARY.md:238 src/bare-metal/aps/entry-point.md:1 +msgid "Getting Ready to Rust" +msgstr "" + +#: src/SUMMARY.md:239 msgid "Inline Assembly" msgstr "" -#: src/SUMMARY.md:232 +#: src/SUMMARY.md:240 msgid "MMIO" msgstr "" -#: src/SUMMARY.md:233 +#: src/SUMMARY.md:241 msgid "Let's Write a UART Driver" msgstr "" -#: src/SUMMARY.md:234 +#: src/SUMMARY.md:242 msgid "More Traits" msgstr "" -#: src/SUMMARY.md:235 +#: src/SUMMARY.md:243 msgid "A Better UART Driver" msgstr "一个更好的 UART 驱动程序" -#: src/SUMMARY.md:236 src/bare-metal/aps/better-uart/bitflags.md:1 +#: src/SUMMARY.md:244 src/bare-metal/aps/better-uart/bitflags.md:1 msgid "Bitflags" msgstr "" -#: src/SUMMARY.md:237 +#: src/SUMMARY.md:245 msgid "Multiple Registers" msgstr "" -#: src/SUMMARY.md:238 src/bare-metal/aps/better-uart/driver.md:1 +#: src/SUMMARY.md:246 src/bare-metal/aps/better-uart/driver.md:1 msgid "Driver" msgstr "" -#: src/SUMMARY.md:239 src/SUMMARY.md:241 +#: src/SUMMARY.md:247 src/SUMMARY.md:249 msgid "Using It" msgstr "" -#: src/SUMMARY.md:243 +#: src/SUMMARY.md:250 src/bare-metal/aps/exceptions.md:1 +#, fuzzy +msgid "Exceptions" +msgstr "函数" + +#: src/SUMMARY.md:252 msgid "Useful Crates" msgstr "" -#: src/SUMMARY.md:244 +#: src/SUMMARY.md:253 msgid "zerocopy" msgstr "" -#: src/SUMMARY.md:245 +#: src/SUMMARY.md:254 msgid "aarch64-paging" msgstr "" -#: src/SUMMARY.md:246 +#: src/SUMMARY.md:255 msgid "buddy_system_allocator" msgstr "" -#: src/SUMMARY.md:247 +#: src/SUMMARY.md:256 msgid "tinyvec" msgstr "" -#: src/SUMMARY.md:248 +#: src/SUMMARY.md:257 msgid "spin" msgstr "" -#: src/SUMMARY.md:250 src/bare-metal/android/vmbase.md:1 +#: src/SUMMARY.md:259 src/bare-metal/android/vmbase.md:1 msgid "vmbase" msgstr "" -#: src/SUMMARY.md:252 +#: src/SUMMARY.md:261 msgid "RTC Driver" msgstr "RTC驱动" -#: src/SUMMARY.md:255 +#: src/SUMMARY.md:264 msgid "Concurrency: Morning" msgstr "并发编程:入门篇" -#: src/SUMMARY.md:260 src/concurrency/threads.md:1 +#: src/SUMMARY.md:269 src/concurrency/threads.md:1 msgid "Threads" msgstr "线程" -#: src/SUMMARY.md:261 src/concurrency/scoped-threads.md:1 +#: src/SUMMARY.md:270 src/concurrency/scoped-threads.md:1 msgid "Scoped Threads" msgstr "范围线程" -#: src/SUMMARY.md:262 src/concurrency/channels.md:1 +#: src/SUMMARY.md:271 src/concurrency/channels.md:1 msgid "Channels" msgstr "通道" -#: src/SUMMARY.md:263 src/concurrency/channels/unbounded.md:1 +#: src/SUMMARY.md:272 src/concurrency/channels/unbounded.md:1 msgid "Unbounded Channels" msgstr "无界通道" -#: src/SUMMARY.md:264 src/concurrency/channels/bounded.md:1 +#: src/SUMMARY.md:273 src/concurrency/channels/bounded.md:1 msgid "Bounded Channels" msgstr "有界通道" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:274 msgid "Send and Sync" msgstr "Send 和 Sync" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:274 msgid "Send" msgstr "Send" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:274 msgid "Sync" msgstr "Sync" -#: src/SUMMARY.md:268 src/concurrency/send-sync/examples.md:1 +#: src/SUMMARY.md:277 src/concurrency/send-sync/examples.md:1 msgid "Examples" msgstr "示例" -#: src/SUMMARY.md:269 src/concurrency/shared_state.md:1 +#: src/SUMMARY.md:278 src/concurrency/shared_state.md:1 msgid "Shared State" msgstr "共享状态" -#: src/SUMMARY.md:270 +#: src/SUMMARY.md:279 msgid "Arc" msgstr "Arc" -#: src/SUMMARY.md:271 +#: src/SUMMARY.md:280 msgid "Mutex" msgstr "Mutex" -#: src/SUMMARY.md:274 src/SUMMARY.md:294 +#: src/SUMMARY.md:283 src/SUMMARY.md:304 #: src/exercises/concurrency/dining-philosophers.md:1 #: src/exercises/concurrency/solutions-morning.md:3 msgid "Dining Philosophers" msgstr "哲学家就餐问题 (Dining philosophers problem)" -#: src/SUMMARY.md:275 src/exercises/concurrency/link-checker.md:1 +#: src/SUMMARY.md:284 src/exercises/concurrency/link-checker.md:1 msgid "Multi-threaded Link Checker" msgstr "多线程链接检查器" -#: src/SUMMARY.md:277 +#: src/SUMMARY.md:286 msgid "Concurrency: Afternoon" msgstr "并发:下午" -#: src/SUMMARY.md:279 +#: src/SUMMARY.md:288 msgid "Async Basics" msgstr "异步基础" -#: src/SUMMARY.md:280 +#: src/SUMMARY.md:289 msgid "async/await" msgstr "async/await" -#: src/SUMMARY.md:281 src/async/futures.md:1 +#: src/SUMMARY.md:290 src/async/futures.md:1 msgid "Futures" msgstr "" -#: src/SUMMARY.md:282 src/async/runtimes.md:1 +#: src/SUMMARY.md:291 src/async/runtimes.md:1 msgid "Runtimes" msgstr "" -#: src/SUMMARY.md:283 src/async/runtimes/tokio.md:1 +#: src/SUMMARY.md:292 src/async/runtimes/tokio.md:1 msgid "Tokio" msgstr "" -#: src/SUMMARY.md:284 src/exercises/concurrency/link-checker.md:106 -#: src/exercises/concurrency/chat-app.md:140 src/async/tasks.md:1 +#: src/SUMMARY.md:293 src/exercises/concurrency/link-checker.md:126 +#: src/async/tasks.md:1 src/exercises/concurrency/chat-app.md:143 msgid "Tasks" msgstr "" -#: src/SUMMARY.md:285 src/async/channels.md:1 +#: src/SUMMARY.md:294 src/async/channels.md:1 msgid "Async Channels" msgstr "" -#: src/SUMMARY.md:287 src/async/control-flow/join.md:1 +#: src/SUMMARY.md:296 src/async/control-flow/join.md:1 msgid "Join" msgstr "" -#: src/SUMMARY.md:288 src/async/control-flow/select.md:1 +#: src/SUMMARY.md:297 src/async/control-flow/select.md:1 msgid "Select" msgstr "" -#: src/SUMMARY.md:289 +#: src/SUMMARY.md:298 msgid "Pitfalls" msgstr "" -#: src/SUMMARY.md:290 +#: src/SUMMARY.md:299 msgid "Blocking the Executor" msgstr "" -#: src/SUMMARY.md:291 src/async/pitfalls/pin.md:1 +#: src/SUMMARY.md:300 src/async/pitfalls/pin.md:1 msgid "Pin" msgstr "" -#: src/SUMMARY.md:292 src/async/pitfalls/async-traits.md:1 +#: src/SUMMARY.md:301 src/async/pitfalls/async-traits.md:1 msgid "Async Traits" msgstr "异步特质" -#: src/SUMMARY.md:295 src/exercises/concurrency/chat-app.md:1 -#: src/exercises/concurrency/solutions-afternoon.md:113 +#: src/SUMMARY.md:302 src/async/pitfalls/cancellation.md:1 +#, fuzzy +msgid "Cancellation" +msgstr "安装" + +#: src/SUMMARY.md:305 src/exercises/concurrency/chat-app.md:1 +#: src/exercises/concurrency/solutions-afternoon.md:95 msgid "Broadcast Chat Application" msgstr "广播聊天应用程序" -#: src/SUMMARY.md:298 +#: src/SUMMARY.md:308 msgid "Final Words" msgstr "结束语" -#: src/SUMMARY.md:302 src/thanks.md:1 +#: src/SUMMARY.md:312 src/thanks.md:1 msgid "Thanks!" msgstr "谢谢!" -#: src/SUMMARY.md:303 +#: src/SUMMARY.md:313 msgid "Other Resources" msgstr "其他资源" -#: src/SUMMARY.md:304 src/credits.md:1 +#: src/SUMMARY.md:314 src/credits.md:1 msgid "Credits" msgstr "鸣谢" -#: src/SUMMARY.md:307 src/exercises/solutions.md:1 +#: src/SUMMARY.md:317 src/exercises/solutions.md:1 msgid "Solutions" msgstr "解答" -#: src/SUMMARY.md:312 +#: src/SUMMARY.md:322 msgid "Day 1 Morning" msgstr "第一天上午" -#: src/SUMMARY.md:313 +#: src/SUMMARY.md:323 msgid "Day 1 Afternoon" msgstr "第一天下午" -#: src/SUMMARY.md:314 +#: src/SUMMARY.md:324 msgid "Day 2 Morning" msgstr "第二天上午" -#: src/SUMMARY.md:315 +#: src/SUMMARY.md:325 msgid "Day 2 Afternoon" msgstr "第二天下午" -#: src/SUMMARY.md:316 +#: src/SUMMARY.md:326 msgid "Day 3 Morning" msgstr "第三天上午" -#: src/SUMMARY.md:317 +#: src/SUMMARY.md:327 msgid "Day 3 Afternoon" msgstr "第三天下午" -#: src/SUMMARY.md:318 +#: src/SUMMARY.md:328 msgid "Bare Metal Rust Morning" msgstr "嵌入式 Rust:入门篇" -#: src/SUMMARY.md:319 src/exercises/bare-metal/solutions-afternoon.md:1 +#: src/SUMMARY.md:329 src/exercises/bare-metal/solutions-afternoon.md:1 msgid "Bare Metal Rust Afternoon" msgstr "嵌入式 Rust:进阶篇" -#: src/SUMMARY.md:320 +#: src/SUMMARY.md:330 msgid "Concurrency Morning" msgstr "并发编程:入门篇" -#: src/SUMMARY.md:321 +#: src/SUMMARY.md:331 msgid "Concurrency Afternoon" msgstr "并发编程:进阶篇" -#: src/welcome.md:3 -msgid "" -"[![Build workflow](https://img.shields.io/github/actions/workflow/status/" -"google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/" -"google/comprehensive-rust/actions/workflows/build.yml?query=branch%3Amain)" -msgstr "" -"[![构建工作流](https://img.shields.io/github/actions/workflow/status/" -"google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/" -"google/comprehensive-rust/actions/workflows/build.yml?query=branch%3Amain)" - -#: src/welcome.md:3 -msgid "Build workflow" -msgstr "构建工作流" - -#: src/welcome.md:3 +#: src/index.md:3 msgid "" "[![Build workflow](https://img.shields.io/github/actions/workflow/status/" "google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/" "google/comprehensive-rust/actions/workflows/build.yml?query=branch%3Amain) [!" "[GitHub contributors](https://img.shields.io/github/contributors/google/" "comprehensive-rust?style=flat-square)](https://github.com/google/" -"comprehensive-rust/graphs/contributors)" -msgstr "" -"[![构建工作流](https://img.shields.io/github/actions/workflow/status/" -"google/comprehensive-rust/build.yml?style=flat-square)](https://github.com/" -"google/comprehensive-rust/actions/workflows/build.yml?query=branch%3Amain) [!" -"[GitHub 贡献者](https://img.shields.io/github/contributors/google/" -"comprehensive-rust?style=flat-square)](https://github.com/google/" -"comprehensive-rust/graphs/contributors)" - -#: src/welcome.md:4 -msgid "GitHub contributors" -msgstr "GitHub 贡献者" - -#: src/welcome.md:4 -msgid "" -"[![GitHub contributors](https://img.shields.io/github/contributors/google/" -"comprehensive-rust?style=flat-square)](https://github.com/google/" "comprehensive-rust/graphs/contributors) [![GitHub stars](https://img.shields." "io/github/stars/google/comprehensive-rust?style=flat-square)](https://github." "com/google/comprehensive-rust/stargazers)" msgstr "" -#: src/welcome.md:5 -msgid "GitHub stars" -msgstr "GitHub stars" - -#: src/welcome.md:5 +#: src/index.md:7 msgid "" -"[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-" -"rust?style=flat-square)](https://github.com/google/comprehensive-rust/" -"stargazers)" +"This is a free Rust course developed by the Android team at Google. The " +"course covers the full spectrum of Rust, from basic syntax to advanced " +"topics like generics and error handling." msgstr "" -"[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-" -"rust?style=flat-square)](https://github.com/google/comprehensive-rust/" -"stargazers)" +"这是一门由 Google Android 团队开发的免费 Rust 课程。课程涵盖 Rust 的全部内" +"容,从基本语法到泛型和错误处理等高级主题。" -#: src/welcome.md:7 +#: src/index.md:11 msgid "" -"This is a three day Rust course developed by the Android team. The course " -"covers the full spectrum of Rust, from basic syntax to advanced topics like " -"generics and error handling. It also includes Android-specific content on " -"the last day." +"The latest version of the course can be found at . If you are reading somewhere else, please check there " +"for updates." msgstr "" -"这是由 Android 团队开发的为期三天的 Rust 课程。该课程涵盖了 Rust 的全部范围,从基本语法到高级主题如泛型和错误处理。最后一天还包括针对 Android 的特定内容。" +"课程的最新版本可在 上找到。如" +"果您在其他地方阅读,请查看那里的更新。" -#: src/welcome.md:11 +#: src/index.md:15 msgid "" "The goal of the course is to teach you Rust. We assume you don't know " "anything about Rust and hope to:" -msgstr "" -"本课程的目标是教授你 Rust。我们假设你对 Rust 一无所知,并希望能够:" +msgstr "本课程的目标是教授你 Rust。我们假设你对 Rust 一无所知,并希望能够:" -#: src/welcome.md:14 +#: src/index.md:18 msgid "Give you a comprehensive understanding of the Rust syntax and language." msgstr "帮助你全面理解 Rust 的语法和语言。" -#: src/welcome.md:15 +#: src/index.md:19 msgid "Enable you to modify existing programs and write new programs in Rust." msgstr "使你能够修改现有的程序并用 Rust 编写新程序。" -#: src/welcome.md:16 +#: src/index.md:20 msgid "Show you common Rust idioms." -msgstr "展示常见的 Rust 习语。" +msgstr "展示常见的 Rust 语言风格。" + +#: src/index.md:22 +msgid "We call the first three course days Rust Fundamentals." +msgstr "我们将前三天的课程称为 Rust 基础。" -#: src/welcome.md:18 +#: src/index.md:24 msgid "" -"The first three days show you the fundamentals of Rust. Following this, " -"you're invited to dive into one or more specialized topics:" -msgstr "" -"前三天将介绍 Rust 的基础知识。之后你可以选择深入学习一个或多个专门的主题:" +"Building on this, you're invited to dive into one or more specialized topics:" +msgstr "在此基础上,我们邀请您深入研究一个或多个专业课题:" -#: src/welcome.md:21 +#: src/index.md:26 msgid "" "[Android](android.md): a half-day course on using Rust for Android platform " "development (AOSP). This includes interoperability with C, C++, and Java." msgstr "" -"[Android](android.md):一个半天的课程,介绍如何在 Android 平台开发中使用 Rust(AOSP)。课程内容包括与 C、C++ 和 Java 的互操作性。" +"[Android](android.md):为期半天的课程,介绍如何在 Android 平台开发中使用 Rust" +"(AOSP)。课程内容包括与 C、C++ 和 Java 的互操作性。" -#: src/welcome.md:23 +#: src/index.md:28 msgid "" -"[Bare-metal](bare-metal.md): a full day class on using Rust for bare-metal " +"[Bare-metal](bare-metal.md): a whole-day class on using Rust for bare-metal " "(embedded) development. Both microcontrollers and application processors are " "covered." msgstr "" -"[Bare-metal](bare-metal.md):为期一天的课程,介绍如何使用 Rust 进行裸机(嵌入式)开发。课程内容涵盖微控制器和应用处理器。" +"[Bare-metal](bare-metal.md):为期一天的课程,介绍如何使用 Rust 进行裸机(嵌入" +"式)开发。课程内容涵盖微控制器和应用处理器。" -#: src/welcome.md:26 +#: src/index.md:31 msgid "" -"[Concurrency](concurrency.md): a full day class on concurrency in Rust. We " +"[Concurrency](concurrency.md): a whole-day class on concurrency in Rust. We " "cover both classical concurrency (preemptively scheduling using threads and " "mutexes) and async/await concurrency (cooperative multitasking using " "futures)." msgstr "" -"[并发](concurrency.md):一天的课程,介绍 Rust 中的并发性。我们将涵盖传统并发(使用线程和互斥锁进行抢占式调度)和 async/await 并发(使用 futures 进行协作式多任务处理)。" +"[并发](concurrency.md):为期一天的课程,介绍 Rust 中的并发性。我们将涵盖传统" +"并发(使用线程和互斥进行抢占式调度)和 async/await 并发(使用 futures 进行协" +"作式多任务处理)。" -#: src/welcome.md:32 +#: src/index.md:37 msgid "Non-Goals" msgstr "非目标" -#: src/welcome.md:34 +#: src/index.md:39 msgid "" "Rust is a large language and we won't be able to cover all of it in a few " "days. Some non-goals of this course are:" msgstr "" "Rust 是一门庞大的语言,我们无法在几天内涵盖所有内容。本课程的一些非目标包括:" -#: src/welcome.md:37 +#: src/index.md:42 msgid "" -"Learn how to develop macros, please see [Chapter 19.5 in the Rust Book]" +"Learning how to develop macros: please see [Chapter 19.5 in the Rust Book]" "(https://doc.rust-lang.org/book/ch19-06-macros.html) and [Rust by Example]" "(https://doc.rust-lang.org/rust-by-example/macros.html) instead." -msgstr "了解如何开发宏,请参阅[本书中的第 19.5 章](https://doc.rust-lang.org/book/ch19-06-macros.html)和 [Rust by Examples](https://doc.rust-lang.org/rust-by-example/macros.html)。" +msgstr "" +"了解如何开发宏,请参阅[本书中的第 19.5 章](https://doc.rust-lang.org/book/" +"ch19-06-macros.html)和 [Rust by Examples](https://doc.rust-lang.org/rust-by-" +"example/macros.html)。" -#: src/welcome.md:41 +#: src/index.md:46 msgid "Assumptions" msgstr "前提假设" -#: src/welcome.md:43 +#: src/index.md:48 msgid "" "The course assumes that you already know how to program. Rust is a " -"statically typed language and we will sometimes make comparisons with C and " +"statically-typed language and we will sometimes make comparisons with C and " "C++ to better explain or contrast the Rust approach." msgstr "" -"本课程假设你已经具备编程知识。Rust 是一种静态类型语言,我们有时会与 C 和 C++ 进行比较,以更好地解释或对比 Rust 的方法。" +"本课程假设你已经具备编程知识。Rust 是一种静态类型语言,我们有时会与 C 和 C++ " +"进行比较,以更好地解释或对比 Rust 的方法。" -#: src/welcome.md:47 +#: src/index.md:52 msgid "" -"If you know how to program in a dynamically typed language such as Python or " +"If you know how to program in a dynamically-typed language such as Python or " "JavaScript, then you will be able to follow along just fine too." msgstr "" -"如果你已经了解如 Python 或 JavaScript 等动态类型语言的编程,那么你也能够很好地跟上本课程。" +"如果你已经了解如 Python 或 JavaScript 等动态类型语言的编程,那么你也能够很好" +"地跟上本课程。" -#: src/welcome.md:52 +#: src/index.md:57 msgid "" -"This is an example of a speaker note. We will use these to add additional " +"This is an example of a _speaker note_. We will use these to add additional " "information to the slides. This could be key points which the instructor " "should cover as well as answers to typical questions which come up in class." msgstr "" -"这是演讲者备注的示例。我们将使用这些备注来为幻灯片添加额外的信息。这可能包括讲师应该涵盖的关键点,以及课堂上常见问题的答案。" +"这是演讲者备注的示例。我们将使用这些备注来为幻灯片添加额外的信息。这可能包括" +"讲师应该讲述的关键点,也可能是对课堂上出现的典型问题的回答。" #: src/running-the-course.md:3 src/running-the-course/course-structure.md:3 msgid "This page is for the course instructor." @@ -1181,10 +1173,24 @@ msgid "" msgstr "以下是有关 Google 内部授课方式的一些背景信息。" #: src/running-the-course.md:8 +msgid "" +"We typically run classes from 10:00 am to 4:00 pm, with a 1 hour lunch break " +"in the middle. This leaves 2.5 hours for the morning class and 2.5 hours for " +"the afternoon class. Note that this is just a recommendation: you can also " +"spend 3 hour on the morning session to give people more time for exercises. " +"The downside of longer session is that people can become very tired after 6 " +"full hours of class in the afternoon." +msgstr "" +"我们通常从上午 10:00 到下午 4:00,中间有 1 个小时的午休时间。这样,上午课程" +"为 2.5 小时,下午课程为 2.5 小时。请注意,这只是一个建议:您也可以把上午的课" +"程安排为 3 个小时,让人们有更多的时间做练习。课程时间长的缺点是,下午上满 6 " +"个小时的课后,人们会变得非常疲惫。" + +#: src/running-the-course.md:16 msgid "Before you run the course, you will want to:" msgstr "在授课之前,你需要完成以下事项:" -#: src/running-the-course.md:10 +#: src/running-the-course.md:18 msgid "" "Make yourself familiar with the course material. We've included speaker " "notes to help highlight the key points (please help us by contributing more " @@ -1197,7 +1203,7 @@ msgstr "" "在“演讲者备注”旁边有一个小箭头)。这样,你就可以确保屏幕整洁有序,更好地向全" "班学员展示课程内容。" -#: src/running-the-course.md:16 +#: src/running-the-course.md:24 msgid "" "Decide on the dates. Since the course takes at least three full days, we " "recommend that you schedule the days over two weeks. Course participants " @@ -1208,7 +1214,7 @@ msgstr "" "间。课程学员曾表示,在每堂课之间留一段间隔会很有帮助,因为这有利于他们吸收我" "们所提供的所有信息。" -#: src/running-the-course.md:21 +#: src/running-the-course.md:29 msgid "" "Find a room large enough for your in-person participants. We recommend a " "class size of 15-25 people. That's small enough that people are comfortable " @@ -1223,7 +1229,7 @@ msgstr "" "教室备有供你和学生使用的“课桌”:你们都需要能够坐下来并操作各自的笔记本电脑。" "特别是身为教师,你现场要进行大量编码,所以讲台对你来说用处不大。" -#: src/running-the-course.md:29 +#: src/running-the-course.md:37 msgid "" "On the day of your course, show up to the room a little early to set things " "up. We recommend presenting directly using `mdbook serve` running on your " @@ -1237,7 +1243,7 @@ msgstr "" "comprehensive-rust#building))。这样可以确保你在切换页面时没有延迟,演示效果" "更好。当你或课程学员发现拼写错误时,你也可以使用笔记本电脑及时更正。" -#: src/running-the-course.md:35 +#: src/running-the-course.md:43 msgid "" "Let people solve the exercises by themselves or in small groups. We " "typically spend 30-45 minutes on exercises in the morning and in the " @@ -1252,13 +1258,13 @@ msgstr "" "助。如果你看到多位学员遇到同样的问题,请在班级集体进行讲解,并提供相应的解决" "方案,例如告诉大家在标准库的什么位置可以找到相关信息。" -#: src/running-the-course.md:43 +#: src/running-the-course.md:51 msgid "" "That is all, good luck running the course! We hope it will be as much fun " "for you as it has been for us!" msgstr "今天的分享就是这些,祝你授课顺利!希望你和我们一样,乐在其中!" -#: src/running-the-course.md:46 +#: src/running-the-course.md:54 msgid "" "Please [provide feedback](https://github.com/google/comprehensive-rust/" "discussions/86) afterwards so that we can keep improving the course. We " @@ -1272,41 +1278,55 @@ msgstr "" "comprehensive-rust/discussions/100)!" #: src/running-the-course/course-structure.md:5 -msgid "The course is fast paced and covers a lot of ground:" -msgstr "本课程节奏紧凑,涵盖内容广泛:" +msgid "Rust Fundamentals" +msgstr "Rust 基础" #: src/running-the-course/course-structure.md:7 -msgid "Day 1: Basic Rust, ownership and the borrow checker." -msgstr "第 1 天:Rust 的基础知识、所有权和借用检查器。" - -#: src/running-the-course/course-structure.md:8 -msgid "Day 2: Compound data types, pattern matching, the standard library." -msgstr "第 2 天:复合数据类型、模式匹配和标准库。" +msgid "" +"The first three days make up [Rust Fundaments](../welcome-day-1.md). The " +"days are fast paced and we cover a lot of ground:" +msgstr "" +"前三天是[Rust 基础](../welcome-day-1.md)。这三天的节奏很快,我们要做的事情很" +"多:" -#: src/running-the-course/course-structure.md:9 -msgid "Day 3: Traits and generics, error handling, testing, unsafe Rust." -msgstr "第 3 天:trait 和泛型、错误处理、测试和不安全 Rust。" +#: src/running-the-course/course-structure.md:10 +msgid "Day 1: Basic Rust, syntax, control flow, creating and consuming values." +msgstr "第 1 天:Rust 基础、语法、控制流、创建和销毁值。" #: src/running-the-course/course-structure.md:11 +msgid "" +"Day 2: Memory management, ownership, compound data types, and the standard " +"library." +msgstr "第 2 天:内存管理、所有权、复合数据类型和标准库。" + +#: src/running-the-course/course-structure.md:12 +msgid "Day 3: Generics, traits, error handling, testing, and unsafe Rust." +msgstr "第 3 天:泛型、特型、错误处理、测试和不安全的 Rust。" + +#: src/running-the-course/course-structure.md:14 msgid "Deep Dives" msgstr "深入探究" -#: src/running-the-course/course-structure.md:13 +#: src/running-the-course/course-structure.md:16 msgid "" "In addition to the 3-day class on Rust Fundamentals, we cover some more " "specialized topics:" msgstr "除了为期 3 天的“Rust 基础知识”课程外,我们还推出了一些专题课程:" -#: src/running-the-course/course-structure.md:18 +#: src/running-the-course/course-structure.md:19 +msgid "Rust in Android" +msgstr "欢迎来到 Android 中的 Rust" + +#: src/running-the-course/course-structure.md:21 msgid "" -"The [Android Deep Dive](../android.md) is a half-day course on using Rust " -"for Android platform development. This includes interoperability with C, C+" -"+, and Java." +"The [Rust in Android](../android.md) deep dive is a half-day course on using " +"Rust for Android platform development. This includes interoperability with " +"C, C++, and Java." msgstr "" "“[深入探究 Android](../android.md)”课程为期半天,旨在介绍如何使用 Rust 进行 " "Android 平台开发。其中包括与 C、C++ 和 Java 的互操作性。" -#: src/running-the-course/course-structure.md:22 +#: src/running-the-course/course-structure.md:25 msgid "" "You will need an [AOSP checkout](https://source.android.com/docs/setup/" "download/downloading). Make a checkout of the [course repository](https://" @@ -1319,7 +1339,7 @@ msgstr "" "comprehensive-rust), 然后将 `src/android/` 目录移至所签出的 AOSP 的根目录。" "这将确保 Android 构建系统能检测到 `src/android/` 中的 `Android.bp` 文件。" -#: src/running-the-course/course-structure.md:27 +#: src/running-the-course/course-structure.md:30 msgid "" "Ensure that `adb sync` works with your emulator or real device and pre-build " "all Android examples using `src/android/build_all.sh`. Read the script to " @@ -1329,20 +1349,20 @@ msgstr "" "sh` 预构建所有 Android 示例。请阅读脚本, 查看它所运行的命令,并确保这些命令" "能在你手动运行时正确执行。" -#: src/running-the-course/course-structure.md:34 -msgid "Bare-Metal" -msgstr "裸机" +#: src/running-the-course/course-structure.md:37 +msgid "Bare-Metal Rust" +msgstr "欢迎来到物理机中的 Rust" -#: src/running-the-course/course-structure.md:36 +#: src/running-the-course/course-structure.md:39 msgid "" -"The [Bare-Metal Deep Dive](../bare-metal.md): a full day class on using Rust " -"for bare-metal (embedded) development. Both microcontrollers and application " -"processors are covered." +"The [Bare-Metal Rust](../bare-metal.md) deep dive is a full day class on " +"using Rust for bare-metal (embedded) development. Both microcontrollers and " +"application processors are covered." msgstr "" -"“[深入探究裸机](../bare-metal.md)”课程为期一天,旨在介绍如何使用 Rust 进行裸" -"机(嵌入式)开发。其中涵盖了微控制器和应用 处理器。" +"“[深入探究物理机](../bare-metal.md)”的课程为期一天,旨在介绍如何使用 Rust 进" +"行物理机(嵌入式)开发。其中涵盖了微控制器和应用处理器。" -#: src/running-the-course/course-structure.md:40 +#: src/running-the-course/course-structure.md:43 msgid "" "For the microcontroller part, you will need to buy the [BBC micro:bit]" "(https://microbit.org/) v2 development board ahead of time. Everybody will " @@ -1353,19 +1373,19 @@ msgstr "" "版开发板。每个人都需要安装多个软件包, 具体如[欢迎页面](../bare-metal.md)中所" "述。" -#: src/running-the-course/course-structure.md:45 -msgid "Concurrency" -msgstr "并发" +#: src/running-the-course/course-structure.md:48 +msgid "Concurrency in Rust" +msgstr "欢迎了解 Rust 中的并发" -#: src/running-the-course/course-structure.md:47 +#: src/running-the-course/course-structure.md:50 msgid "" -"The [Concurrency Deep Dive](../concurrency.md) is a full day class on " -"classical as well as `async`/`await` concurrency." +"The [Concurrency in Rust](../concurrency.md) deep dive is a full day class " +"on classical as well as `async`/`await` concurrency." msgstr "" "“[深入探究并发](../concurrency.md)”课程为期一天,旨在介绍传统并发和 `async`/" "`await` 并发。" -#: src/running-the-course/course-structure.md:50 +#: src/running-the-course/course-structure.md:53 msgid "" "You will need a fresh crate set up and the dependencies downloaded and ready " "to go. You can then copy/paste the examples into `src/main.rs` to experiment " @@ -1374,27 +1394,11 @@ msgstr "" "你需要设置一个新 crate,下载所需的依赖项, 做好课前准备。然后,你可以将示例复" "制/粘贴到 `src/main.rs` 中, 以便对以下代码进行实验:" -#: src/running-the-course/course-structure.md:54 -msgid "" -"```shell\n" -"cargo init concurrency\n" -"cd concurrency\n" -"cargo add tokio --features full\n" -"cargo run\n" -"```" -msgstr "" -"```shell\n" -"cargo init concurrency\n" -"cd concurrency\n" -"cargo add tokio --features full\n" -"cargo run\n" -"```" - -#: src/running-the-course/course-structure.md:61 +#: src/running-the-course/course-structure.md:64 msgid "Format" msgstr "课程形式" -#: src/running-the-course/course-structure.md:63 +#: src/running-the-course/course-structure.md:66 msgid "" "The course is meant to be very interactive and we recommend letting the " "questions drive the exploration of Rust!" @@ -1402,7 +1406,7 @@ msgstr "本课程的互动性非常强, 建议你以问题驱动探索 Rust! #: src/running-the-course/keyboard-shortcuts.md:3 msgid "There are several useful keyboard shortcuts in mdBook:" -msgstr "mdBook 中有一些实用键盘快捷键:" +msgstr "在 mdBook 中有一些实用键盘快捷键:" #: src/running-the-course/keyboard-shortcuts.md:5 msgid "Arrow-Left" @@ -1444,34 +1448,92 @@ msgstr "一批优秀的志愿者已将本课程翻译成其他语言:" #: src/running-the-course/translations.md:6 msgid "" -"\\[Brazilian Portuguese\\]\\[pt-BR\\] by \\[@rastringer\\] and " -"\\[@hugojacob\\]." +"[Brazilian Portuguese](https://google.github.io/comprehensive-rust/pt-BR/) " +"by [@rastringer](https://github.com/rastringer), [@hugojacob](https://github." +"com/hugojacob), [@joaovicmendes](https://github.com/joaovicmendes), and " +"[@henrif75](https://github.com/henrif75)." msgstr "" -"[巴西葡萄牙语](https://google.github.io/comprehensive-rust/pt-BR/):" -"[@rastringer](https://github.com/rastringer) 和 [@hugojacob](https://github." -"com/hugojacob)。" #: src/running-the-course/translations.md:7 msgid "" -"\\[Korean\\]\\[ko\\] by \\[@keispace\\], \\[@jiyongp\\] and " -"\\[@jooyunghan\\]." +"[Korean](https://google.github.io/comprehensive-rust/ko/) by [@keispace]" +"(https://github.com/keispace), [@jiyongp](https://github.com/jiyongp), and " +"[@jooyunghan](https://github.com/jooyunghan)." msgstr "" -"[韩语](https://google.github.io/comprehensive-rust/ko/):[@keispace](https://" -"github.com/keispace)、[@jiyongp](https://github.com/jiyongp) 和 [@jooyunghan]" -"(https://github.com/jooyunghan)。" -#: src/running-the-course/translations.md:9 +#: src/running-the-course/translations.md:8 +msgid "" +"[Spanish](https://google.github.io/comprehensive-rust/es/) by [@deavid]" +"(https://github.com/deavid)." +msgstr "" + +#: src/running-the-course/translations.md:10 msgid "" "Use the language picker in the top-right corner to switch between languages." msgstr "使用右上角的语言选择器切换语言。" -#: src/running-the-course/translations.md:20 +#: src/running-the-course/translations.md:12 +msgid "Incomplete Translations" +msgstr "未完成的翻译" + +#: src/running-the-course/translations.md:14 +msgid "" +"There is a large number of in-progress translations. We link to the most " +"recently updated translations:" +msgstr "多数语言版本仍在翻译中。我们会提供最近更新的翻译的链接:" + +#: src/running-the-course/translations.md:17 +msgid "" +"[Bengali](https://google.github.io/comprehensive-rust/bn/) by [@raselmandol]" +"(https://github.com/raselmandol)." +msgstr "" + +#: src/running-the-course/translations.md:18 +msgid "" +"[Chinese (Traditional)](https://google.github.io/comprehensive-rust/zh-TW/) " +"by [@hueich](https://github.com/hueich), [@victorhsieh](https://github.com/" +"victorhsieh), [@mingyc](https://github.com/mingyc), and [@johnathan79717]" +"(https://github.com/johnathan79717)." +msgstr "" + +#: src/running-the-course/translations.md:19 +msgid "" +"[Chinese (Simplified)](https://google.github.io/comprehensive-rust/zh-CN/) " +"by [@suetfei](https://github.com/suetfei), [@wnghl](https://github.com/" +"wnghl), [@anlunx](https://github.com/anlunx), [@kongy](https://github.com/" +"kongy), [@noahdragon](https://github.com/noahdragon), and [@superwhd]" +"(https://github.com/superwhd)." +msgstr "" + +#: src/running-the-course/translations.md:20 +msgid "" +"[French](https://google.github.io/comprehensive-rust/fr/) by [@KookaS]" +"(https://github.com/KookaS) and [@vcaen](https://github.com/vcaen)." +msgstr "" + +#: src/running-the-course/translations.md:21 +msgid "" +"[German](https://google.github.io/comprehensive-rust/de/) by [@Throvn]" +"(https://github.com/Throvn) and [@ronaldfw](https://github.com/ronaldfw)." +msgstr "" + +#: src/running-the-course/translations.md:22 msgid "" -"If you want to help with this effort, please see \\[our instructions\\] for " -"how to get going. Translations are coordinated on the \\[issue tracker\\]." +"[Japanese](https://google.github.io/comprehensive-rust/ja/) by [@CoinEZ-JPN]" +"(https://github.com/CoinEZ) and [@momotaro1105](https://github.com/" +"momotaro1105)." msgstr "" -"如果你想参与其中,请参阅\\[我们的说明\\], 了解如何开始翻译。翻译工作将通过" -"\\[问题跟踪器\\]进行协调。" + +#: src/running-the-course/translations.md:24 +msgid "" +"If you want to help with this effort, please see [our instructions](https://" +"github.com/google/comprehensive-rust/blob/main/TRANSLATIONS.md) for how to " +"get going. Translations are coordinated on the [issue tracker](https://" +"github.com/google/comprehensive-rust/issues/282)." +msgstr "" +"如果你想参与其中,请参阅[我们的说明](https://github.com/google/comprehensive-" +"rust/blob/main/TRANSLATIONS.md), 了解如何开始翻译。翻译工作将通过 [问题跟踪" +"器](https://github.com/google/comprehensive-rust/issues/282)进行协作。" #: src/cargo.md:3 msgid "" @@ -1491,74 +1553,46 @@ msgid "Installation" msgstr "安装" #: src/cargo.md:10 -msgid "Rustup (Recommended)" -msgstr "Rustup(推荐)" +msgid "**Please follow the instructions on .**" +msgstr "**请按照 上的说明操作。**" #: src/cargo.md:12 msgid "" -"You can follow the instructions to install cargo and rust compiler, among " -"other standard ecosystem tools with the [rustup](https://rust-analyzer." -"github.io/) tool, which is maintained by the Rust Foundation." +"This will give you the Cargo build tool (`cargo`) and the Rust compiler " +"(`rustc`). You will also get `rustup`, a command line utility that you can " +"use to install to different compiler versions." msgstr "" -"你可以按照说明,使用由 Rust 基金会维护的 [rustup](https://rust-analyzer." -"github.io/) 工具安装 cargo 和 rust 编译器,以及其他标准的生态系统工具。" +"这将为你提供 Cargo 编译工具 (`cargo`) 和 Rust 编译器 (`rustc`)。你还会得到 " +"`rustup`,这是一个命令行工具,可以用来安装不同版本的编译器。" #: src/cargo.md:14 msgid "" -"Along with cargo and rustc, Rustup will install itself as a command line " -"utility that you can use to install/switch toolchains, setup cross " -"compilation, etc." -msgstr "" -"Rustup 将与 cargo 和 rustc 一起安装为命令行实用程序,你可以用它来安装/切换工" -"具链、设置交叉编译等。" - -#: src/cargo.md:16 -msgid "Package Managers" -msgstr "软件包管理系统" +"After installing Rust, you should configure your editor or IDE to work with " +"Rust. Most editors do this by talking to [rust-analyzer](https://rust-" +"analyzer.github.io/), which provides auto-completion and jump-to-definition " +"functionality for [VS Code](https://code.visualstudio.com/), [Emacs](https://" +"rust-analyzer.github.io/manual.html#emacs), [Vim/Neovim](https://rust-" +"analyzer.github.io/manual.html#vimneovim), and many others. There is also a " +"different IDE available called [RustRover](https://www.jetbrains.com/rust/)." +msgstr "" +"安装 Rust 后,你应该配置编辑器或集成开发环境,以便与 Rust 协同工作。大多数编" +"辑器都是通过与 [rust-analyzer](https://rust-analyzer.github.io/) 对话来实现这" +"一点的,它为 [VS Code](https://code.visualstudio.com/), [Emacs](https://rust-" +"analyzer.github.io/manual.html#emacs), [Vim/Neovim](https://rust-analyzer." +"github.io/manual.html#vimneovim), 和其他许多编辑器提供了自动补全和跳转到定义" +"的功能。还有另外一种不同的集成开发环境,名为 [RustRover](https://www." +"jetbrains.com/rust/)。" #: src/cargo.md:18 -msgid "Debian" -msgstr "Debian" - -#: src/cargo.md:20 -msgid "" -"On Debian/Ubuntu, you can install Cargo, the Rust source and the [Rust " -"formatter](https://github.com/rust-lang/rustfmt) with" -msgstr "" -"在 Debian/Ubuntu 上,你可以通过以下方式安装 Cargo、Rust 源代码和 [Rust 格式设" -"置工具](https://github.com/rust-lang/rustfmt):" - -#: src/cargo.md:22 -msgid "" -"```shell\n" -"$ sudo apt install cargo rust-src rustfmt\n" -"```" -msgstr "" -"```shell\n" -"$ sudo apt install cargo rust-src rustfmt\n" -"```" - -#: src/cargo.md:26 msgid "" -"This will allow \\[rust-analyzer\\]\\[1\\] to jump to the definitions. We " -"suggest using [VS Code](https://code.visualstudio.com/) to edit the code " -"(but any LSP compatible editor works)." +"On Debian/Ubuntu, you can also install Cargo, the Rust source and the [Rust " +"formatter](https://github.com/rust-lang/rustfmt) via `apt`. However, this " +"gets you an outdated rust version and may lead to unexpected behavior. The " +"command would be:" msgstr "" -"这将允许 \\[rust-analyzer\\]\\[1\\] 跳到定义。我们建议使用 [VS Code](https://" -"code.visualstudio.com/) 来编写代码(也可使用任何兼容 LSP 的编辑器)。" - -#: src/cargo.md:29 -msgid "" -"Some folks also like to use the [JetBrains](https://www.jetbrains.com/" -"clion/) family of IDEs, which do their own analysis but have their own " -"tradeoffs. If you prefer them, you can install the [Rust Plugin](https://www." -"jetbrains.com/rust/). Please take note that as of January 2023 debugging " -"only works on the CLion version of the JetBrains IDEA suite." -msgstr "" -"有些人还喜欢使用 [JetBrains](https://www.jetbrains.com/clion/) IDE 系列自行分" -"析,但会根据自身情况加以权衡。如果你想使用这些工具,可以安装 [Rust 插件]" -"(https://www.jetbrains.com/rust/)。请注意,自 2023 年 1 月起,调试仅适用于 " -"CLion 版本的 JetBrains IDEA 套件。" +"在 Debian/Ubuntu 上,你也可以通过 apt 安装 Cargo、Rust 源代码和 [Rust 格式化" +"工具](https://github.com/rust-lang/rustfmt)。不过,这样会得到一个过时的 Rust " +"版本,并可能导致意想不到的行为。命令如下:" #: src/cargo/rust-ecosystem.md:1 msgid "The Rust Ecosystem" @@ -1578,13 +1612,13 @@ msgstr "`rustc`:Rust 编译器,可将 `.rs` 文件转换为二进制文件 #: src/cargo/rust-ecosystem.md:8 msgid "" "`cargo`: the Rust dependency manager and build tool. Cargo knows how to " -"download dependencies hosted on and it will pass them to " -"`rustc` when building your project. Cargo also comes with a built-in test " -"runner which is used to execute unit tests." +"download dependencies, usually hosted on , and it will " +"pass them to `rustc` when building your project. Cargo also comes with a " +"built-in test runner which is used to execute unit tests." msgstr "" -"`cargo`:Rust 依赖项管理器和构建工具。Cargo 知道如何 下载托管在 上的依赖项,并在构建项目时将它们 传递给 `rustc`。Cargo 还附带一个" -"内置的 测试运行程序,用于执行单元测试。" +"`cargo`:Rust 依赖关系管理器和构建工具。Cargo 知道如何下载依赖项(通常托管在 " +" 上),并会在构建项目时将其传递给 rustc。Cargo 还内置了一" +"个测试运行器,用于执行单元测试。" #: src/cargo/rust-ecosystem.md:13 msgid "" @@ -1603,6 +1637,7 @@ msgstr "" #: src/why-rust/modern.md:21 src/basic-syntax/compound-types.md:30 #: src/basic-syntax/references.md:23 #: src/pattern-matching/destructuring-enums.md:35 +#: src/ownership/double-free-modern-cpp.md:55 #: src/error-handling/try-operator.md:48 #: src/error-handling/converting-error-types-example.md:50 #: src/concurrency/threads.md:30 src/async/async-await.md:25 @@ -1615,89 +1650,100 @@ msgid "" "weeks. New releases maintain backwards compatibility with old releases --- " "plus they enable new functionality." msgstr "" -"Rust 有一个快速发布时间表,每六周就会发布一次 新版本。新版本保持与 旧版本的向" -"后兼容性,还添加了新功能。" +"Rust 有一个快速发布时间表,每六周就会发布一次新版本。新版本保持与旧版本的向后" +"兼容性,还添加了新功能。" #: src/cargo/rust-ecosystem.md:27 msgid "" "There are three release channels: \"stable\", \"beta\", and \"nightly\"." -msgstr "共有三个发布阶段:“稳定版”“Beta 版”和“夜间版”。" +msgstr "共有三个发布阶段:“stable”“beta 版”和“nightly”。" #: src/cargo/rust-ecosystem.md:29 msgid "" "New features are being tested on \"nightly\", \"beta\" is what becomes " "\"stable\" every six weeks." -msgstr "我们会在“夜间版”上测试新功能,每六周将“Beta 版”升级为 “稳定版”。" +msgstr "新功能在 “nightly “进行测试,”beta “每六周变为 “stable”。" #: src/cargo/rust-ecosystem.md:32 msgid "" +"Dependencies can also be resolved from alternative [registries](https://doc." +"rust-lang.org/cargo/reference/registries.html), git, folders, and more." +msgstr "" +"依赖关系还可以通过其他 [registries](https://doc.rust-lang.org/cargo/" +"reference/registries.html)、git、文件夹等解决。" + +#: src/cargo/rust-ecosystem.md:34 +msgid "" "Rust also has [editions](https://doc.rust-lang.org/edition-guide/): the " "current edition is Rust 2021. Previous editions were Rust 2015 and Rust 2018." msgstr "" -"Rust 也有三个\\[版本\\]:当前版本是 Rust 2021。之前的 版本是 Rust 2015 和 " -"Rust 2018。" +"Rust 也有[版本](https://doc.rust-lang.org/edition-guide/):当前版本是 Rust " +"2021。之前的 版本是 Rust 2015 和 Rust 2018。" -#: src/cargo/rust-ecosystem.md:35 +#: src/cargo/rust-ecosystem.md:37 msgid "" "The editions are allowed to make backwards incompatible changes to the " "language." -msgstr "这些版本支持对语言进行向后不兼容的 更改。" +msgstr "这些版本允许对语言进行向后不兼容的修改。" -#: src/cargo/rust-ecosystem.md:38 +#: src/cargo/rust-ecosystem.md:40 msgid "" "To prevent breaking code, editions are opt-in: you select the edition for " "your crate via the `Cargo.toml` file." msgstr "" -"为防止破坏代码,你可以自行选择版本: 通过 `Cargo.toml` 文件为 crate 选择合适" -"的版本。" +"为防止破坏代码,版本是选择性的: 通过 `Cargo.toml` 文件为 crate 选择合适的版" +"本。" -#: src/cargo/rust-ecosystem.md:41 +#: src/cargo/rust-ecosystem.md:43 msgid "" "To avoid splitting the ecosystem, Rust compilers can mix code written for " "different editions." -msgstr "为免分割生态系统,Rust 编译器可以混合使用 为不同版本编写的代码。" +msgstr "为了避免生态系统分裂,Rust 编译器可以混合使用为不同版本编写的代码。" -#: src/cargo/rust-ecosystem.md:44 +#: src/cargo/rust-ecosystem.md:46 msgid "" "Mention that it is quite rare to ever use the compiler directly not through " "`cargo` (most users never do)." msgstr "" -"提及不通过 `cargo` 而直接使用编译器的情况相当少见(大多数用户从不这样做)。" +"请注意,不通过 `cargo` 命令而直接操作编译器的情况相当少见(大多数用户从不这样" +"做)。" -#: src/cargo/rust-ecosystem.md:46 +#: src/cargo/rust-ecosystem.md:48 msgid "" "It might be worth alluding that Cargo itself is an extremely powerful and " "comprehensive tool. It is capable of many advanced features including but " "not limited to: " msgstr "" "值得注意的是,Cargo 本身就是一个功能强大且全面的工具。它能够实现许多高级功" -"能,包括但不限于:" +"能,包括但不限于: " -#: src/cargo/rust-ecosystem.md:47 +#: src/cargo/rust-ecosystem.md:49 msgid "Project/package structure" msgstr "项目/软件包结构" -#: src/cargo/rust-ecosystem.md:48 +#: src/cargo/rust-ecosystem.md:50 msgid "[workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html)" -msgstr "\\[工作区\\]" +msgstr "[工作区]((https://doc.rust-lang.org/cargo/reference/workspaces.html))" -#: src/cargo/rust-ecosystem.md:49 +#: src/cargo/rust-ecosystem.md:51 msgid "Dev Dependencies and Runtime Dependency management/caching" -msgstr "开发依赖项和运行时依赖项管理/缓存" +msgstr "管理/缓存开发依赖项和运行时依赖项" -#: src/cargo/rust-ecosystem.md:50 +#: src/cargo/rust-ecosystem.md:52 msgid "" "[build scripting](https://doc.rust-lang.org/cargo/reference/build-scripts." "html)" -msgstr "\\[构建脚本\\]" +msgstr "" +"[构建脚本](https://doc.rust-lang.org/cargo/reference/build-scripts.html)" -#: src/cargo/rust-ecosystem.md:51 +#: src/cargo/rust-ecosystem.md:53 msgid "" "[global installation](https://doc.rust-lang.org/cargo/commands/cargo-install." "html)" -msgstr "\\[全局安装\\] \\]" +msgstr "" +"[全局安装](https://doc.rust-lang.org/cargo/commands/cargo-install.html)" -#: src/cargo/rust-ecosystem.md:52 +#: src/cargo/rust-ecosystem.md:54 msgid "" "It is also extensible with sub command plugins as well (such as [cargo " "clippy](https://github.com/rust-lang/rust-clippy))." @@ -1705,10 +1751,10 @@ msgstr "" "它还可以使用子命令插件(例如 [cargo clippy](https://github.com/rust-lang/" "rust-clippy))进行扩展。" -#: src/cargo/rust-ecosystem.md:53 +#: src/cargo/rust-ecosystem.md:55 msgid "" "Read more from the [official Cargo Book](https://doc.rust-lang.org/cargo/)" -msgstr "如需了解详情,请参阅\\[ Cargo 官方图书\\]" +msgstr "如需了解详情,请参阅[Cargo 官方手册](https://doc.rust-lang.org/cargo/)" #: src/cargo/code-samples.md:1 msgid "Code Samples in This Training" @@ -1720,7 +1766,7 @@ msgid "" "which can be executed through your browser. This makes the setup much easier " "and ensures a consistent experience for everyone." msgstr "" -"在本培训中,我们将主要通过示例 探索 Rust 语言,这些示例可通过浏览器执行。这能" +"在本培训中,我们将主要通过示例探索 Rust 语言,这些示例可通过浏览器执行。这能" "大大简化设置过程, 并确保所有人都能获得一致的体验。" #: src/cargo/code-samples.md:7 @@ -1729,7 +1775,7 @@ msgid "" "the exercises. On the last day, we will do a larger exercise which shows you " "how to work with dependencies and for that you need Cargo." msgstr "" -"我们仍然建议你安装 Cargo:它有助于你更轻松地完成 练习。在最后一天,我们要做一" +"我们仍然建议你安装 Cargo:它有助于你更轻松地完成练习。在最后一天,我们要做一" "个更大的练习, 向你展示如何使用依赖项,因此你需要安装 Cargo。" #: src/cargo/code-samples.md:11 @@ -1752,7 +1798,7 @@ msgstr "" #: src/cargo/code-samples.md:19 msgid "You can use " -msgstr "当文本框为 焦点时,你可以使用 " +msgstr "当文本框为焦点时,你可以使用 " #: src/cargo/code-samples.md:19 msgid " to execute the code when focus is in the text box." @@ -1763,7 +1809,7 @@ msgid "" "Most code samples are editable like shown above. A few code samples are not " "editable for various reasons:" msgstr "" -"大多数代码示例都可修改(如上图所示)。少数代码示例 可能会因各种原因而不可修" +"大多数代码示例都可修改(如上图所示)。少数代码示例可能会因各种原因而不可修" "改:" #: src/cargo/code-samples.md:27 @@ -1771,7 +1817,7 @@ msgid "" "The embedded playgrounds cannot execute unit tests. Copy-paste the code and " "open it in the real Playground to demonstrate unit tests." msgstr "" -"嵌入式 Playground 无法执行单元测试。将代码复制并粘贴 到实际 Playground 中,以" +"嵌入式 Playground 无法执行单元测试。将代码复制并粘贴到实际 Playground 中,以" "演示单元测试。" #: src/cargo/code-samples.md:30 @@ -1780,8 +1826,8 @@ msgid "" "the page! This is the reason that the students should solve the exercises " "using a local Rust installation or via the Playground." msgstr "" -"嵌入式 Playground 会在你离开页面后立即 丢失其状态!正因如此,学员应使用本地" -"安装的 Rust 或通过 Playground 解题。" +"嵌入式 Playground 会在你离开页面后立即丢失其状态!正因如此,学员应使用本地安" +"装的 Rust 或通过 Playground 解题。" #: src/cargo/running-locally.md:1 msgid "Running Code Locally with Cargo" @@ -1800,84 +1846,42 @@ msgstr "" "作。这应会为你提供一个有效的 `rustc` 和 `cargo`。在撰写 本文时,最新的 Rust " "稳定版具有以下版本号:" -#: src/cargo/running-locally.md:8 +#: src/cargo/running-locally.md:15 msgid "" -"```shell\n" -"% rustc --version\n" -"rustc 1.69.0 (84c898d65 2023-04-16)\n" -"% cargo --version\n" -"cargo 1.69.0 (6e9a83356 2023-04-12)\n" -"```" -msgstr "" -"```shell\n" -"% rustc --version\n" -"rustc 1.69.0 (84c898d65 2023-04-16)\n" -"% cargo --version\n" -"cargo 1.69.0 (6e9a83356 2023-04-12)\n" -"```" +"You can use any later version too since Rust maintains backwards " +"compatibility." +msgstr "您也可以使用任意最新的版本,因为 Rust 保持了向后兼容性。" -#: src/cargo/running-locally.md:15 +#: src/cargo/running-locally.md:17 msgid "" "With this in place, follow these steps to build a Rust binary from one of " "the examples in this training:" msgstr "" -"了解这些信息后,请按照以下步骤从本培训中的 一个示例中构建 Rust 二进制文件:" +"了解这些信息后,请按照以下步骤从本培训中的一个示例中构建 Rust 二进制文件:" -#: src/cargo/running-locally.md:18 +#: src/cargo/running-locally.md:20 msgid "Click the \"Copy to clipboard\" button on the example you want to copy." msgstr "在你要复制的示例上点击“复制到剪贴板”按钮。" -#: src/cargo/running-locally.md:20 +#: src/cargo/running-locally.md:22 msgid "" "Use `cargo new exercise` to create a new `exercise/` directory for your code:" msgstr "使用 `cargo new exercise` 为你的代码新建一个 `exercise/` 目录:" -#: src/cargo/running-locally.md:22 -msgid "" -"```shell\n" -"$ cargo new exercise\n" -" Created binary (application) `exercise` package\n" -"```" -msgstr "" -"```shell\n" -"$ cargo new exercise\n" -" Created binary (application) `exercise` package\n" -"```" - -#: src/cargo/running-locally.md:27 +#: src/cargo/running-locally.md:29 msgid "" "Navigate into `exercise/` and use `cargo run` to build and run your binary:" msgstr "导航至 `exercise/` 并使用 `cargo run` 构建并运行你的二进制文件:" -#: src/cargo/running-locally.md:29 -msgid "" -"```shell\n" -"$ cd exercise\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" -" Running `target/debug/exercise`\n" -"Hello, world!\n" -"```" -msgstr "" -"```shell\n" -"$ cd exercise\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" -" Running `target/debug/exercise`\n" -"Hello, world!\n" -"```" - -#: src/cargo/running-locally.md:38 +#: src/cargo/running-locally.md:40 msgid "" "Replace the boiler-plate code in `src/main.rs` with your own code. For " "example, using the example on the previous page, make `src/main.rs` look like" msgstr "" -"将 `src/main.rs` 中的样板代码替换为你自己的代码。例如, 使用上一页中的示例," -"将 `src/main.rs` 改为:" +"用你自己的代码替换 `src/main.rs` 中的模板代码。例如,使用上一页中的示例,将 " +"`src/main.rs` 改成如下样子" -#: src/cargo/running-locally.md:41 +#: src/cargo/running-locally.md:43 msgid "" "```rust\n" "fn main() {\n" @@ -1891,29 +1895,11 @@ msgstr "" "}\n" "```" -#: src/cargo/running-locally.md:47 +#: src/cargo/running-locally.md:49 msgid "Use `cargo run` to build and run your updated binary:" msgstr "使用 `cargo run` 构建并运行你更新后的二进制文件:" -#: src/cargo/running-locally.md:49 -msgid "" -"```shell\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" -" Running `target/debug/exercise`\n" -"Edit me!\n" -"```" -msgstr "" -"```shell\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" -" Running `target/debug/exercise`\n" -"Edit me!\n" -"```" - -#: src/cargo/running-locally.md:57 +#: src/cargo/running-locally.md:59 msgid "" "Use `cargo check` to quickly check your project for errors, use `cargo " "build` to compile it without running it. You will find the output in `target/" @@ -1922,25 +1908,25 @@ msgid "" msgstr "" "使用 `cargo check` 快速检查项目是否存在错误;使用 `cargo build` 只进行编译," "而不运行。你可以在 `target/debug/` 中找到常规调试 build 的输出。使用 `cargo " -"build --release` 在 `target/release/` 中生成经过优化的 发布 build。" +"build --release` 在 `target/release/` 中生成经过优化的发布 build。" -#: src/cargo/running-locally.md:62 +#: src/cargo/running-locally.md:64 msgid "" "You can add dependencies for your project by editing `Cargo.toml`. When you " "run `cargo` commands, it will automatically download and compile missing " "dependencies for you." msgstr "" -"你可以通过修改 `Cargo.toml` 为项目添加依赖项。当你 运行 `cargo` 命令时,系统" -"会自动为你下载和编译缺失 的依赖项。" +"你可以通过修改 `Cargo.toml` 为项目添加依赖项。当你运行 `cargo` 命令时,系统会" +"自动为你下载和编译缺失的依赖项。" -#: src/cargo/running-locally.md:70 +#: src/cargo/running-locally.md:72 msgid "" "Try to encourage the class participants to install Cargo and use a local " "editor. It will make their life easier since they will have a normal " "development environment." msgstr "" -"尽量鼓励全班学员安装 Cargo 并使用 本地编辑器。这能为他们营造常规 开发环境,让" -"工作变得更加轻松。" +"尽量鼓励全班学员安装 Cargo 并使用本地编辑器。这能为他们营造常规开发环境,让工" +"作变得更加轻松。" #: src/welcome-day-1.md:1 msgid "Welcome to Day 1" @@ -1948,9 +1934,9 @@ msgstr "欢迎来到第一天" #: src/welcome-day-1.md:3 msgid "" -"This is the first day of Comprehensive Rust. We will cover a lot of ground " +"This is the first day of Rust Fundamentals. We will cover a lot of ground " "today:" -msgstr "现在是学习 Comprehensive Rust 的第一天。今天我们会涉及很多内容:" +msgstr "今天是 Rust 基础课程的第一天。今天我们将学习很多内容:" #: src/welcome-day-1.md:6 msgid "" @@ -1962,16 +1948,14 @@ msgstr "" #: src/welcome-day-1.md:9 msgid "" -"Memory management: stack vs heap, manual memory management, scope-based " -"memory management, and garbage collection." -msgstr "内存管理:栈与堆,手动内存管理,基于作用域的内存管理,以及垃圾回收。" +"Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and " +"`continue`." +msgstr "" +"控制流的构造: `if`, `if let`, `while`, `while let`, `break`, 和 `continue`。" #: src/welcome-day-1.md:12 -msgid "" -"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." -msgstr "" -"所有权:移动(move)的语义,复制(copy)和克隆(clone),借用(borrow),以及" -"生命周期。" +msgid "Pattern matching: destructuring enums, structs, and arrays." +msgstr "模式匹配: 解构枚举, 结构体和数组(array)。" #: src/welcome-day-1.md:16 msgid "Please remind the students that:" @@ -1986,23 +1970,24 @@ msgstr "他们可以随时提问,不需要留到最后。" msgid "" "The class is meant to be interactive and discussions are very much " "encouraged!" -msgstr "这个课程本应该是互动的,我们鼓励大家积极讨论。" +msgstr "课堂旨在互动,非常鼓励讨论!" #: src/welcome-day-1.md:20 msgid "" "As an instructor, you should try to keep the discussions relevant, i.e., " -"keep the related to how Rust does things vs some other language. It can be " -"hard to find the right balance, but err on the side of allowing discussions " -"since they engage people much more than one-way communication." +"keep the discussions related to how Rust does things vs some other " +"language. It can be hard to find the right balance, but err on the side of " +"allowing discussions since they engage people much more than one-way " +"communication." msgstr "" -"作为讲师,你应该尽量保证讨论话题的相关性,例如,讨论围绕Rust是如何做某些事" -"情,而不是其他的语言如何如何。 这个平衡点不容易找到,但是尽量倾向于允许 讨" -"论,因为讨论比起单方面的灌输更有利于让大家投入。" +"作为讲师,你应该尽量保持讨论的相关性,也就是保持讨论 Rust语言和其他语言的异" +"同。要找到适当的平衡点可能很难,但还是要偏向于允许讨论,因为讨论比单向交流更" +"能吸引人。" #: src/welcome-day-1.md:24 msgid "" "The questions will likely mean that we talk about things ahead of the slides." -msgstr "有些问题会导致我们提前谈到后面的内容" +msgstr "这些问题很可能意味着我们要在幻灯片之前讨论一些事情。" #: src/welcome-day-1.md:25 msgid "" @@ -2010,7 +1995,7 @@ msgid "" "Remember that the slides are just a support and you are free to skip them as " "you like." msgstr "" -"这完全没有问题! 重复是学习的一个重要方法。请记得 这些幻灯片只是一个辅助,你可" +"这完全没有问题! 重复是学习的一个重要方法。请记得这些幻灯片只是一个辅助,你可" "以选择性地跳过。" #: src/welcome-day-1.md:29 @@ -2019,8 +2004,8 @@ msgid "" "speak about the famous borrow checker. The way Rust handles memory is a " "major feature and we should show students this right away." msgstr "" -"第一天的主要目标是要谈到著名的 borrow checker,其他方面点到为止。Rust 处理内" -"存的方式是其主要特点,这点我们应该尽早展示给学生。" +"第一天的主要目标是要谈到著名的 borrow checker,其他方面 _点到为止_ 。Rust 处" +"理内存的方式是其主要特点,这点我们应该尽早展示给学生。" #: src/welcome-day-1.md:33 msgid "" @@ -2032,7 +2017,7 @@ msgstr "" #: src/welcome-day-1.md:36 msgid "Morning: 9:00 to 12:00," -msgstr "早上:9:00 到 12:00," +msgstr "早上:9:00 到 12:00," #: src/welcome-day-1.md:37 msgid "Afternoon: 13:00 to 16:00." @@ -2084,19 +2069,19 @@ msgstr "Rust 被广泛用于各种设备中:" #: src/welcome-day-1/what-is-rust.md:12 msgid "firmware and boot loaders," -msgstr "固件和引导程序," +msgstr "固件和引导程序," #: src/welcome-day-1/what-is-rust.md:13 msgid "smart displays," -msgstr "智能显示器," +msgstr "智能显示器," #: src/welcome-day-1/what-is-rust.md:14 msgid "mobile phones," -msgstr "手机," +msgstr "手机," #: src/welcome-day-1/what-is-rust.md:15 msgid "desktops," -msgstr "桌面," +msgstr "桌面," #: src/welcome-day-1/what-is-rust.md:16 msgid "servers." @@ -2115,8 +2100,9 @@ msgid "High level of control." msgstr "高度的控制能力。" #: src/welcome-day-1/what-is-rust.md:25 -msgid "Can be scaled down to very constrained devices like mobile phones." -msgstr "能够在资源极度有限的设备(如手机)上运行。" +msgid "" +"Can be scaled down to very constrained devices such as microcontrollers." +msgstr "能够在资源极度有限的设备(如微控制器)上运行。" #: src/welcome-day-1/what-is-rust.md:26 msgid "Has no runtime or garbage collection." @@ -2173,20 +2159,19 @@ msgstr "Rust 字符串是 UTF-8 编码的,可以包含任何 Unicode 字符。 #: src/hello-world.md:22 msgid "" "This slide tries to make the students comfortable with Rust code. They will " -"see a ton of it over the next four days so we start small with something " +"see a ton of it over the next three days so we start small with something " "familiar." msgstr "" -"这张幻灯片试图让学生们熟悉 Rust 代码。在接下来的四天里,他们会看到很多 Rust " +"这张幻灯片试图让学生们熟悉 Rust 代码。在接下来的三天里,他们会看到很多 Rust " "代码, 所以我们从一些熟悉的东西开始。" #: src/hello-world.md:27 msgid "" "Rust is very much like other languages in the C/C++/Java tradition. It is " -"imperative (not functional) and it doesn't try to reinvent things unless " -"absolutely necessary." +"imperative and it doesn't try to reinvent things unless absolutely necessary." msgstr "" -"Rust 非常像 C/C++/Java 等其他传统语言。它是指令式语言(而非函数式),而且除非" -"绝对必要,它不会尝试重新发明新的概念。" +"Rust 与其他 C/C++/Java 传统语言非常相似。它是命令式的,除非绝对必要,否则它不" +"会试图重新发明事物。" #: src/hello-world.md:31 msgid "Rust is modern with full support for things like Unicode." @@ -2211,9 +2196,20 @@ msgstr "" "只是[部分卫生](https://veykril.github.io/tlborm/decl-macros/minutiae/hygiene." "html)。" +#: src/hello-world.md:40 +msgid "" +"Rust is multi-paradigm. For example, it has powerful [object-oriented " +"programming features](https://doc.rust-lang.org/book/ch17-00-oop.html), and, " +"while it is not a functional language, it includes a range of [functional " +"concepts](https://doc.rust-lang.org/book/ch13-00-functional-features.html)." +msgstr "" +"Rust 是一种多范式语言。例如,它具有强大的[面向对象编程功能](https://doc.rust-" +"lang.org/book/ch17-00-oop.html),虽然不是函数式语言,但包含一系列[函数式概念]" +"((https://doc.rust-lang.org/book/ch13-00-functional-features.html))。" + #: src/hello-world/small-example.md:3 msgid "Here is a small example program in Rust:" -msgstr "以下是一个简短的 Rust 示例程序" +msgstr "以下是一个简短的 Rust 示例程序:" #: src/hello-world/small-example.md:5 msgid "" @@ -2292,6 +2288,12 @@ msgstr "" "向学生展示标准库,展示如何搜索 `std::fmt`,其中包含用于格式化字符串的微型语言" "规则。要点是让学生熟悉在标准库中搜索的过程。" +#: src/hello-world/small-example.md:44 +msgid "" +"In a shell `rustup doc std::fmt` will open a browser on the local std::fmt " +"documentation" +msgstr "在 shell 中,`rustup doc std::fmt` 将打开一个本地 std::fmt 文档浏览器" + #: src/why-rust.md:3 msgid "Some unique selling points of Rust:" msgstr "Rust 有一些独特的卖点:" @@ -2322,8 +2324,8 @@ msgid "" "constructs like pattern matching and built-in dependency management." msgstr "" "使用过 C 或 C++:Rust 利用\"借用检查\"消除了一类 _运行时错误_ 。你可以达到堪" -"比 C 和 C++ 的性能,而没有内存不安全的问题。并且你还可以得到些现代的语言构" -"造,比如模式匹配和内置依赖管理。" +"比 C 和 C++ 的性能,而没有内存不安全的问题。并且你还可以得到些现代的语言特" +"性,比如模式匹配和内置依赖管理。" #: src/why-rust.md:19 msgid "" @@ -2379,12 +2381,12 @@ msgstr "在(安全的)Rust 中也有可能产生内存泄漏。例如:" #: src/why-rust/compile-time.md:19 msgid "" -"You can for use [`Box::leak`](https://doc.rust-lang.org/std/boxed/struct.Box." +"You can use [`Box::leak`](https://doc.rust-lang.org/std/boxed/struct.Box." "html#method.leak) to leak a pointer. A use of this could be to get runtime-" "initialized and runtime-sized static variables" msgstr "" "可以使用 [`Box::leak`](https://doc.rust-lang.org/std/boxed/struct.Box." -"html#method.leak) 来泄漏一个指针。该方法可以用于得到在运行时决定大小和初始化" +"html#method.leak) 来泄漏一个指针。它的一个用途是获取运行时初始化和运行时大小" "的静态变量" #: src/why-rust/compile-time.md:21 @@ -2425,21 +2427,25 @@ msgid "Array access is bounds checked." msgstr "数组访问有边界检查。" #: src/why-rust/runtime.md:6 -msgid "Integer overflow is defined." -msgstr "整数溢出的行为有明确定义。" +msgid "Integer overflow is defined (panic or wrap-around)." +msgstr "定义了整数溢出(panic 或 wrap-around)。" #: src/why-rust/runtime.md:12 msgid "" -"Integer overflow is defined via a compile-time flag. The options are either " -"a panic (a controlled crash of the program) or wrap-around semantics. By " -"default, you get panics in debug mode (`cargo build`) and wrap-around in " -"release mode (`cargo build --release`)." +"Integer overflow is defined via the [`overflow-checks`](https://doc.rust-" +"lang.org/rustc/codegen-options/index.html#overflow-checks) compile-time " +"flag. If enabled, the program will panic (a controlled crash of the " +"program), otherwise you get wrap-around semantics. By default, you get " +"panics in debug mode (`cargo build`) and wrap-around in release mode (`cargo " +"build --release`)." msgstr "" -"整数溢出的行为由编译时的标志指定。可以选择 panic(一种受控的程序崩溃)或使" -"用“绕回(wrap-around)”语义。默认情况下,使用调试模式编译(`cargo build`)的" -"行为为 panic,使用发布模式编译(`cargo build --release`)的行为为“绕回”。" +"整数溢出是通过编译时溢出检查标志( [`overflow-checks`](https://doc.rust-lang." +"org/rustc/codegen-options/index.html#overflow-checks) )定义的。如果启用,程" +"序将出现 panic(程序受控崩溃),否则将出现 wrap-around。默认情况下,在调试模" +"式(`cargo build`)下会出现溢出,而在发布模式(`cargo build—release`)下则会" +"出现 wrap-around。" -#: src/why-rust/runtime.md:17 +#: src/why-rust/runtime.md:18 msgid "" "Bounds checking cannot be disabled with a compiler flag. It can also not be " "disabled directly with the `unsafe` keyword. However, `unsafe` allows you to " @@ -2450,8 +2456,8 @@ msgstr "" "`unsafe` 允许你调用 `slice::get_unchecked` 等不做边界检查的函数。" #: src/why-rust/modern.md:3 -msgid "Rust is built with all the experience gained in the last 40 years." -msgstr "Rust 建立于过去 40 年来所获得的经验之上。" +msgid "Rust is built with all the experience gained in the last decades." +msgstr "Rust 是在过去几十年中积累的所有经验的基础上建成的。" #: src/why-rust/modern.md:5 msgid "Language Features" @@ -2510,8 +2516,8 @@ msgid "" "known as 'sum types', which allow the type system to express things like " "`Option` and `Result`." msgstr "" -"值得一提的是,Rust 的枚举是“代数数据类型”(也叫“和类型”)。它使得类型系统可以" -"表示 `Option` 和 `Result` 等结构。" +"值得一提的是,Rust 的枚举是“代数数据(algebraic data)类型”(也叫“和(sum)类" +"型”)。它使得类型系统可以表示 `Option` 和 `Result` 等结构。" #: src/why-rust/modern.md:32 msgid "" @@ -2539,11 +2545,12 @@ msgstr "随机数生成器,可以使用 [rand](https://docs.rs/rand/) 替代 #: src/why-rust/modern.md:42 msgid "support for SSL or TLS, but see [rusttls](https://docs.rs/rustls/)." -msgstr "SSL 和 TLS 支持,可以使用 [rusttls](https://docs.rs/rustls/) 替代。" +msgstr "对 SSL 和 TLS 支持,可以使用 [rusttls](https://docs.rs/rustls/) 替代。" #: src/why-rust/modern.md:43 msgid "support for JSON, but see [serde_json](https://docs.rs/serde_json/)." -msgstr "JSON 支持,可以使用 [serde_json](https://docs.rs/serde_json/) 替代。" +msgstr "" +"对 JSON 支持,可以使用 [serde_json](https://docs.rs/serde_json/) 替代。" #: src/why-rust/modern.md:45 msgid "" @@ -2562,8 +2569,8 @@ msgid "" "makes it trivial to download and compile third-party crates. A consequence " "of this is that the standard library can be smaller." msgstr "" -"Rust 内置了一个包管理器 Cargo,使得下载和编译第三方 crate 变得简单。这也导致" -"标准库可以更加精简。" +"Rust 内置了一个包管理器 Cargo,使得下载和编译第三方 crate 变得简单。这样一" +"来,标准库的规模就会更小。" #: src/why-rust/modern.md:54 msgid "" @@ -2584,7 +2591,7 @@ msgstr "" #: src/basic-syntax.md:3 msgid "Much of the Rust syntax will be familiar to you from C, C++ or Java:" -msgstr "Rust 的许多语法与 C, C++ 和 Java 的语法相似" +msgstr "Rust 的许多语法与 C, C++ 和 Java 的语法相似:" #: src/basic-syntax.md:5 msgid "Blocks and scopes are delimited by curly braces." @@ -2622,7 +2629,7 @@ msgid "`i8`, `i16`, `i32`, `i64`, `i128`, `isize`" msgstr "`i8`, `i16`, `i32`, `i64`, `i128`, `isize`" #: src/basic-syntax/scalar-types.md:5 -msgid "`-10`, `0`, `1_000`, `123i64`" +msgid "`-10`, `0`, `1_000`, `123_i64`" msgstr "`-10`, `0`, `1_000`, `123i64`" #: src/basic-syntax/scalar-types.md:6 @@ -2634,7 +2641,7 @@ msgid "`u8`, `u16`, `u32`, `u64`, `u128`, `usize`" msgstr "`u8`, `u16`, `u32`, `u64`, `u128`, `usize`" #: src/basic-syntax/scalar-types.md:6 -msgid "`0`, `123`, `10u16`" +msgid "`0`, `123`, `10_u16`" msgstr "`0`, `123`, `10u16`" #: src/basic-syntax/scalar-types.md:7 @@ -2646,7 +2653,7 @@ msgid "`f32`, `f64`" msgstr "`f32`, `f64`" #: src/basic-syntax/scalar-types.md:7 -msgid "`3.14`, `-10.0e20`, `2f32`" +msgid "`3.14`, `-10.0e20`, `2_f32`" msgstr "`3.14`, `-10.0e20`, `2f32`" #: src/basic-syntax/scalar-types.md:8 @@ -2691,18 +2698,18 @@ msgstr "各类型占用的空间为:" #: src/basic-syntax/scalar-types.md:14 msgid "`iN`, `uN`, and `fN` are _N_ bits wide," -msgstr "`iN`, `uN` 和 `fN` 占用 _N_ 位," +msgstr "`iN`, `uN` 和 `fN` 占用 _N_ 位," #: src/basic-syntax/scalar-types.md:15 msgid "`isize` and `usize` are the width of a pointer," -msgstr "`isize` 和 `usize` 占用一个指针大小的空间," +msgstr "`isize` 和 `usize` 占用一个指针大小的空间," #: src/basic-syntax/scalar-types.md:16 -msgid "`char` is 32 bit wide," -msgstr "`char` 占用 32 位空间," +msgid "`char` is 32 bits wide," +msgstr "`char` 占用 32 位空间," #: src/basic-syntax/scalar-types.md:17 -msgid "`bool` is 8 bit wide." +msgid "`bool` is 8 bits wide." msgstr "`bool` 占用 8 位空间。" #: src/basic-syntax/scalar-types.md:21 @@ -2754,6 +2761,15 @@ msgstr "" "}\n" "```" +#: src/basic-syntax/scalar-types.md:43 +msgid "" +"All underscores in numbers can be left out, they are for legibility only. So " +"`1_000` can be written as `1000` (or `10_00`), and `123_i64` can be written " +"as `123i64`." +msgstr "" +"数字中的所有下划线都可以省略,它们只是为了便于辨认。因此,`1_000` 可以写成 " +"`1000`(或 `10_00`),`123_i64` 可以写成 `123i64`。" + #: src/basic-syntax/compound-types.md:5 msgid "Arrays" msgstr "数组(Arrays)" @@ -2814,14 +2830,14 @@ msgstr "数组:" #: src/basic-syntax/compound-types.md:34 msgid "" -"Arrays have elements of the same type, `T`, and length, `N`, which is a " -"compile-time constant. Note that the length of the array is _part of its " -"type_, which means that `[u8; 3]` and `[u8; 4]` are considered two different " -"types." +"A value of the array type `[T; N]` holds `N` (a compile-time constant) " +"elements of the same type `T`. Note that the length of the array is _part of " +"its type_, which means that `[u8; 3]` and `[u8; 4]` are considered two " +"different types." msgstr "" -"数组中的元素具有相同的类型 `T`,数组的长度为 `N`,`N` 是一个编译期常量。 需要" -"注意的是数组的长度是它_类型的一部分_, 这意味着 `[u8; 3]` 和 `[u8; 4]` 在 " -"Rust 中被认为是不同的类型。" +"数组类型 `[T; N]` 的值包含 `N` 个(编译时常数)相同类型 `T` 的元素。请注意," +"数组的长度是其类型的一部分,这意味着 `[u8; 3]` 和 `[u8; 4]` 被视为两种不同的" +"类型。" #: src/basic-syntax/compound-types.md:38 msgid "We can use literals to assign values to arrays." @@ -2834,9 +2850,9 @@ msgid "" "the debug output. We could also have used `{a}` and `{a:?}` without " "specifying the value after the format string." msgstr "" -"在主函数中,打印(print)语句使用 `?` 格式请求调试实现。 使用参数 `{}` 打印默" -"认输出,`{:?}` 表示以调试格式输出。 我们也可以不在格式化字符串后面指定变量" -"值,直接使用 `{a}` 和 `{a:?}` 进行输出。" +"在主函数中,打印(print)语句使用 `?` 请求使用调试格式的实现。 使用参数 `{}` " +"打印默认输出,`{:?}` 表示以调试格式输出。 我们也可以不在格式化字符串后面指定" +"变量值,直接使用 `{a}` 和 `{a:?}` 进行输出。" #: src/basic-syntax/compound-types.md:45 msgid "" @@ -2871,19 +2887,19 @@ msgid "" "value are expressed as `()`. It is used to indicate, for example, that a " "function or expression has no return value, as we'll see in a future slide. " msgstr "" -"空元组 `()` 也被称作 “单元(unit)类型”. 它既是一个类型, 也是这种类型的唯一" -"值——也就是说它的类型和它的 值都被表示为 `()`。它通常用于表示,比如,一个 函" -"数或表达式没有返回值,我们会在后续的幻灯片种见到这种用法。" +"空元组 `()` 也被称作 “单元(unit)类型”。 它既是一个类型,也是该类型的唯一有" +"效值——也就是说,类型及其值都用 `()` 表示。它通常用于表示,比如,一个函数或表" +"达式没有返回值,我们会在后续的幻灯片种见到这种用法。 " #: src/basic-syntax/compound-types.md:59 msgid "" "You can think of it as `void` that can be familiar to you from other " "programming languages." -msgstr "你可以将其理解为你可能在其他编程语言中比较熟悉的 `void` 类型" +msgstr "你可以将其理解为你可能在其他编程语言中比较熟悉的 `void` 类型." #: src/basic-syntax/references.md:3 msgid "Like C++, Rust has references:" -msgstr "如同 C++ 一样,Rust 也提供了引用类型。" +msgstr "如同 C++ 一样,Rust 也提供了引用类型:" #: src/basic-syntax/references.md:5 msgid "" @@ -2979,38 +2995,37 @@ msgstr "我们会在讲到所有权(ownership)时详细讨论借用(borrow #: src/basic-syntax/slices.md:3 msgid "A slice gives you a view into a larger collection:" -msgstr "切片 (slice) 的作用是提供对集合 (collection) 的视图 (view):" +msgstr "可以将切片 (slice) 看做集合类型 (collection) 的一个子集:" #: src/basic-syntax/slices.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let a: [i32; 6] = [10, 20, 30, 40, 50, 60];\n" +" let mut a: [i32; 6] = [10, 20, 30, 40, 50, 60];\n" " println!(\"a: {a:?}\");\n" "\n" " let s: &[i32] = &a[2..4];\n" +"\n" " println!(\"s: {s:?}\");\n" "}\n" "```" msgstr "" -#: src/basic-syntax/slices.md:15 +#: src/basic-syntax/slices.md:16 msgid "Slices borrow data from the sliced type." -msgstr "切片从被切片的类型中借用 (borrow) 数据。" +msgstr "切片从被切片的类型(sliced type)中借用 (borrow) 数据。" -#: src/basic-syntax/slices.md:16 -msgid "Question: What happens if you modify `a[3]`?" -msgstr "请思考:如果我们改变 `a[3]`,将会产生怎样的后果?" +#: src/basic-syntax/slices.md:17 +msgid "Question: What happens if you modify `a[3]` right before printing `s`?" +msgstr "问题:如果在打印 `s` 之前修改 `a[3]`,会发生什么情况?" -#: src/basic-syntax/slices.md:20 -#, fuzzy +#: src/basic-syntax/slices.md:21 msgid "" "We create a slice by borrowing `a` and specifying the starting and ending " "indexes in brackets." msgstr "创建切片时,我们借用了 `a` ,并在方括号中标明了起始和结尾下标。" -#: src/basic-syntax/slices.md:22 -#, fuzzy +#: src/basic-syntax/slices.md:23 msgid "" "If the slice starts at index 0, Rust’s range syntax allows us to drop the " "starting index, meaning that `&a[0..a.len()]` and `&a[..a.len()]` are " @@ -3019,47 +3034,48 @@ msgstr "" "如果切片的起始下标为 0, Rust 语法允许我们省略起始下标。比如说 `&a[0..a." "len()]` 与 `&a[..a.len()]` 是等价的。" -#: src/basic-syntax/slices.md:24 -#, fuzzy +#: src/basic-syntax/slices.md:25 msgid "" "The same is true for the last index, so `&a[2..a.len()]` and `&a[2..]` are " "identical." msgstr "" "结尾下标也可以用相同方式省略。比如说 `&a[2..a.len()]` 和 `&a[2..]` 是等价的。" -#: src/basic-syntax/slices.md:26 -#, fuzzy +#: src/basic-syntax/slices.md:27 msgid "" "To easily create a slice of the full array, we can therefore use `&a[..]`." msgstr "因此,我们可以用 `&a[..]` 来创建包含整个数组的切片。" -#: src/basic-syntax/slices.md:28 -#, fuzzy +#: src/basic-syntax/slices.md:29 msgid "" "`s` is a reference to a slice of `i32`s. Notice that the type of `s` " "(`&[i32]`) no longer mentions the array length. This allows us to perform " "computation on slices of different sizes." msgstr "" -"切片会从另外一个对象中借用数据。在这个例子中, `a` 必须在其切片存活时保持存活" -"(处于作用域中)。" +"`s` 是对 `i32`s 这个切片的引用。请注意,`s` 的类型(`&[i32]`)不再提及数组长" +"度。这样我们就可以对不同大小的切片进行计算。" -#: src/basic-syntax/slices.md:30 -#, fuzzy +#: src/basic-syntax/slices.md:31 msgid "" "Slices always borrow from another object. In this example, `a` has to remain " "'alive' (in scope) for at least as long as our slice. " msgstr "" -"关于修改 `a[3]` 的问题可能会引发精彩的讨论。正确答案是:为了保证内存安全,在" -"创建切片后,我们不能通过 `a` 来修改数据。不过我们可以通过 `a` 或者 `s` 来读取" -"数据。我们将会在“借用”章节着重介绍这个内容。" +"切片总是借用另一个对象。在本例中,`a` 必须至少和我们的片段一样保持 “活着”(在" +"作用域内)。 " -#: src/basic-syntax/slices.md:32 +#: src/basic-syntax/slices.md:33 msgid "" "The question about modifying `a[3]` can spark an interesting discussion, but " -"the answer is that for memory safety reasons you cannot do it through `a` " -"after you created a slice, but you can read the data from both `a` and `s` " -"safely. More details will be explained in the borrow checker section." +"the answer is that for memory safety reasons you cannot do it through `a` at " +"this point in the execution, but you can read the data from both `a` and `s` " +"safely. It works before you created the slice, and again after the " +"`println`, when the slice is no longer used. More details will be explained " +"in the borrow checker section." msgstr "" +"关于修改`a[3]`的问题可以引发有趣的讨论,但答案是基于内存安全的考虑,您无法通" +"过`a`在此执行点进行修改,但可以安全地从`a`和`s`中读取数据。在您创建切片之前和" +"`println`之后(切片不再使用)都可以正常工作。在借用检查器部分将会进一步解释详" +"细信息。" #: src/basic-syntax/string-slices.md:1 msgid "`String` vs `str`" @@ -3067,7 +3083,7 @@ msgstr "" #: src/basic-syntax/string-slices.md:3 msgid "We can now understand the two string types in Rust:" -msgstr "现在我们就可以理解 Rust 中的两种字符串类型:" +msgstr "现在我们可以理解一下 Rust 中的两种字符串类型:" #: src/basic-syntax/string-slices.md:5 msgid "" @@ -3269,7 +3285,7 @@ msgid "" "idiomatic to document all public items in an API using this pattern." msgstr "" "文档的内容会被当做 Markdown 处理。所有已发布 Rust 库 crate 都会自动被" -"[rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html) 工具在 " +"[rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html)工具在 " "[`docs.rs`](https://docs.rs)存档。 按照这种方式来为 API 中的所有公开项编写文" "档是 Rust 中惯用的做法。" @@ -3295,6 +3311,15 @@ msgid "" "be addressed here." msgstr "内部文档注释将在稍后(在讲解模块的页面)讨论,这里无需进行说明。" +#: src/basic-syntax/rustdoc.md:33 +msgid "" +"Rustdoc comments can contain code snippets that we can run and test using " +"`cargo test`. We will discuss these tests in the [Testing section](../" +"testing/doc-tests.html)." +msgstr "" +"Rustdoc 注释可以包含我们可以使用 `cargo test` 运行和测试的代码片段。我们将在 " +"[Testing section](../testing/doc-tests.html) 讨论这些测试。" + #: src/basic-syntax/methods.md:3 msgid "" "Methods are functions associated with a type. The `self` argument of a " @@ -3336,27 +3361,24 @@ msgid "" msgstr "我们将在今天的练习和明天的课程中更深入地学习方法相关的概念。" #: src/basic-syntax/methods.md:34 -msgid "Add a `Rectangle::new` constructor and call this from `main`:" +msgid "Add a static method called `Rectangle::new` and call this from `main`:" msgstr "新增一个 `Rectangle::new` 构造函数并在 `main` 函数中调用它:" -#: src/basic-syntax/methods.md:36 +#: src/basic-syntax/methods.md:42 msgid "" -"```rust,editable,compile_fail\n" -"fn new(width: u32, height: u32) -> Rectangle {\n" -" Rectangle { width, height }\n" -"}\n" -"```" +"While _technically_, Rust does not have custom constructors, static methods " +"are commonly used to initialize structs (but don't have to). The actual " +"constructor, `Rectangle { width, height }`, could be called directly. See " +"the [Rustnomicon](https://doc.rust-lang.org/nomicon/constructors.html)." msgstr "" -"```rust,editable,compile_fail\n" -"fn new(width: u32, height: u32) -> Rectangle {\n" -" Rectangle { width, height }\n" -"}\n" -"```" +"虽然从 _技术层面_ 讲,Rust 没有自定义构造函数,但静态方法通常用于初始化结构体" +"(但并非必须如此)。实际的构造函数 `Rectangle { width, height }` 可以直接调" +"用。参见 [Rustnomicon](https://doc.rust-lang.org/nomicon/constructors.html)。" -#: src/basic-syntax/methods.md:42 +#: src/basic-syntax/methods.md:45 msgid "" -"Add a `Rectangle::new_square(width: u32)` constructor to illustrate that " -"constructors can take arbitrary parameters." +"Add a `Rectangle::square(width: u32)` constructor to illustrate that such " +"static methods can take arbitrary parameters." msgstr "" "新增一个 `Rectangle::new_square(width: u32)` 构造函数来说明构造函数可以接受任" "意参数。" @@ -3470,15 +3492,13 @@ msgid "" msgstr "" "页面内嵌的代码片段是不可编辑的:因为离开页面后内嵌代码片段中的修改会丢失。" -#: src/exercises/day-1/morning.md:22 src/exercises/day-1/afternoon.md:11 -#: src/exercises/day-2/morning.md:11 src/exercises/day-2/afternoon.md:7 -#: src/exercises/day-3/morning.md:7 src/exercises/bare-metal/morning.md:7 -#: src/exercises/bare-metal/afternoon.md:7 +#: src/exercises/day-1/morning.md:22 src/exercises/day-2/morning.md:11 +#: src/exercises/day-3/morning.md:9 src/exercises/bare-metal/morning.md:7 #: src/exercises/concurrency/morning.md:12 -#: src/exercises/concurrency/afternoon.md:13 msgid "" -"After looking at the exercises, you can look at the \\[solutions\\] provided." -msgstr "读完习题后,可以阅读本书提供的 \\[题解\\]。" +"After looking at the exercises, you can look at the [solutions](solutions-" +"morning.md) provided." +msgstr "读完习题后,可以阅读本书提供的 [解决方案](solutions-morning.md)。" #: src/exercises/day-1/implicit-conversions.md:3 msgid "" @@ -3584,16 +3604,6 @@ msgstr "数组与 `for` 循环" msgid "We saw that an array can be declared like this:" msgstr "我们可以这样声明一个数组:" -#: src/exercises/day-1/for-loops.md:5 -msgid "" -"```rust\n" -"let array = [10, 20, 30];\n" -"```" -msgstr "" -"```rust\n" -"let array = [10, 20, 30];\n" -"```" - #: src/exercises/day-1/for-loops.md:9 msgid "" "You can print such an array by asking for its debug representation with `{:?}" @@ -3628,7 +3638,7 @@ msgid "" "fn main() {\n" " let array = [10, 20, 30];\n" " print!(\"Iterating over array:\");\n" -" for n in array {\n" +" for n in &array {\n" " print!(\" {n}\");\n" " }\n" " println!();\n" @@ -3641,22 +3651,6 @@ msgid "" "}\n" "```" msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let array = [10, 20, 30];\n" -" print!(\"Iterating over array:\");\n" -" for n in array {\n" -" print!(\" {n}\");\n" -" }\n" -" println!();\n" -"\n" -" print!(\"Iterating over range:\");\n" -" for i in 0..3 {\n" -" print!(\" {}\", array[i]);\n" -" }\n" -" println!();\n" -"}\n" -"```" #: src/exercises/day-1/for-loops.md:38 msgid "" @@ -3767,8 +3761,8 @@ msgid "" "See the [`ndarray` crate](https://docs.rs/ndarray/) for a production quality " "implementation." msgstr "" -"参考 [`ndarray` crate](https://docs.rs/ndarray/) 以了解该功能满足生产环境质量" -"的实现。" +"参考 [`ndarray` crate](https://docs.rs/ndarray/) 以了解满足生产环境质量的实" +"现。" #: src/exercises/day-1/for-loops.md:92 msgid "" @@ -3778,3482 +3772,3788 @@ msgstr "" "题目解答和附加题的答案在 [题解](solutions-morning.md#arrays-and-for-loops) 章" "节中。" -#: src/basic-syntax/variables.md:3 +#: src/exercises/day-1/for-loops.md:95 msgid "" -"Rust provides type safety via static typing. Variable bindings are immutable " -"by default:" -msgstr "Rust 通过静态类型实现了类型安全。变量绑定默认是不可变的:" +"The use of the reference `&array` within `for n in &array` is a subtle " +"preview of issues of ownership that will come later in the afternoon." +msgstr "" +"在 `for n in &array` 中使用 `&array` 是对下午将要讨论的所有权问题的微妙预告。" -#: src/basic-syntax/variables.md:6 +#: src/exercises/day-1/for-loops.md:98 +msgid "Without the `&`..." +msgstr "没有 `&` 的话…" + +#: src/exercises/day-1/for-loops.md:99 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let x: i32 = 10;\n" -" println!(\"x: {x}\");\n" -" // x = 20;\n" -" // println!(\"x: {x}\");\n" -"}\n" -"```" +"The loop would have been one that consumes the array. This is a change " +"[introduced in the 2021 Edition](https://doc.rust-lang.org/edition-guide/" +"rust-2021/IntoIterator-for-arrays.html)." +msgstr "" +"该循环本应是一个消耗数组的循环。 这是[2021 年版引入的](https://doc.rust-lang." +"org/edition-guide/rust-2021/IntoIterator-for-arrays.html)一个变化。" + +#: src/exercises/day-1/for-loops.md:102 +msgid "" +"An implicit array copy would have occurred. Since `i32` is a copy type, " +"then `[i32; 3]` is also a copy type." +msgstr "" +"隐式数组复制就会发生。 既然 `i32` 是复制类型,那么 `[i32; 3]` 也是复制类型。" + +#: src/control-flow.md:3 +msgid "" +"As we have seen, `if` is an expression in Rust. It is used to conditionally " +"evaluate one of two blocks, but the blocks can have a value which then " +"becomes the value of the `if` expression. Other control flow expressions " +"work similarly in Rust." +msgstr "" +"正如我们所知,`if` 是 Rust 中的一个表达式。它用于有条件地 评估两个块中的一" +"个,但这些块可以有一个值, 然后成为 `if` 表达式的值。其他控制流表达式在 Rust " +"中也有类似的运作方式。" + +#: src/control-flow/blocks.md:3 +msgid "" +"A block in Rust contains a sequence of expressions. Each block has a value " +"and a type, which are those of the last expression of the block:" msgstr "" +"Rust 中的代码块包含一系列表达式。每个代码块都有一个值和一个类型,它们是代码块" +"中最后一个表达式的值和类型:" + +#: src/control-flow/blocks.md:7 +msgid "" "```rust,editable\n" "fn main() {\n" -" let x: i32 = 10;\n" +" let x = {\n" +" let y = 10;\n" +" println!(\"y: {y}\");\n" +" let z = {\n" +" let w = {\n" +" 3 + 4\n" +" };\n" +" println!(\"w: {w}\");\n" +" y * w\n" +" };\n" +" println!(\"z: {z}\");\n" +" z - y\n" +" };\n" " println!(\"x: {x}\");\n" -" // x = 20;\n" -" // println!(\"x: {x}\");\n" "}\n" "```" - -#: src/basic-syntax/variables.md:17 -msgid "" -"Due to type inference the `i32` is optional. We will gradually show the " -"types less and less as the course progresses." msgstr "" -"由于类型推导,`i32` 可以省略。随着课程推进,我们会越来越少地看到类型声明。" -#: src/basic-syntax/variables.md:18 +#: src/control-flow/blocks.md:26 msgid "" -"Note that since `println!` is a macro, `x` is not moved, even using the " -"function like syntax of `println!(\"x: {}\", x)`" -msgstr "" -"需要注意的是由于 `println!` 是一个宏,尽管使用了一个 `println!(\"x: {}\", " -"x)` 这样形如函数的语法,`x` 也不会被移动。" +"If the last expression ends with `;`, then the resulting value and type is " +"`()`." +msgstr "如果最后一个表达式以 `;` 结尾,则结果值和类型为`()`。" -#: src/basic-syntax/type-inference.md:3 -msgid "Rust will look at how the variable is _used_ to determine the type:" -msgstr "Rust 会根据变量的使用来确定其类型:" +#: src/control-flow/blocks.md:28 +msgid "" +"The same rule is used for functions: the value of the function body is the " +"return value:" +msgstr "同样的规则也适用于函数:函数主体的值 是返回值:" -#: src/basic-syntax/type-inference.md:5 +#: src/control-flow/blocks.md:31 msgid "" "```rust,editable\n" -"fn takes_u32(x: u32) {\n" -" println!(\"u32: {x}\");\n" -"}\n" -"\n" -"fn takes_i8(y: i8) {\n" -" println!(\"i8: {y}\");\n" +"fn double(x: i32) -> i32 {\n" +" x + x\n" "}\n" "\n" "fn main() {\n" -" let x = 10;\n" -" let y = 20;\n" -"\n" -" takes_u32(x);\n" -" takes_i8(y);\n" -" // takes_u32(y);\n" +" println!(\"doubled: {}\", double(7));\n" "}\n" "```" msgstr "" "```rust,editable\n" -"fn takes_u32(x: u32) {\n" -" println!(\"u32: {x}\");\n" -"}\n" -"\n" -"fn takes_i8(y: i8) {\n" -" println!(\"i8: {y}\");\n" +"fn double(x: i32) -> i32 {\n" +" x + x\n" "}\n" "\n" "fn main() {\n" -" let x = 10;\n" -" let y = 20;\n" -"\n" -" takes_u32(x);\n" -" takes_i8(y);\n" -" // takes_u32(y);\n" +" println!(\"doubled: {}\", double(7));\n" "}\n" "```" -#: src/basic-syntax/type-inference.md:26 +#: src/control-flow/blocks.md:43 src/enums.md:34 src/enums/sizes.md:28 +#: src/pattern-matching.md:25 src/pattern-matching/match-guards.md:22 +#: src/structs.md:31 src/methods.md:30 src/methods/example.md:46 +msgid "Key Points:" +msgstr "关键点:" + +#: src/control-flow/blocks.md:44 msgid "" -"This slide demonstrates how the Rust compiler infers types based on " -"constraints given by variable declarations and usages." -msgstr "这张幻灯片演示了 Rust 编译器是如何根据变量声明和用法来推导其类型的。" +"The point of this slide is to show that blocks have a type and value in " +"Rust. " +msgstr "这张幻灯片的重点是说明在 Rust 中,块有类型和值。 " -#: src/basic-syntax/type-inference.md:28 +#: src/control-flow/blocks.md:45 msgid "" -"It is very important to emphasize that variables declared like this are not " -"of some sort of dynamic \"any type\" that can hold any data. The machine " -"code generated by such declaration is identical to the explicit declaration " -"of a type. The compiler does the job for us and helps us write more concise " -"code." +"You can show how the value of the block changes by changing the last line in " +"the block. For instance, adding/removing a semicolon or using a `return`." msgstr "" -"需要重点强调的是这样声明的变量并非像那种动态类型语言中可以持有任何数据的“任何" -"类型”。这种声明所生成的机器码与明确类型声明完全相同。编译器进行类型推导能够让" -"我们编写更简略的代码。" +"你可以通过更改块的最后一行,来展示块值的变化情况。例如,添加/移除分号或使用 " +"`return`。" -#: src/basic-syntax/type-inference.md:32 -#, fuzzy +#: src/control-flow/if-expressions.md:1 +msgid "`if` expressions" +msgstr "`if` 表达式" + +#: src/control-flow/if-expressions.md:3 msgid "" -"The following code tells the compiler to copy into a certain generic " -"container without the code ever explicitly specifying the contained type, " -"using `_` as a placeholder:" +"You use [`if` expressions](https://doc.rust-lang.org/reference/expressions/" +"if-expr.html#if-expressions) exactly like `if` statements in other languages:" msgstr "" -"下面的代码通过使用 `_` 占位符来告诉编译器无需明确指定其类型就可以将对应数据拷" -"贝到该容器: " +"[`if` 表达式](https://doc.rust-lang.org/reference/expressions/if-expr." +"html#if-expressions) 的用法与其他语言中的 `if` 语句完全一样:" -#: src/basic-syntax/type-inference.md:34 +#: src/control-flow/if-expressions.md:18 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut v = Vec::new();\n" -" v.push((10, false));\n" -" v.push((20, true));\n" -" println!(\"v: {v:?}\");\n" -"\n" -" let vv = v.iter().collect::>();\n" -" println!(\"vv: {vv:?}\");\n" -"}\n" -"```" +"In addition, you can use `if` as an expression. The last expression of each " +"block becomes the value of the `if` expression:" msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let mut v = Vec::new();\n" -" v.push((10, false));\n" -" v.push((20, true));\n" -" println!(\"v: {v:?}\");\n" -"\n" -" let vv = v.iter().collect::>();\n" -" println!(\"vv: {vv:?}\");\n" -"}\n" -"```" +"此外,你还可以将 `if` 用作一个表达式。每个块的最后一个表达式 将成为 `if` 表达" +"式的值:" -#: src/basic-syntax/type-inference.md:46 +#: src/control-flow/if-expressions.md:35 msgid "" -"[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator." -"html#method.collect) relies on `FromIterator`, which [`HashSet`](https://doc." -"rust-lang.org/std/iter/trait.FromIterator.html) implements." +"Because `if` is an expression and must have a particular type, both of its " +"branch blocks must have the same type. Consider showing what happens if you " +"add `;` after `x / 2` in the second example." msgstr "" -"[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator." -"html#method.collect) 依赖 [`HashSet`](https://doc.rust-lang.org/std/iter/" -"trait.FromIterator.html) 实现的 `FromIterator`。" - -#: src/basic-syntax/static-and-const.md:1 -msgid "Static and Constant Variables" -msgstr "静态 (Static) 变量和常数 (Constant) 变量" - -#: src/basic-syntax/static-and-const.md:3 -msgid "Global state is managed with static and constant variables." -msgstr "全局的状态是由静态变量和常数变量来管理的。" +"由于 `if` 是一个表达式且必须有一个特定的类型,因此它的两个分支块必须有相同的" +"类型。考虑在第二个示例中将 `;` 添加到 `x / 2` 的后面,看看会出现什么情况。" -#: src/basic-syntax/static-and-const.md:5 -msgid "`const`" -msgstr "`const`" +#: src/control-flow/for-expressions.md:1 +msgid "`for` loops" +msgstr "`for` 循环" -#: src/basic-syntax/static-and-const.md:7 -msgid "You can declare compile-time constants:" -msgstr "你可以声明编译期 (compile-time) 常量:" +#: src/control-flow/for-expressions.md:3 +msgid "" +"The [`for` loop](https://doc.rust-lang.org/std/keyword.for.html) is closely " +"related to the [`while let` loop](while-let-expressions.md). It will " +"automatically call `into_iter()` on the expression and then iterate over it:" +msgstr "" +"[for 循环](https://doc.rust-lang.org/std/keyword.for.html)与[while let 循环]" +"(while-let-expressions.md)密切相关。它会在表达式上自动调用 `into_iter()`,然" +"后遍历表达式:" -#: src/basic-syntax/static-and-const.md:9 +#: src/control-flow/for-expressions.md:7 msgid "" "```rust,editable\n" -"const DIGEST_SIZE: usize = 3;\n" -"const ZERO: Option = Some(42);\n" +"fn main() {\n" +" let v = vec![10, 20, 30];\n" "\n" -"fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {\n" -" let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];\n" -" for (idx, &b) in text.as_bytes().iter().enumerate() {\n" -" digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE]." -"wrapping_add(b);\n" +" for x in v {\n" +" println!(\"x: {x}\");\n" +" }\n" +" \n" +" for i in (0..10).step_by(2) {\n" +" println!(\"i: {i}\");\n" " }\n" -" digest\n" -"}\n" -"\n" -"fn main() {\n" -" let digest = compute_digest(\"Hello\");\n" -" println!(\"Digest: {digest:?}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"const DIGEST_SIZE: usize = 3;\n" -"const ZERO: Option = Some(42);\n" +"fn main() {\n" +" let v = vec![10, 20, 30];\n" "\n" -"fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {\n" -" let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];\n" -" for (idx, &b) in text.as_bytes().iter().enumerate() {\n" -" digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE]." -"wrapping_add(b);\n" +" for x in v {\n" +" println!(\"x: {x}\");\n" +" }\n" +" \n" +" for i in (0..10).step_by(2) {\n" +" println!(\"i: {i}\");\n" " }\n" -" digest\n" -"}\n" -"\n" -"fn main() {\n" -" let digest = compute_digest(\"Hello\");\n" -" println!(\"Digest: {digest:?}\");\n" "}\n" "```" -#: src/basic-syntax/static-and-const.md:27 +#: src/control-flow/for-expressions.md:21 +msgid "You can use `break` and `continue` here as usual." +msgstr "你可以在此照常使用 `break` 和 `continue`。" + +#: src/control-flow/for-expressions.md:25 +msgid "Index iteration is not a special syntax in Rust for just that case." +msgstr "在这种情况下,索引迭代在 Rust 中并不是一个特殊的语法。" + +#: src/control-flow/for-expressions.md:26 +msgid "`(0..10)` is a range that implements an `Iterator` trait. " +msgstr "`(0..10)` 是实现 `Iterator` trait 的范围。 " + +#: src/control-flow/for-expressions.md:27 msgid "" -"According to the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" -"vs-static.html) these are inlined upon use." +"`step_by` is a method that returns another `Iterator` that skips every other " +"element. " +msgstr "`step_by` 是返回另一个 `Iterator` 的方法,用于逐一跳过所有其他元素。 " + +#: src/control-flow/for-expressions.md:28 +msgid "" +"Modify the elements in the vector and explain the compiler errors. Change " +"vector `v` to be mutable and the for loop to `for x in v.iter_mut()`." msgstr "" -"根据 [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-vs-static." -"html) 这些变量在使用时是内联 (inlined) 的。" +"修改vector中的元素并说明编译器错误。将vector `v` 改为可变,并将 for 循环改为 " +"`for x in v.iter_mut()`。" -#: src/basic-syntax/static-and-const.md:29 -msgid "`static`" -msgstr "`static`" +#: src/control-flow/while-expressions.md:1 +msgid "`while` loops" +msgstr "`while` 循环" -#: src/basic-syntax/static-and-const.md:31 -msgid "You can also declare static variables:" -msgstr "你也可以声明静态变量:" +#: src/control-flow/while-expressions.md:3 +msgid "" +"The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-" +"expr.html#predicate-loops) works very similar to other languages:" +msgstr "" +"[`while` 关键字](https://doc.rust-lang.org/reference/expressions/loop-expr." +"html#predicate-loops) 的工作方式与其他语言非常相似:" -#: src/basic-syntax/static-and-const.md:33 +#: src/control-flow/while-expressions.md:6 msgid "" "```rust,editable\n" -"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" -"\n" "fn main() {\n" -" println!(\"{BANNER}\");\n" +" let mut x = 10;\n" +" while x != 1 {\n" +" x = if x % 2 == 0 {\n" +" x / 2\n" +" } else {\n" +" 3 * x + 1\n" +" };\n" +" }\n" +" println!(\"Final x: {x}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" -"\n" "fn main() {\n" -" println!(\"{BANNER}\");\n" +" let mut x = 10;\n" +" while x != 1 {\n" +" x = if x % 2 == 0 {\n" +" x / 2\n" +" } else {\n" +" 3 * x + 1\n" +" };\n" +" }\n" +" println!(\"Final x: {x}\");\n" "}\n" "```" -#: src/basic-syntax/static-and-const.md:41 -msgid "" -"As noted in the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" -"vs-static.html), these are not inlined upon use and have an actual " -"associated memory location. This is useful for unsafe and embedded code, " -"and the variable lives through the entirety of the program execution." -msgstr "" -"正如 [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-vs-static." -"html) 中所述,这些变量在使用时并不是内联的,而且还具有实际相关联的内存位置。" -"这对于不安全的嵌入式代码是有用的,并且这些变量存在于整个程序的执行过程之中。" - -#: src/basic-syntax/static-and-const.md:44 -msgid "" -"We will look at mutating static data in the [chapter on Unsafe Rust](../" -"unsafe.md)." -msgstr "我们会在[关于不安全的Rust的章节](../unsafe.md)中研究改变静态数据。" - -#: src/basic-syntax/static-and-const.md:48 -msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." -msgstr "值得一提的是,`const` 在语义上与C++的 `constexpr` 类似。" +#: src/control-flow/break-continue.md:1 +msgid "`break` and `continue`" +msgstr "`break` 和 `continue`" -#: src/basic-syntax/static-and-const.md:49 +#: src/control-flow/break-continue.md:3 msgid "" -"`static`, on the other hand, is much more similar to a `const` or mutable " -"global variable in C++." -msgstr "另一方面,`static` 远远更类似于C++中的 `const` 或可改变的全局变量。" +"If you want to exit a loop early, use [`break`](https://doc.rust-lang.org/" +"reference/expressions/loop-expr.html#break-expressions)," +msgstr "" +"如果你想提前退出循环,请使用 [`break`](https://doc.rust-lang.org/reference/" +"expressions/loop-expr.html#break-expressions)," -#: src/basic-syntax/static-and-const.md:50 +#: src/control-flow/break-continue.md:4 msgid "" -"It isn't super common that one would need a runtime evaluated constant, but " -"it is helpful and safer than using a static." +"If you want to immediately start the next iteration use [`continue`](https://" +"doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions)." msgstr "" -"虽然需要使用在运行中求值的常量的情况并不是很常见,但是它是有帮助的,而且比使" -"用静态变量更安全。" +"如果需要立即启动 下一次迭代,请使用 [`continue`](https://doc.rust-lang.org/" +"reference/expressions/loop-expr.html#continue-expressions)。" -#: src/basic-syntax/scopes-shadowing.md:3 +#: src/control-flow/break-continue.md:7 msgid "" -"You can shadow variables, both those from outer scopes and variables from " -"the same scope:" -msgstr "你可以隐藏变量,位于外部作用域的变量和 相同作用域的变量都可以:" +"Both `continue` and `break` can optionally take a label argument which is " +"used to break out of nested loops:" +msgstr "`continue` 和 `break` 都可以选择接受一个标签参数,用来 终止嵌套循环:" -#: src/basic-syntax/scopes-shadowing.md:6 +#: src/control-flow/break-continue.md:10 msgid "" "```rust,editable\n" "fn main() {\n" -" let a = 10;\n" -" println!(\"before: {a}\");\n" -"\n" -" {\n" -" let a = \"hello\";\n" -" println!(\"inner scope: {a}\");\n" -"\n" -" let a = true;\n" -" println!(\"shadowed in inner scope: {a}\");\n" -" }\n" -"\n" -" println!(\"after: {a}\");\n" +" let v = vec![10, 20, 30];\n" +" let mut iter = v.into_iter();\n" +" 'outer: while let Some(x) = iter.next() {\n" +" println!(\"x: {x}\");\n" +" let mut i = 0;\n" +" while i < x {\n" +" println!(\"x: {x}, i: {i}\");\n" +" i += 1;\n" +" if i == 3 {\n" +" break 'outer;\n" +" }\n" +" }\n" +" }\n" "}\n" "```" msgstr "" "```rust,editable\n" "fn main() {\n" -" let a = 10;\n" -" println!(\"before: {a}\");\n" -"\n" -" {\n" -" let a = \"hello\";\n" -" println!(\"inner scope: {a}\");\n" -"\n" -" let a = true;\n" -" println!(\"shadowed in inner scope: {a}\");\n" +" let v = vec![10, 20, 30];\n" +" let mut iter = v.into_iter();\n" +" 'outer: while let Some(x) = iter.next() {\n" +" println!(\"x: {x}\");\n" +" let mut i = 0;\n" +" while i < x {\n" +" println!(\"x: {x}, i: {i}\");\n" +" i += 1;\n" +" if i == 3 {\n" +" break 'outer;\n" +" }\n" +" }\n" " }\n" -"\n" -" println!(\"after: {a}\");\n" "}\n" "```" -#: src/basic-syntax/scopes-shadowing.md:25 +#: src/control-flow/break-continue.md:28 msgid "" -"Definition: Shadowing is different from mutation, because after shadowing " -"both variable's memory locations exist at the same time. Both are available " -"under the same name, depending where you use it in the code. " -msgstr "" -"定义: 隐藏和变更 (mutation) 不同,因为在隐藏之后,两个变量都会同时存在于内存" -"的不同位置中。在同一个名字下的两个变量都是可以被使用的,但是你在代码的哪里使" -"用会最终决定你使用哪一个变量。" - -#: src/basic-syntax/scopes-shadowing.md:26 -msgid "A shadowing variable can have a different type. " -msgstr "一个隐藏变量可以具有不同的类型。" +"In this case we break the outer loop after 3 iterations of the inner loop." +msgstr "在本示例中,我们会在内循环 3 次迭代后终止外循环。" -#: src/basic-syntax/scopes-shadowing.md:27 -msgid "" -"Shadowing looks obscure at first, but is convenient for holding on to values " -"after `.unwrap()`." -msgstr "隐藏起初看起来会有些晦涩,但是它很便于存 `.unwrap()` 之后的得到的值。" +#: src/control-flow/loop-expressions.md:1 +msgid "`loop` expressions" +msgstr "`loop` 表达式" -#: src/basic-syntax/scopes-shadowing.md:28 +#: src/control-flow/loop-expressions.md:3 msgid "" -"The following code demonstrates why the compiler can't simply reuse memory " -"locations when shadowing an immutable variable in a scope, even if the type " -"does not change." +"Finally, there is a [`loop` keyword](https://doc.rust-lang.org/reference/" +"expressions/loop-expr.html#infinite-loops) which creates an endless loop." msgstr "" -"以下代码说明了为什么在作用域内隐藏一个不可变的变量时,即使是在变量类型没有改" -"变的情况下,编译器也不能简单地重复利用之前的内存位置。" +"最后是用于创建无限循环的 [`loop` 关键字](https://doc.rust-lang.org/reference/" +"expressions/loop-expr.html#infinite-loops) 。" -#: src/basic-syntax/scopes-shadowing.md:30 +#: src/control-flow/loop-expressions.md:6 +msgid "Here you must either `break` or `return` to stop the loop:" +msgstr "在下例中,你必须 `break` 或 `return` 才能停止循环:" + +#: src/control-flow/loop-expressions.md:8 msgid "" "```rust,editable\n" "fn main() {\n" -" let a = 1;\n" -" let b = &a;\n" -" let a = a + 1;\n" -" println!(\"{a} {b}\");\n" +" let mut x = 10;\n" +" loop {\n" +" x = if x % 2 == 0 {\n" +" x / 2\n" +" } else {\n" +" 3 * x + 1\n" +" };\n" +" if x == 1 {\n" +" break;\n" +" }\n" +" }\n" +" println!(\"Final x: {x}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" "fn main() {\n" -" let a = 1;\n" -" let b = &a;\n" -" let a = a + 1;\n" -" println!(\"{a} {b}\");\n" +" let mut x = 10;\n" +" loop {\n" +" x = if x % 2 == 0 {\n" +" x / 2\n" +" } else {\n" +" 3 * x + 1\n" +" };\n" +" if x == 1 {\n" +" break;\n" +" }\n" +" }\n" +" println!(\"Final x: {x}\");\n" "}\n" "```" -#: src/memory-management.md:3 -msgid "Traditionally, languages have fallen into two broad categories:" -msgstr "传统上,语言分为两大类:" - -#: src/memory-management.md:5 -msgid "Full control via manual memory management: C, C++, Pascal, ..." -msgstr "通过手动内存管理实现完全控制:C、C++、Pascal…" - -#: src/memory-management.md:6 -msgid "" -"Full safety via automatic memory management at runtime: Java, Python, Go, " -"Haskell, ..." -msgstr "运行时通过自动内存管理实现完全安全:Java、Python、Go、Haskell…" - -#: src/memory-management.md:8 -msgid "Rust offers a new mix:" -msgstr "Rust 提供了一个全新的组合:" +#: src/control-flow/loop-expressions.md:27 +msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." +msgstr "用一个值(例如 `break 8`)来中断 `loop` 并将其输出。" -#: src/memory-management.md:10 +#: src/control-flow/loop-expressions.md:28 msgid "" -"Full control _and_ safety via compile time enforcement of correct memory " -"management." -msgstr "通过编译时强制执行正确的内存>管理来实现完全控制与安全。" - -#: src/memory-management.md:13 -msgid "It does this with an explicit ownership concept." -msgstr "它通过一个明确的所有权(ownership)概念来实现此目的。" - -#: src/memory-management.md:15 -msgid "First, let's refresh how memory management works." -msgstr "首先,我们回顾一下内存管理的工作原理。" - -#: src/memory-management/stack-vs-heap.md:1 -msgid "The Stack vs The Heap" -msgstr "栈与堆" - -#: src/memory-management/stack-vs-heap.md:3 -msgid "Stack: Continuous area of memory for local variables." -msgstr "栈:局部变量的连续内存区域。" - -#: src/memory-management/stack-vs-heap.md:4 -msgid "Values have fixed sizes known at compile time." -msgstr "值在编译时具有已知的固定大小。" - -#: src/memory-management/stack-vs-heap.md:5 -msgid "Extremely fast: just move a stack pointer." -msgstr "速度极快:只需移动一个栈指针。" - -#: src/memory-management/stack-vs-heap.md:6 -msgid "Easy to manage: follows function calls." -msgstr "易于管理:遵循函数调用规则。" - -#: src/memory-management/stack-vs-heap.md:7 -msgid "Great memory locality." -msgstr "优秀的内存局部性。" - -#: src/memory-management/stack-vs-heap.md:9 -msgid "Heap: Storage of values outside of function calls." -msgstr "堆:函数调用之外的值的存储。" - -#: src/memory-management/stack-vs-heap.md:10 -msgid "Values have dynamic sizes determined at runtime." -msgstr "值具有动态大小,具体大小需在运行时确定。" - -#: src/memory-management/stack-vs-heap.md:11 -msgid "Slightly slower than the stack: some book-keeping needed." -msgstr "比栈稍慢:需要向系统申请空间。" - -#: src/memory-management/stack-vs-heap.md:12 -msgid "No guarantee of memory locality." -msgstr "不保证内存局部性。" +"Note that `loop` is the only looping construct which returns a non-trivial " +"value. This is because it's guaranteed to be entered at least once (unlike " +"`while` and `for` loops)." +msgstr "" +"请注意,`loop` 是唯一返回有意义的值的循环结构。 这是因为它保证至少被输入一次" +"(与 `while` 和 `for` 循环不同)。" -#: src/memory-management/stack.md:3 +#: src/basic-syntax/variables.md:3 msgid "" -"Creating a `String` puts fixed-sized data on the stack and dynamically sized " -"data on the heap:" -msgstr "" -"创建 `String` 时将固定大小的数据存储在栈上, 并将动态大小的数据存储在堆上:" +"Rust provides type safety via static typing. Variable bindings are immutable " +"by default:" +msgstr "Rust 通过静态类型实现了类型安全。变量绑定默认是不可变的:" -#: src/memory-management/stack.md:6 +#: src/basic-syntax/variables.md:6 msgid "" "```rust,editable\n" "fn main() {\n" -" let s1 = String::from(\"Hello\");\n" +" let x: i32 = 10;\n" +" println!(\"x: {x}\");\n" +" // x = 20;\n" +" // println!(\"x: {x}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" "fn main() {\n" -" let s1 = String::from(\"Hello\");\n" +" let x: i32 = 10;\n" +" println!(\"x: {x}\");\n" +" // x = 20;\n" +" // println!(\"x: {x}\");\n" "}\n" "```" -#: src/memory-management/stack.md:12 -msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+----+----+ :\n" -": | ptr | o---+---+-----+-->| H | e | l | l | o | :\n" -": | len | 5 | : : +----+----+----+----+----+ :\n" -": | capacity | 5 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" -msgstr "" -"```bob\n" -" 栈 堆\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+----+----+ :\n" -": | ptr | o---+---+-----+-->| H | e | l | l | o | :\n" -": | len | 5 | : : +----+----+----+----+----+ :\n" -": | capacity | 5 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" - -#: src/memory-management/stack.md:28 -msgid "" -"Mention that a `String` is backed by a `Vec`, so it has a capacity and " -"length and can grow if mutable via reallocation on the heap." -msgstr "" -"指出 `String` 底层由 `Vec` 实现,因此它具有容量和长度,如果值可变,则可以通过" -"在堆上重新分配存储空间进行增长。" - -#: src/memory-management/stack.md:30 +#: src/basic-syntax/variables.md:17 msgid "" -"If students ask about it, you can mention that the underlying memory is heap " -"allocated using the [System Allocator](https://doc.rust-lang.org/std/alloc/" -"struct.System.html) and custom allocators can be implemented using the " -"[Allocator API](https://doc.rust-lang.org/std/alloc/index.html)" +"Due to type inference the `i32` is optional. We will gradually show the " +"types less and less as the course progresses." msgstr "" -"如果学员提出相关问题,你可以提及我们不仅能使用\\[系统分配器\\]在堆上分配底层" -"内存,还能使用 [Allocator API](https://doc.rust-lang.org/std/alloc/index." -"html) 实现自定义分配器" +"由于类型推导,`i32` 可以省略。随着课程推进,我们会越来越少地看到类型声明。" -#: src/memory-management/stack.md:32 -msgid "" -"We can inspect the memory layout with `unsafe` code. However, you should " -"point out that this is rightfully unsafe!" -msgstr "" -"我们可以使用 `unsafe` 代码检查内存布局。不过,你应该指出,这种做法不安全!" +#: src/basic-syntax/type-inference.md:3 +msgid "Rust will look at how the variable is _used_ to determine the type:" +msgstr "Rust 会根据变量的使用来确定其类型:" -#: src/memory-management/stack.md:34 +#: src/basic-syntax/type-inference.md:5 msgid "" "```rust,editable\n" +"fn takes_u32(x: u32) {\n" +" println!(\"u32: {x}\");\n" +"}\n" +"\n" +"fn takes_i8(y: i8) {\n" +" println!(\"i8: {y}\");\n" +"}\n" +"\n" "fn main() {\n" -" let mut s1 = String::from(\"Hello\");\n" -" s1.push(' ');\n" -" s1.push_str(\"world\");\n" -" // DON'T DO THIS AT HOME! For educational purposes only.\n" -" // String provides no guarantees about its layout, so this could lead " -"to\n" -" // undefined behavior.\n" -" unsafe {\n" -" let (capacity, ptr, len): (usize, usize, usize) = std::mem::" -"transmute(s1);\n" -" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n" -" }\n" +" let x = 10;\n" +" let y = 20;\n" +"\n" +" takes_u32(x);\n" +" takes_i8(y);\n" +" // takes_u32(y);\n" "}\n" "```" msgstr "" "```rust,editable\n" +"fn takes_u32(x: u32) {\n" +" println!(\"u32: {x}\");\n" +"}\n" +"\n" +"fn takes_i8(y: i8) {\n" +" println!(\"i8: {y}\");\n" +"}\n" +"\n" "fn main() {\n" -" let mut s1 = String::from(\"Hello\");\n" -" s1.push(' ');\n" -" s1.push_str(\"world\");\n" -" // DON'T DO THIS AT HOME! For educational purposes only.\n" -" // String provides no guarantees about its layout, so this could lead " -"to\n" -" // undefined behavior.\n" -" unsafe {\n" -" let (capacity, ptr, len): (usize, usize, usize) = std::mem::" -"transmute(s1);\n" -" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n" -" }\n" +" let x = 10;\n" +" let y = 20;\n" +"\n" +" takes_u32(x);\n" +" takes_i8(y);\n" +" // takes_u32(y);\n" "}\n" "```" -#: src/memory-management/manual.md:3 -msgid "You allocate and deallocate heap memory yourself." -msgstr "你自己实现堆内存分配和释放。" - -#: src/memory-management/manual.md:5 +#: src/basic-syntax/type-inference.md:26 msgid "" -"If not done with care, this can lead to crashes, bugs, security " -"vulnerabilities, and memory leaks." -msgstr "稍有不慎,这可能会导致崩溃、bug、安全漏洞和内存泄漏。" +"This slide demonstrates how the Rust compiler infers types based on " +"constraints given by variable declarations and usages." +msgstr "这张幻灯片演示了 Rust 编译器是如何根据变量声明和用法来推导其类型的。" -#: src/memory-management/manual.md:7 -msgid "C Example" -msgstr "C++ 示例" +#: src/basic-syntax/type-inference.md:28 +msgid "" +"It is very important to emphasize that variables declared like this are not " +"of some sort of dynamic \"any type\" that can hold any data. The machine " +"code generated by such declaration is identical to the explicit declaration " +"of a type. The compiler does the job for us and helps us write more concise " +"code." +msgstr "" +"需要重点强调的是这样声明的变量并非像那种动态类型语言中可以持有任何数据的“任何" +"类型”。这种声明所生成的机器码与明确类型声明完全相同。编译器进行类型推导能够让" +"我们编写更简略的代码。" -#: src/memory-management/manual.md:9 -msgid "You must call `free` on every pointer you allocate with `malloc`:" -msgstr "你必须对使用 `malloc` 分配的每个指针调用 `free`:" +#: src/basic-syntax/type-inference.md:32 +msgid "" +"The following code tells the compiler to copy into a certain generic " +"container without the code ever explicitly specifying the contained type, " +"using `_` as a placeholder:" +msgstr "" +"下面的代码通过使用 `_` 占位符来告诉编译器无需明确指定其类型就可以将对应数据拷" +"贝到该容器:" -#: src/memory-management/manual.md:11 +#: src/basic-syntax/type-inference.md:34 msgid "" -"```c\n" -"void foo(size_t n) {\n" -" int* int_array = (int*)malloc(n * sizeof(int));\n" -" //\n" -" // ... lots of code\n" -" //\n" -" free(int_array);\n" +"```rust,editable\n" +"fn main() {\n" +" let mut v = Vec::new();\n" +" v.push((10, false));\n" +" v.push((20, true));\n" +" println!(\"v: {v:?}\");\n" +"\n" +" let vv = v.iter().collect::>();\n" +" println!(\"vv: {vv:?}\");\n" "}\n" "```" msgstr "" -"```c\n" -"void foo(size_t n) {\n" -" int* int_array = (int*)malloc(n * sizeof(int));\n" -" //\n" -" // ... lots of code\n" -" //\n" -" free(int_array);\n" -"}\n" -"```" -#: src/memory-management/manual.md:21 +#: src/basic-syntax/type-inference.md:46 msgid "" -"Memory is leaked if the function returns early between `malloc` and `free`: " -"the pointer is lost and we cannot deallocate the memory." +"[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator." +"html#method.collect) relies on [`FromIterator`](https://doc.rust-lang.org/" +"std/iter/trait.FromIterator.html), which [`HashSet`](https://doc.rust-lang." +"org/std/collections/struct.HashSet.html#impl-FromIterator%3CT%3E-for-" +"HashSet%3CT,+S%3E) implements." msgstr "" -"如果函数在 `malloc` 和 `free` 之间提前返回,则会导致内存泄漏: 指针丢失,而我" -"们无法释放对应的内存。" +"[`collect`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator." +"html#method.collect) 依赖 [`HashSet`](https://doc.rust-lang.org/std/" +"collections/struct.HashSet.html#impl-FromIterator%3CT%3E-for-HashSet%3CT," +"+S%3E) 实现的 [`FromIterator`](https://doc.rust-lang.org/std/iter/trait." +"FromIterator.html)。" -#: src/memory-management/scope-based.md:3 -msgid "" -"Constructors and destructors let you hook into the lifetime of an object." -msgstr "构造函数和析构函数让你可以钩入对象的生命周期。" +#: src/basic-syntax/static-and-const.md:1 +msgid "Static and Constant Variables" +msgstr "静态 (Static) 变量和常数 (Constant) 变量" -#: src/memory-management/scope-based.md:5 +#: src/basic-syntax/static-and-const.md:3 msgid "" -"By wrapping a pointer in an object, you can free memory when the object is " -"destroyed. The compiler guarantees that this happens, even if an exception " -"is raised." +"Static and constant variables are two different ways to create globally-" +"scoped values that cannot be moved or reallocated during the execution of " +"the program. " msgstr "" -"通过将指针封装在对象中,你可以在该对象 被销毁时释放内存。编译器可保证这一点的" -"实现,即使引发了异常也不例外。" +"静态变量和常量变量是创建全局范围值的两种不同方法,这些值在程序执行过程中不能" +"移动或重新分配。 " -#: src/memory-management/scope-based.md:9 -msgid "" -"This is often called _resource acquisition is initialization_ (RAII) and " -"gives you smart pointers." -msgstr "" -"这通常称为“资源获取即初始化 (resource acquisition is initialization, " -"RAII)”, 并为你提供智能指针。" +#: src/basic-syntax/static-and-const.md:6 +msgid "`const`" +msgstr "`const`" -#: src/memory-management/scope-based.md:12 -msgid "C++ Example" -msgstr "C++ 示例" +#: src/basic-syntax/static-and-const.md:8 +msgid "" +"Constant variables are evaluated at compile time and their values are " +"inlined wherever they are used:" +msgstr "常量在编译时进行评估,其值在使用时被内联:" -#: src/memory-management/scope-based.md:14 +#: src/basic-syntax/static-and-const.md:11 msgid "" -"```c++\n" -"void say_hello(std::unique_ptr person) {\n" -" std::cout << \"Hello \" << person->name << std::endl;\n" +"```rust,editable\n" +"const DIGEST_SIZE: usize = 3;\n" +"const ZERO: Option = Some(42);\n" +"\n" +"fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {\n" +" let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];\n" +" for (idx, &b) in text.as_bytes().iter().enumerate() {\n" +" digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE]." +"wrapping_add(b);\n" +" }\n" +" digest\n" "}\n" -"```" -msgstr "" -"```c++\n" -"void say_hello(std::unique_ptr person) {\n" -" std::cout << \"Hello \" << person->name << std::endl;\n" +"\n" +"fn main() {\n" +" let digest = compute_digest(\"Hello\");\n" +" println!(\"Digest: {digest:?}\");\n" "}\n" "```" +msgstr "" -#: src/memory-management/scope-based.md:20 +#: src/basic-syntax/static-and-const.md:29 msgid "" -"The `std::unique_ptr` object is allocated on the stack, and points to memory " -"allocated on the heap." -msgstr "`std::unique_ptr` 对象在栈上分配内存,并指向在堆上分配的内存。" +"According to the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" +"vs-static.html) these are inlined upon use." +msgstr "" +"根据 [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-vs-static." +"html) 这些变量在使用时是内联 (inlined) 的。" -#: src/memory-management/scope-based.md:22 -msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." -msgstr "在 `say_hello` 结束时,`std::unique_ptr` 析构函数将运行。" +#: src/basic-syntax/static-and-const.md:31 +msgid "" +"Only functions marked `const` can be called at compile time to generate " +"`const` values. `const` functions can however be called at runtime." +msgstr "" +"只有标记为 `const` 的函数才能在编译时被调用以生成 `const` 值。而 `const` 函数" +"可以在运行时调用。" -#: src/memory-management/scope-based.md:23 -msgid "The destructor frees the `Person` object it points to." -msgstr "析构函数释放它所指向的 `Person` 对象。" +#: src/basic-syntax/static-and-const.md:33 +msgid "`static`" +msgstr "`static`" -#: src/memory-management/scope-based.md:25 +#: src/basic-syntax/static-and-const.md:35 msgid "" -"Special move constructors are used when passing ownership to a function:" -msgstr "将所有权传递给函数时,使用特殊的 move 构造函数:" +"Static variables will live during the whole execution of the program, and " +"therefore will not move:" +msgstr "静态变量在程序的整个执行过程中都会存在,因此不会移动:" -#: src/memory-management/scope-based.md:27 +#: src/basic-syntax/static-and-const.md:37 msgid "" -"```c++\n" -"std::unique_ptr person = find_person(\"Carla\");\n" -"say_hello(std::move(person));\n" +"```rust,editable\n" +"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" +"\n" +"fn main() {\n" +" println!(\"{BANNER}\");\n" +"}\n" "```" msgstr "" -"```c++\n" -"std::unique_ptr person = find_person(\"Carla\");\n" -"say_hello(std::move(person));\n" +"```rust,editable\n" +"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" +"\n" +"fn main() {\n" +" println!(\"{BANNER}\");\n" +"}\n" "```" -#: src/memory-management/garbage-collection.md:1 -msgid "Automatic Memory Management" -msgstr "自动内存管理" - -#: src/memory-management/garbage-collection.md:3 +#: src/basic-syntax/static-and-const.md:45 msgid "" -"An alternative to manual and scope-based memory management is automatic " -"memory management:" -msgstr "自动内存管理是手动和基于作用域的内存管理 的替代方案:" - -#: src/memory-management/garbage-collection.md:6 -msgid "The programmer never allocates or deallocates memory explicitly." -msgstr "程序员从不显式分配或取消分配内存。" +"As noted in the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" +"vs-static.html), these are not inlined upon use and have an actual " +"associated memory location. This is useful for unsafe and embedded code, " +"and the variable lives through the entirety of the program execution. When a " +"globally-scoped value does not have a reason to need object identity, " +"`const` is generally preferred." +msgstr "" +"正如 [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-vs-static." +"html) 中所述,这些变量在使用时并不是内联的,而且还有一个实际关联的内存位置。" +"这对于不安全的嵌入式代码是有用的,变量会在整个程序执行过程中一直存在。当一个" +"全局作用域的值不需要对象标识时,`const` 通常是首选。" -#: src/memory-management/garbage-collection.md:7 +#: src/basic-syntax/static-and-const.md:49 msgid "" -"A garbage collector finds unused memory and deallocates it for the " -"programmer." -msgstr "垃圾回收器找到未使用的内存,并为程序员将其取消分配。" +"Because `static` variables are accessible from any thread, they must be " +"`Sync`. Interior mutability is possible through a [`Mutex`](https://doc.rust-" +"lang.org/std/sync/struct.Mutex.html), atomic or similar. It is also possible " +"to have mutable statics, but they require manual synchronisation so any " +"access to them requires `unsafe` code. We will look at [mutable statics](../" +"unsafe/mutable-static-variables.md) in the chapter on Unsafe Rust." +msgstr "" +"由于任何线程都可以访问`static`变量,因此它们必须是 `Sync`的。内部可变性可以通" +"过 [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)、atomic或" +"类似方式实现。可变静态变量也是可能的,但它们需要手动同步,因此任何对它们的访" +"问都需要`unsafe`代码。我们将在不安全的 Rust一章中讨论[可变静态变量](…/unsafe/" +"mutable-static-variables.md)。" -#: src/memory-management/garbage-collection.md:9 -msgid "Java Example" -msgstr "Java 示例" +#: src/basic-syntax/static-and-const.md:57 +msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." +msgstr "值得一提的是,`const` 在语义上与C++的 `constexpr` 类似。" -#: src/memory-management/garbage-collection.md:11 -msgid "The `person` object is not deallocated after `sayHello` returns:" -msgstr "`sayHello` 返回后,`person` 对象未被取消分配:" +#: src/basic-syntax/static-and-const.md:58 +msgid "" +"`static`, on the other hand, is much more similar to a `const` or mutable " +"global variable in C++." +msgstr "另一方面,`static` 远远更类似于C++中的 `const` 或可改变的全局变量。" -#: src/memory-management/garbage-collection.md:13 +#: src/basic-syntax/static-and-const.md:59 msgid "" -"```java\n" -"void sayHello(Person person) {\n" -" System.out.println(\"Hello \" + person.getName());\n" -"}\n" -"```" +"`static` provides object identity: an address in memory and state as " +"required by types with interior mutability such as `Mutex`." msgstr "" -"```java\n" -"void sayHello(Person person) {\n" -" System.out.println(\"Hello \" + person.getName());\n" -"}\n" -"```" +"`static` 提供了对象标识:内存中的地址以及内部可变类型(如 `Mutex`)所需的" +"状态。" -#: src/memory-management/rust.md:1 -msgid "Memory Management in Rust" -msgstr "Rust 中的内存管理" - -#: src/memory-management/rust.md:3 -msgid "Memory management in Rust is a mix:" -msgstr "Rust 中的内存管理是一种混合模式:" - -#: src/memory-management/rust.md:5 -msgid "Safe and correct like Java, but without a garbage collector." -msgstr "像 Java 一样安全又正确,但没有垃圾回收器。" - -#: src/memory-management/rust.md:6 +#: src/basic-syntax/static-and-const.md:60 msgid "" -"Depending on which abstraction (or combination of abstractions) you choose, " -"can be a single unique pointer, reference counted, or atomically reference " -"counted." +"It isn't super common that one would need a runtime evaluated constant, but " +"it is helpful and safer than using a static." msgstr "" -"根据你选择的抽象(或抽象组合),可以是单个唯一指针,也可以是引用计数,或原子" -"引用计数。" +"虽然需要使用在运行中求值的常量的情况并不是很常见,但是它是有帮助的,而且比使" +"用静态变量更安全。" -#: src/memory-management/rust.md:7 -msgid "Scope-based like C++, but the compiler enforces full adherence." -msgstr "像 C++ 一样基于作用域,但编译器会强制完全遵循规则。" +#: src/basic-syntax/static-and-const.md:61 +msgid "`thread_local` data can be created with the macro `std::thread_local`." +msgstr "可使用宏 `std::thread_local` 创建 `thread_local` 数据。" -#: src/memory-management/rust.md:8 -msgid "" -"A Rust user can choose the right abstraction for the situation, some even " -"have no cost at runtime like C." -msgstr "" -"Rust 用户可以根据具体情况选择合适的抽象,有些甚至没有像 C 那样的运行时开销。" +#: src/basic-syntax/static-and-const.md:63 +msgid "Properties table:" +msgstr "特性列表:" -#: src/memory-management/rust.md:10 -msgid "It achieves this by modeling _ownership_ explicitly." -msgstr "它通过对“所有权”进行显式建模来实现这一点。" +#: src/basic-syntax/static-and-const.md:65 +msgid "Property" +msgstr "特性" -#: src/memory-management/rust.md:14 -msgid "" -"If asked how at this point, you can mention that in Rust this is usually " -"handled by RAII wrapper types such as [Box](https://doc.rust-lang.org/std/" -"boxed/struct.Box.html), [Vec](https://doc.rust-lang.org/std/vec/struct.Vec." -"html), [Rc](https://doc.rust-lang.org/std/rc/struct.Rc.html), or [Arc]" -"(https://doc.rust-lang.org/std/sync/struct.Arc.html). These encapsulate " -"ownership and memory allocation via various means, and prevent the potential " -"errors in C." +#: src/basic-syntax/static-and-const.md:65 +msgid "Static" msgstr "" -"如果此时被问及如何操作,你可以提及在 Rust 中,这通常由 RAII 封装容器类型(例" -"如 [Box](https://doc.rust-lang.org/std/boxed/struct.Box.html)、[Vec](https://" -"doc.rust-lang.org/std/vec/struct.Vec.html)、[Rc](https://doc.rust-lang.org/" -"std/rc/struct.Rc.html) 或 [Arc](https://doc.rust-lang.org/std/sync/struct." -"Arc.html))处理。这些类型通过各种方式封装了所有权和内存分配,并防止了 C 中潜" -"在错误的发生。" -#: src/memory-management/rust.md:16 -msgid "" -"You may be asked about destructors here, the [Drop](https://doc.rust-lang." -"org/std/ops/trait.Drop.html) trait is the Rust equivalent." +#: src/basic-syntax/static-and-const.md:65 +msgid "Constant" msgstr "" -"你可能会被问及析构函数,此处 [Drop](https://doc.rust-lang.org/std/ops/trait." -"Drop.html) trait 是 Rust 等效项。" - -#: src/memory-management/comparison.md:3 -msgid "Here is a rough comparison of the memory management techniques." -msgstr "下面是对内存管理技术的粗略比较。" - -#: src/memory-management/comparison.md:5 -msgid "Pros of Different Memory Management Techniques" -msgstr "不同内存管理技术的优点" - -#: src/memory-management/comparison.md:7 src/memory-management/comparison.md:22 -msgid "Manual like C:" -msgstr "手动(如 C):" - -#: src/memory-management/comparison.md:8 src/memory-management/comparison.md:14 -#: src/memory-management/comparison.md:17 -msgid "No runtime overhead." -msgstr "无运行时开销。" - -#: src/memory-management/comparison.md:9 src/memory-management/comparison.md:26 -msgid "Automatic like Java:" -msgstr "自动(如 Java):" - -#: src/memory-management/comparison.md:10 -msgid "Fully automatic." -msgstr "完全自动。" -#: src/memory-management/comparison.md:11 -#: src/memory-management/comparison.md:18 -msgid "Safe and correct." -msgstr "安全且正确。" +#: src/basic-syntax/static-and-const.md:67 +msgid "Has an address in memory" +msgstr "在内存中是否有地址" -#: src/memory-management/comparison.md:12 -#: src/memory-management/comparison.md:29 -msgid "Scope-based like C++:" -msgstr "基于作用域(如 C++):" - -#: src/memory-management/comparison.md:13 -msgid "Partially automatic." -msgstr "部分自动。" - -#: src/memory-management/comparison.md:15 -msgid "Compiler-enforced scope-based like Rust:" -msgstr "由编译器强制执行、基于作用域(如 Rust):" - -#: src/memory-management/comparison.md:16 -msgid "Enforced by compiler." -msgstr "由编译器强制执行。" - -#: src/memory-management/comparison.md:20 -msgid "Cons of Different Memory Management Techniques" -msgstr "不同内存管理技术的缺点" - -#: src/memory-management/comparison.md:23 -msgid "Use-after-free." -msgstr "释放后使用。" - -#: src/memory-management/comparison.md:24 -msgid "Double-frees." -msgstr "双重释放。" +#: src/basic-syntax/static-and-const.md:67 +#: src/basic-syntax/static-and-const.md:68 +#: src/basic-syntax/static-and-const.md:70 +#: src/basic-syntax/static-and-const.md:71 +msgid "Yes" +msgstr "" -#: src/memory-management/comparison.md:25 -msgid "Memory leaks." -msgstr "内存泄漏。" +#: src/basic-syntax/static-and-const.md:67 +msgid "No (inlined)" +msgstr "" -#: src/memory-management/comparison.md:27 -msgid "Garbage collection pauses." -msgstr "垃圾回收暂停。" +#: src/basic-syntax/static-and-const.md:68 +msgid "Lives for the entire duration of the program" +msgstr "在整个程序中一直存活" -#: src/memory-management/comparison.md:28 -msgid "Destructor delays." -msgstr "析构函数延迟。" +#: src/basic-syntax/static-and-const.md:68 +#: src/basic-syntax/static-and-const.md:69 +#: src/basic-syntax/static-and-const.md:71 +msgid "No" +msgstr "" -#: src/memory-management/comparison.md:30 -msgid "Complex, opt-in by programmer." -msgstr "复杂,由程序员自选。" +#: src/basic-syntax/static-and-const.md:69 +msgid "Can be mutable" +msgstr "是否可变" -#: src/memory-management/comparison.md:31 -msgid "Potential for use-after-free." -msgstr "有望释放后使用。" +#: src/basic-syntax/static-and-const.md:69 +msgid "Yes (unsafe)" +msgstr "" -#: src/memory-management/comparison.md:32 -msgid "Compiler-enforced and scope-based like Rust:" -msgstr "由编译器强制执行、基于作用域(如 Rust):" +#: src/basic-syntax/static-and-const.md:70 +msgid "Evaluated at compile time" +msgstr "在编译时被评估" -#: src/memory-management/comparison.md:33 -msgid "Some upfront complexity." -msgstr "前期有些复杂。" +#: src/basic-syntax/static-and-const.md:70 +msgid "Yes (initialised at compile time)" +msgstr "" -#: src/memory-management/comparison.md:34 -msgid "Can reject valid programs." -msgstr "可能拒绝有效的程序。" +#: src/basic-syntax/static-and-const.md:71 +msgid "Inlined wherever it is used" +msgstr "无论在何处使用都是内联的" -#: src/ownership.md:3 +#: src/basic-syntax/scopes-shadowing.md:3 msgid "" -"All variable bindings have a _scope_ where they are valid and it is an error " -"to use a variable outside its scope:" -msgstr "所有变量绑定都有一个有效的“作用域”,使用 超出其作用域的变量是错误的:" +"You can shadow variables, both those from outer scopes and variables from " +"the same scope:" +msgstr "你可以隐藏变量,位于外部作用域的变量和相同作用域的变量都可以:" -#: src/ownership.md:6 +#: src/basic-syntax/scopes-shadowing.md:6 msgid "" -"```rust,editable,compile_fail\n" -"struct Point(i32, i32);\n" -"\n" +"```rust,editable\n" "fn main() {\n" -" {\n" -" let p = Point(3, 4);\n" -" println!(\"x: {}\", p.0);\n" -" }\n" -" println!(\"y: {}\", p.1);\n" -"}\n" -"```" -msgstr "" -"```rust,editable,compile_fail\n" -"struct Point(i32, i32);\n" +" let a = 10;\n" +" println!(\"before: {a}\");\n" "\n" -"fn main() {\n" " {\n" -" let p = Point(3, 4);\n" -" println!(\"x: {}\", p.0);\n" +" let a = \"hello\";\n" +" println!(\"inner scope: {a}\");\n" +"\n" +" let a = true;\n" +" println!(\"shadowed in inner scope: {a}\");\n" " }\n" -" println!(\"y: {}\", p.1);\n" +"\n" +" println!(\"after: {a}\");\n" "}\n" "```" +msgstr "" -#: src/ownership.md:18 +#: src/basic-syntax/scopes-shadowing.md:25 msgid "" -"At the end of the scope, the variable is _dropped_ and the data is freed." -msgstr "作用域结束时,变量会“被丢弃”,数据会被释放。" +"Definition: Shadowing is different from mutation, because after shadowing " +"both variable's memory locations exist at the same time. Both are available " +"under the same name, depending where you use it in the code. " +msgstr "" +"定义: 隐藏和变更 (mutation) 不同,因为在隐藏之后,两个变量都会同时存在于内存" +"的不同位置中。在同一个名字下的两个变量都是可以被使用的,但是你在代码的哪里使" +"用会最终决定你使用哪一个变量。 " -#: src/ownership.md:19 -msgid "A destructor can run here to free up resources." -msgstr "析构函数可在此运行以释放资源。" +#: src/basic-syntax/scopes-shadowing.md:26 +msgid "A shadowing variable can have a different type. " +msgstr "一个隐藏变量可以具有不同的类型。 " -#: src/ownership.md:20 -msgid "We say that the variable _owns_ the value." -msgstr "指出变量“拥有”值。" +#: src/basic-syntax/scopes-shadowing.md:27 +msgid "" +"Shadowing looks obscure at first, but is convenient for holding on to values " +"after `.unwrap()`." +msgstr "隐藏起初看起来会有些晦涩,但是它很便于存 `.unwrap()` 之后的得到的值。" -#: src/ownership/move-semantics.md:3 -msgid "An assignment will transfer ownership between variables:" -msgstr "赋值操作将在变量之间转移所有权:" +#: src/basic-syntax/scopes-shadowing.md:28 +msgid "" +"The following code demonstrates why the compiler can't simply reuse memory " +"locations when shadowing an immutable variable in a scope, even if the type " +"does not change." +msgstr "" +"以下代码说明了为什么在作用域内隐藏一个不可变的变量时,即使是在变量类型没有改" +"变的情况下,编译器也不能简单地重复利用之前的内存位置。" -#: src/ownership/move-semantics.md:5 +#: src/basic-syntax/scopes-shadowing.md:30 msgid "" "```rust,editable\n" "fn main() {\n" -" let s1: String = String::from(\"Hello!\");\n" -" let s2: String = s1;\n" -" println!(\"s2: {s2}\");\n" -" // println!(\"s1: {s1}\");\n" +" let a = 1;\n" +" let b = &a;\n" +" let a = a + 1;\n" +" println!(\"{a} {b}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" "fn main() {\n" -" let s1: String = String::from(\"Hello!\");\n" -" let s2: String = s1;\n" -" println!(\"s2: {s2}\");\n" -" // println!(\"s1: {s1}\");\n" +" let a = 1;\n" +" let b = &a;\n" +" let a = a + 1;\n" +" println!(\"{a} {b}\");\n" "}\n" "```" -#: src/ownership/move-semantics.md:14 -msgid "The assignment of `s1` to `s2` transfers ownership." -msgstr "将 `s1` 赋值给 `s2`,即转移了所有权。" - -#: src/ownership/move-semantics.md:15 -msgid "The data was _moved_ from `s1` and `s1` is no longer accessible." -msgstr "数据从 `s1`“移出”,且 `s1` 不再可供访问。" - -#: src/ownership/move-semantics.md:16 -msgid "When `s1` goes out of scope, nothing happens: it has no ownership." -msgstr "当 `s1` 离开作用域时,什么都不会发生:它没有所有权。" - -#: src/ownership/move-semantics.md:17 -msgid "When `s2` goes out of scope, the string data is freed." -msgstr "当 `s2` 离开作用域时,字符串数据被释放。" - -#: src/ownership/move-semantics.md:18 -msgid "There is always _exactly_ one variable binding which owns a value." -msgstr "变量绑定在任一时刻有且“只有”一个值。" - -#: src/ownership/move-semantics.md:22 +#: src/enums.md:3 msgid "" -"Mention that this is the opposite of the defaults in C++, which copies by " -"value unless you use `std::move` (and the move constructor is defined!)." -msgstr "" -"指出这与 C++ 中的默认值相反。除非你使用 `std::move`(并已定义 move 构造函" -"数!),否则 C++ 中的默认值是按值复制的。" - -#: src/ownership/move-semantics.md:24 -msgid "In Rust, clones are explicit (by using `clone`)." -msgstr "在 Rust 中,克隆是显式的(通过使用 `clone`)。" +"The `enum` keyword allows the creation of a type which has a few different " +"variants:" +msgstr "`enum` 关键字允许创建具有几个 不同变体的类型:" -#: src/ownership/moved-strings-rust.md:3 +#: src/enums.md:6 msgid "" "```rust,editable\n" -"fn main() {\n" -" let s1: String = String::from(\"Rust\");\n" -" let s2: String = s1;\n" +"fn generate_random_number() -> i32 {\n" +" // Implementation based on https://xkcd.com/221/\n" +" 4 // Chosen by fair dice roll. Guaranteed to be random.\n" "}\n" -"```" -msgstr "" -"```rust,editable\n" +"\n" +"#[derive(Debug)]\n" +"enum CoinFlip {\n" +" Heads,\n" +" Tails,\n" +"}\n" +"\n" +"fn flip_coin() -> CoinFlip {\n" +" let random_number = generate_random_number();\n" +" if random_number % 2 == 0 {\n" +" return CoinFlip::Heads;\n" +" } else {\n" +" return CoinFlip::Tails;\n" +" }\n" +"}\n" +"\n" "fn main() {\n" -" let s1: String = String::from(\"Rust\");\n" -" let s2: String = s1;\n" +" println!(\"You got: {:?}\", flip_coin());\n" "}\n" "```" +msgstr "" -#: src/ownership/moved-strings-rust.md:10 -msgid "The heap data from `s1` is reused for `s2`." -msgstr "`s1` 中的堆数据会被 `s2` 重复使用。" +#: src/enums.md:36 +msgid "Enumerations allow you to collect a set of values under one type" +msgstr "枚举允许你从一种类型下收集一组值" -#: src/ownership/moved-strings-rust.md:11 -msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." -msgstr "当 `s1` 离开作用域时,什么都不会发生(它已被移出)。" +#: src/enums.md:37 +msgid "" +"This page offers an enum type `CoinFlip` with two variants `Heads` and " +"`Tails`. You might note the namespace when using variants." +msgstr "" +"本页提供了一个枚举类型 `CoinFlip`,其中包含 `Heads` 和`Tail`两个变体。在使用" +"变体时,使用变体时请注意命名空间。" -#: src/ownership/moved-strings-rust.md:13 -msgid "Before move to `s2`:" -msgstr "移动到 `s2` 中之前:" +#: src/enums.md:38 +msgid "This might be a good time to compare Structs and Enums:" +msgstr "这可能是比较结构体和枚举的好时机:" -#: src/ownership/moved-strings-rust.md:15 +#: src/enums.md:39 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+----+ :\n" -": | ptr | o---+---+-----+-->| R | u | s | t | :\n" -": | len | 4 | : : +----+----+----+----+ :\n" -": | capacity | 4 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - - - -'\n" -": :\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"In both, you can have a simple version without fields (unit struct) or one " +"with different types of fields (variant payloads). " msgstr "" -"```bob\n" -" 栈 堆\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+----+ :\n" -": | ptr | o---+---+-----+-->| R | u | s | t | :\n" -": | len | 4 | : : +----+----+----+----+ :\n" -": | capacity | 4 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - - - -'\n" -": :\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"在这两种结构中,您可以使用不带字段的简单版本(单位结构体),也可以使用带不同" +"类型字段的版本(变体载荷)。 " -#: src/ownership/moved-strings-rust.md:30 -msgid "After move to `s2`:" -msgstr "移动到 `s2` 中之后:" +#: src/enums.md:40 +msgid "In both, associated functions are defined within an `impl` block." +msgstr "在这两者中,关联的函数都在 `impl` 块中定义。" -#: src/ownership/moved-strings-rust.md:32 +#: src/enums.md:41 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 \"(inaccessible)\" : : :\n" -": +-----------+-------+ : : +----+----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| R | u | s | t | :\n" -": | len | 4 | : | : +----+----+----+----+ :\n" -": | capacity | 4 | : | : :\n" -": +-----------+-------+ : | : :\n" -": : | `- - - - - - - - - - - - - -'\n" -": s2 : |\n" -": +-----------+-------+ : |\n" -": | ptr | o---+---+--'\n" -": | len | 4 | :\n" -": | capacity | 4 | :\n" -": +-----------+-------+ :\n" -": :\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"You could even implement the different variants of an enum with separate " +"structs but then they wouldn’t be the same type as they would if they were " +"all defined in an enum. " msgstr "" -"```bob\n" -" 栈 堆\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" -": : : :\n" -": s1 \"(无法访问)\" : : :\n" -": +-----------+-------+ : : +----+----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| R | u | s | t | :\n" -": | len | 4 | : | : +----+----+----+----+ :\n" -": | capacity | 4 | : | : :\n" -": +-----------+-------+ : | : :\n" -": : | `- - - - - - - - - - - - - -'\n" -": s2 : |\n" -": +-----------+-------+ : |\n" -": | ptr | o---+---+--'\n" -": | len | 4 | :\n" -": | capacity | 4 | :\n" -": +-----------+-------+ :\n" -": :\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"您甚至可以用单独的结构体来实现枚举的不同变体,但这样一来,它们的类型就不一样" +"了,因为它们都是在枚举中定义的。 " -#: src/ownership/double-free-modern-cpp.md:3 -msgid "Modern C++ solves this differently:" -msgstr "现代 C++ 以不同的方式解决此问题:" +#: src/enums/variant-payloads.md:3 +msgid "" +"You can define richer enums where the variants carry data. You can then use " +"the `match` statement to extract the data from each variant:" +msgstr "" +"你可以定义更丰富的枚举,其中变体会携带数据。然后,你可以使用 `match` 语句从每" +"个变体中提取数据:" -#: src/ownership/double-free-modern-cpp.md:5 +#: src/enums/variant-payloads.md:6 msgid "" -"```c++\n" -"std::string s1 = \"Cpp\";\n" -"std::string s2 = s1; // Duplicate the data in s1.\n" +"```rust,editable\n" +"enum WebEvent {\n" +" PageLoad, // Variant without payload\n" +" KeyPress(char), // Tuple struct variant\n" +" Click { x: i64, y: i64 }, // Full struct variant\n" +"}\n" +"\n" +"#[rustfmt::skip]\n" +"fn inspect(event: WebEvent) {\n" +" match event {\n" +" WebEvent::PageLoad => println!(\"page loaded\"),\n" +" WebEvent::KeyPress(c) => println!(\"pressed '{c}'\"),\n" +" WebEvent::Click { x, y } => println!(\"clicked at x={x}, y={y}\"),\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let load = WebEvent::PageLoad;\n" +" let press = WebEvent::KeyPress('x');\n" +" let click = WebEvent::Click { x: 20, y: 80 };\n" +"\n" +" inspect(load);\n" +" inspect(press);\n" +" inspect(click);\n" +"}\n" "```" msgstr "" -"```c++\n" -"std::string s1 = \"Cpp\";\n" -"std::string s2 = s1; // 复制 s1 中的数据。\n" -"```" -#: src/ownership/double-free-modern-cpp.md:10 +#: src/enums/variant-payloads.md:35 msgid "" -"The heap data from `s1` is duplicated and `s2` gets its own independent copy." -msgstr "`s1` 中的堆数据被复制,`s2` 获得自己的独立副本。" +"The values in the enum variants can only be accessed after being pattern " +"matched. The pattern binds references to the fields in the \"match arm\" " +"after the `=>`." +msgstr "" +"枚举变体中的值只有在被模式匹配后,才可访问。模式将引用绑定到 `=>` 之后" +"的“match 分支”中的字段。" -#: src/ownership/double-free-modern-cpp.md:11 -msgid "When `s1` and `s2` go out of scope, they each free their own memory." -msgstr "当 `s1` 和 `s2` 离开作用域时,它们会各自释放自己的内存。" +#: src/enums/variant-payloads.md:36 +msgid "" +"The expression is matched against the patterns from top to bottom. There is " +"no fall-through like in C or C++." +msgstr "表达式会从上到下与模式匹配。没有像 C 或 C++ 中那样的跳转。" -#: src/ownership/double-free-modern-cpp.md:13 -msgid "Before copy-assignment:" -msgstr "复制-赋值之前:" +#: src/enums/variant-payloads.md:37 +msgid "" +"The match expression has a value. The value is the last expression in the " +"match arm which was executed." +msgstr "匹配表达式拥有一个值。值是 match 分支中被执行的最后一个表达式。" -#: src/ownership/double-free-modern-cpp.md:16 +#: src/enums/variant-payloads.md:38 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"Starting from the top we look for what pattern matches the value then run " +"the code following the arrow. Once we find a match, we stop. " msgstr "" -"```bob\n" -" 栈 堆\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"从顶部开始,查找与该值匹配的模式,然后沿箭头运行代码。一旦找到匹配,我们便会" +"停止。 " -#: src/ownership/double-free-modern-cpp.md:30 -msgid "After copy-assignment:" -msgstr "复制-赋值之后:" +#: src/enums/variant-payloads.md:39 +msgid "" +"Demonstrate what happens when the search is inexhaustive. Note the advantage " +"the Rust compiler provides by confirming when all cases are handled. " +msgstr "" +"展示搜索不详尽时会发生的情况。请注意 Rust 编译器的优势,即确认所有情况何时都" +"得到了处理。 " + +#: src/enums/variant-payloads.md:40 +msgid "`match` inspects a hidden discriminant field in the `enum`." +msgstr "`match` 会检查 `enum` 中的隐藏的判别字段。" -#: src/ownership/double-free-modern-cpp.md:32 +#: src/enums/variant-payloads.md:41 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : : :\n" -": s2 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+-----+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"It is possible to retrieve the discriminant by calling `std::mem::" +"discriminant()`" +msgstr "可以通过调用 `std::mem::discriminant()` 来检索判别" + +#: src/enums/variant-payloads.md:42 +msgid "" +"This is useful, for example, if implementing `PartialEq` for structs where " +"comparing field values doesn't affect equality." +msgstr "这很有用,例如如果为结构体实现 `PartialEq`,比较字段值不会影响等式。" + +#: src/enums/variant-payloads.md:43 +msgid "" +"`WebEvent::Click { ... }` is not exactly the same as `WebEvent::" +"Click(Click)` with a top level `struct Click { ... }`. The inlined version " +"cannot implement traits, for example." msgstr "" -"```bob\n" -" 栈 堆\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" -": : : :\n" -": s1 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+--+--+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : : :\n" -": s2 : : :\n" -": +-----------+-------+ : : +----+----+----+ :\n" -": | ptr | o---+---+-----+-->| C | p | p | :\n" -": | len | 3 | : : +----+----+----+ :\n" -": | capacity | 3 | : : :\n" -": +-----------+-------+ : : :\n" -": : `- - - - - - - - - - - -'\n" -"`- - - - - - - - - - - - - -'\n" -"```" +"`WebEvent::Click { ... }` 与含顶层 `struct Click { ... }` 的 `WebEvent::" +"Click(Click)` 不完全相同。例如,内嵌版本无法实现 trait。" -#: src/ownership/moves-function-calls.md:3 +#: src/enums/sizes.md:3 msgid "" -"When you pass a value to a function, the value is assigned to the function " -"parameter. This transfers ownership:" -msgstr "你将值传递给函数时,该值会被赋给函数 参数。这就转移了所有权:" +"Rust enums are packed tightly, taking constraints due to alignment into " +"account:" +msgstr "Rust 枚举的包装非常紧凑,考虑到了对齐的限制:" -#: src/ownership/moves-function-calls.md:6 +#: src/enums/sizes.md:5 msgid "" "```rust,editable\n" -"fn say_hello(name: String) {\n" -" println!(\"Hello {name}\")\n" +"use std::any::type_name;\n" +"use std::mem::{align_of, size_of};\n" +"\n" +"fn dbg_size() {\n" +" println!(\"{}: size {} bytes, align: {} bytes\",\n" +" type_name::(), size_of::(), align_of::());\n" +"}\n" +"\n" +"enum Foo {\n" +" A,\n" +" B,\n" "}\n" "\n" "fn main() {\n" -" let name = String::from(\"Alice\");\n" -" say_hello(name);\n" -" // say_hello(name);\n" +" dbg_size::();\n" "}\n" "```" msgstr "" + +#: src/enums/sizes.md:24 +msgid "" +"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." +"html)." +msgstr "" +"请参阅 [Rust 引用](https://doc.rust-lang.org/reference/type-layout.html)。" + +#: src/enums/sizes.md:30 +msgid "" +"Internally Rust is using a field (discriminant) to keep track of the enum " +"variant." +msgstr "Rust 在内部使用一个字段(判别式)来跟踪枚举变量。" + +#: src/enums/sizes.md:32 +msgid "" +"You can control the discriminant if needed (e.g., for compatibility with C):" +msgstr "如有需要,您可以控制判别式(例如,为了与 C 语言兼容):" + +#: src/enums/sizes.md:34 +msgid "" "```rust,editable\n" -"fn say_hello(name: String) {\n" -" println!(\"Hello {name}\")\n" +"#[repr(u32)]\n" +"enum Bar {\n" +" A, // 0\n" +" B = 10000,\n" +" C, // 10001\n" "}\n" "\n" "fn main() {\n" -" let name = String::from(\"Alice\");\n" -" say_hello(name);\n" -" // say_hello(name);\n" +" println!(\"A: {}\", Bar::A as u32);\n" +" println!(\"B: {}\", Bar::B as u32);\n" +" println!(\"C: {}\", Bar::C as u32);\n" "}\n" "```" +msgstr "" -#: src/ownership/moves-function-calls.md:20 +#: src/enums/sizes.md:49 msgid "" -"With the first call to `say_hello`, `main` gives up ownership of `name`. " -"Afterwards, `name` cannot be used anymore within `main`." +"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " +"bytes." msgstr "" -"首次调用 `say_hello` 时,`main` 便放弃了 `name` 的所有权。此后,`main` 中不能" -"再使用 `name`。" +"如果不使用 `repr`,判别类型会占用 2 个字节,因为 10001 是一个 2 个字节的数" +"值。" -#: src/ownership/moves-function-calls.md:21 -msgid "" -"The heap memory allocated for `name` will be freed at the end of the " -"`say_hello` function." -msgstr "在 `say_hello` 函数结束时,系统会释放为 `name` 分配的堆内存。" +#: src/enums/sizes.md:53 +msgid "Try out other types such as" +msgstr "试试其他类型,例如" -#: src/ownership/moves-function-calls.md:22 -msgid "" -"`main` can retain ownership if it passes `name` as a reference (`&name`) and " -"if `say_hello` accepts a reference as a parameter." +#: src/enums/sizes.md:55 +msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," msgstr "" -"如果 `main` 将 `name` 作为引用 (`&name`) 传递过去,且 `say_hello` 接受作为参" -"数的引用,则可保留所有权。" -#: src/ownership/moves-function-calls.md:23 +#: src/enums/sizes.md:56 msgid "" -"Alternatively, `main` can pass a clone of `name` in the first call (`name." -"clone()`)." -msgstr "此外,`main` 也可以在首次调用时传递 `name` 的克隆 (`name.clone()`)。" +"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " +"see below)," +msgstr "" -#: src/ownership/moves-function-calls.md:24 -msgid "" -"Rust makes it harder than C++ to inadvertently create copies by making move " -"semantics the default, and by forcing programmers to make clones explicit." +#: src/enums/sizes.md:57 +msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," msgstr "" -"相较于 C++,Rust 通过将移动语义设为默认值,并强制程序员进行显式克隆,更难以无" -"意中创建副本。" -#: src/ownership/copy-clone.md:3 +#: src/enums/sizes.md:58 msgid "" -"While move semantics are the default, certain types are copied by default:" -msgstr "虽然移动语义是默认的,但默认情况下会复制某些类型:" +"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " +"optimization, see below)." +msgstr "" -#: src/ownership/copy-clone.md:5 +#: src/enums/sizes.md:60 +msgid "" +"Niche optimization: Rust will merge unused bit patterns for the enum " +"discriminant." +msgstr "小众优化:Rust 会合并枚举判别式中未使用的位模式。" + +#: src/enums/sizes.md:63 +msgid "" +"Null pointer optimization: For [some types](https://doc.rust-lang.org/std/" +"option/#representation), Rust guarantees that `size_of::()` equals " +"`size_of::>()`." +msgstr "" +"空指针优化: 对于 [某些类型](https://doc.rust-lang.org/std/option/" +"#representation),Rust 保证 `size_of::()` 等于 `size_of::>()` 。" + +#: src/enums/sizes.md:67 +msgid "" +"Example code if you want to show how the bitwise representation _may_ look " +"like in practice. It's important to note that the compiler provides no " +"guarantees regarding this representation, therefore this is totally unsafe." +msgstr "" +"示例代码(如果您想展示按位表示法在实际应用中的样子)。需要注意的是,编译器对" +"这种表示法不提供任何保证,因此这是完全不安全的。" + +#: src/enums/sizes.md:70 msgid "" "```rust,editable\n" +"use std::mem::transmute;\n" +"\n" +"macro_rules! dbg_bits {\n" +" ($e:expr, $bit_type:ty) => {\n" +" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " +"$bit_type>($e));\n" +" };\n" +"}\n" +"\n" "fn main() {\n" -" let x = 42;\n" -" let y = x;\n" -" println!(\"x: {x}\");\n" -" println!(\"y: {y}\");\n" +" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" +" // representation of types.\n" +" unsafe {\n" +" println!(\"Bitwise representation of bool\");\n" +" dbg_bits!(false, u8);\n" +" dbg_bits!(true, u8);\n" +"\n" +" println!(\"Bitwise representation of Option\");\n" +" dbg_bits!(None::, u8);\n" +" dbg_bits!(Some(false), u8);\n" +" dbg_bits!(Some(true), u8);\n" +"\n" +" println!(\"Bitwise representation of Option>\");\n" +" dbg_bits!(Some(Some(false)), u8);\n" +" dbg_bits!(Some(Some(true)), u8);\n" +" dbg_bits!(Some(None::), u8);\n" +" dbg_bits!(None::>, u8);\n" +"\n" +" println!(\"Bitwise representation of Option<&i32>\");\n" +" dbg_bits!(None::<&i32>, usize);\n" +" dbg_bits!(Some(&0i32), usize);\n" +" }\n" "}\n" "```" msgstr "" + +#: src/enums/sizes.md:105 +msgid "" +"More complex example if you want to discuss what happens when we chain more " +"than 256 `Option`s together." +msgstr "" +"如果你想讨论将超过 256 个 `Option` 串联在一起会发生什么,可以举一个更复杂的例" +"子。" + +#: src/enums/sizes.md:107 +msgid "" "```rust,editable\n" +"#![recursion_limit = \"1000\"]\n" +"\n" +"use std::mem::transmute;\n" +"\n" +"macro_rules! dbg_bits {\n" +" ($e:expr, $bit_type:ty) => {\n" +" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " +"$bit_type>($e));\n" +" };\n" +"}\n" +"\n" +"// Macro to wrap a value in 2^n Some() where n is the number of \"@\" " +"signs.\n" +"// Increasing the recursion limit is required to evaluate this macro.\n" +"macro_rules! many_options {\n" +" ($value:expr) => { Some($value) };\n" +" ($value:expr, @) => {\n" +" Some(Some($value))\n" +" };\n" +" ($value:expr, @ $($more:tt)+) => {\n" +" many_options!(many_options!($value, $($more)+), $($more)+)\n" +" };\n" +"}\n" +"\n" "fn main() {\n" -" let x = 42;\n" -" let y = x;\n" -" println!(\"x: {x}\");\n" -" println!(\"y: {y}\");\n" +" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" +" // representation of types.\n" +" unsafe {\n" +" assert_eq!(many_options!(false), Some(false));\n" +" assert_eq!(many_options!(false, @), Some(Some(false)));\n" +" assert_eq!(many_options!(false, @@), " +"Some(Some(Some(Some(false)))));\n" +"\n" +" println!(\"Bitwise representation of a chain of 128 Option's.\");\n" +" dbg_bits!(many_options!(false, @@@@@@@), u8);\n" +" dbg_bits!(many_options!(true, @@@@@@@), u8);\n" +"\n" +" println!(\"Bitwise representation of a chain of 256 Option's.\");\n" +" dbg_bits!(many_options!(false, @@@@@@@@), u16);\n" +" dbg_bits!(many_options!(true, @@@@@@@@), u16);\n" +"\n" +" println!(\"Bitwise representation of a chain of 257 Option's.\");\n" +" dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);\n" +" dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);\n" +" dbg_bits!(many_options!(None::, @@@@@@@@), u16);\n" +" }\n" "}\n" "```" +msgstr "" -#: src/ownership/copy-clone.md:14 -msgid "These types implement the `Copy` trait." -msgstr "这些类型实现了 `Copy` trait。" +#: src/control-flow/novel.md:3 +msgid "" +"Rust has a few control flow constructs which differ from other languages. " +"They are used for pattern matching:" +msgstr "Rust 有一些与其他语言不同的控制流结构。它们用于模式匹配:" -#: src/ownership/copy-clone.md:16 -msgid "You can opt-in your own types to use copy semantics:" -msgstr "你可以选择自己的类型来使用复制语义:" +#: src/control-flow/novel.md:6 src/control-flow/if-let-expressions.md:1 +msgid "`if let` expressions" +msgstr "`if let` 表达式" -#: src/ownership/copy-clone.md:18 +#: src/control-flow/novel.md:7 +msgid "`while let` expressions" +msgstr "`while let` 表达式" + +#: src/control-flow/novel.md:8 src/control-flow/match-expressions.md:1 +msgid "`match` expressions" +msgstr "`match` 表达式" + +#: src/control-flow/if-let-expressions.md:3 +msgid "" +"The [`if let` expression](https://doc.rust-lang.org/reference/expressions/if-" +"expr.html#if-let-expressions) lets you execute different code depending on " +"whether a value matches a pattern:" +msgstr "" +"[`if let` 表达式](https://doc.rust-lang.org/reference/expressions/if-expr." +"html#if-let-expressions) 能让你根据某个值是否与模式相匹配来执行不同的代码:" + +#: src/control-flow/if-let-expressions.md:7 msgid "" "```rust,editable\n" -"#[derive(Copy, Clone, Debug)]\n" -"struct Point(i32, i32);\n" -"\n" "fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = p1;\n" -" println!(\"p1: {p1:?}\");\n" -" println!(\"p2: {p2:?}\");\n" +" let arg = std::env::args().next();\n" +" if let Some(value) = arg {\n" +" println!(\"Program name: {value}\");\n" +" } else {\n" +" println!(\"Missing name?\");\n" +" }\n" "}\n" "```" msgstr "" "```rust,editable\n" -"#[derive(Copy, Clone, Debug)]\n" -"struct Point(i32, i32);\n" -"\n" "fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = p1;\n" -" println!(\"p1: {p1:?}\");\n" -" println!(\"p2: {p2:?}\");\n" +" let arg = std::env::args().next();\n" +" if let Some(value) = arg {\n" +" println!(\"Program name: {value}\");\n" +" } else {\n" +" println!(\"Missing name?\");\n" +" }\n" "}\n" "```" -#: src/ownership/copy-clone.md:30 -msgid "After the assignment, both `p1` and `p2` own their own data." -msgstr "赋值之后,`p1` 和 `p2` 都拥有自己的数据。" - -#: src/ownership/copy-clone.md:31 -msgid "We can also use `p1.clone()` to explicitly copy the data." -msgstr "我们还可以使用 `p1.clone()` 显式复制数据。" - -#: src/ownership/copy-clone.md:35 -msgid "Copying and cloning are not the same thing:" -msgstr "复制和克隆是两码事:" - -#: src/ownership/copy-clone.md:37 -msgid "" -"Copying refers to bitwise copies of memory regions and does not work on " -"arbitrary objects." -msgstr "复制是指内存区域的按位复制,不适用于任意对象。" - -#: src/ownership/copy-clone.md:38 +#: src/control-flow/if-let-expressions.md:18 +#: src/control-flow/while-let-expressions.md:21 +#: src/control-flow/match-expressions.md:23 msgid "" -"Copying does not allow for custom logic (unlike copy constructors in C++)." -msgstr "复制不允许自定义逻辑(不同于 C++ 中的复制构造函数)。" +"See [pattern matching](../pattern-matching.md) for more details on patterns " +"in Rust." +msgstr "" +"如需详细了解 Rust 中 的模式,请参阅[模式匹配](../pattern-matching.md)。" -#: src/ownership/copy-clone.md:39 +#: src/control-flow/if-let-expressions.md:23 msgid "" -"Cloning is a more general operation and also allows for custom behavior by " -"implementing the `Clone` trait." -msgstr "克隆是一种更通用的操作,也允许通过实现 `Clone` trait 来自定义行为。" +"Unlike `match`, `if let` does not have to cover all branches. This can make " +"it more concise than `match`." +msgstr "与 `match` 不同,`if let` 不必覆盖所有分支。这使它比 `match` 更简洁。" -#: src/ownership/copy-clone.md:40 -msgid "Copying does not work on types that implement the `Drop` trait." -msgstr "复制不适用于实现 `Drop` trait 的类型。" +#: src/control-flow/if-let-expressions.md:24 +msgid "A common usage is handling `Some` values when working with `Option`." +msgstr "使用 `Option` 时,常见的做法是处理 `Some` 值。" -#: src/ownership/copy-clone.md:42 src/ownership/lifetimes-function-calls.md:29 -msgid "In the above example, try the following:" -msgstr "在上述示例中,请尝试以下操作:" +#: src/control-flow/if-let-expressions.md:25 +msgid "" +"Unlike `match`, `if let` does not support guard clauses for pattern matching." +msgstr "与 `match` 不同的是,`if let` 不支持模式匹配的 guard 子句。" -#: src/ownership/copy-clone.md:44 +#: src/control-flow/if-let-expressions.md:26 msgid "" -"Add a `String` field to `struct Point`. It will not compile because `String` " -"is not a `Copy` type." +"Since 1.65, a similar [let-else](https://doc.rust-lang.org/rust-by-example/" +"flow_control/let_else.html) construct allows to do a destructuring " +"assignment, or if it fails, execute a block which is required to abort " +"normal control flow (with `panic`/`return`/`break`/`continue`):" msgstr "" -"在 `struct Point` 中添加 `String` 字段。由于 `String` 不属于 `Copy` 类型,因" -"此无法编译。" +"自 1.65 版起,[let-else](https://doc.rust-lang.org/rust-by-example/" +"flow_control/let_else.html) 结构允许进行去结构化赋值,如果赋值失败,则执行一" +"个需要中止正常控制流的代码块(使用 `panic`/`return`/`break`/`continue` ):" -#: src/ownership/copy-clone.md:45 +#: src/control-flow/if-let-expressions.md:28 msgid "" -"Remove `Copy` from the `derive` attribute. The compiler error is now in the " -"`println!` for `p1`." +"```rust,editable\n" +"fn main() {\n" +" println!(\"{:?}\", second_word_to_upper(\"foo bar\"));\n" +"}\n" +" \n" +"fn second_word_to_upper(s: &str) -> Option {\n" +" let mut it = s.split(' ');\n" +" let (Some(_), Some(item)) = (it.next(), it.next()) else {\n" +" return None;\n" +" };\n" +" Some(item.to_uppercase())\n" +"}\n" +"\n" +"```" msgstr "" -"从 `derive` 属性中移除 `Copy`。现在,编译器错误位于 `p1`的 `println!` 中。" -#: src/ownership/copy-clone.md:46 -msgid "Show that it works if you clone `p1` instead." -msgstr "指出如果你改为克隆 `p1`,则可按预期运行。" +#: src/control-flow/while-let-expressions.md:1 +msgid "`while let` loops" +msgstr "`while let` 循环" -#: src/ownership/copy-clone.md:48 +#: src/control-flow/while-let-expressions.md:3 msgid "" -"If students ask about `derive`, it is sufficient to say that this is a way " -"to generate code in Rust at compile time. In this case the default " -"implementations of `Copy` and `Clone` traits are generated." +"Like with `if let`, there is a [`while let`](https://doc.rust-lang.org/" +"reference/expressions/loop-expr.html#predicate-pattern-loops) variant which " +"repeatedly tests a value against a pattern:" msgstr "" -"如果学员问起 `derive`,只需说这是一种 在编译时生成 Rust 代码的方法。在这种情" -"况下,系统会生成 `Copy` 和 `Clone` trait 的默认实现。" - -#: src/ownership/borrowing.md:3 -msgid "" -"Instead of transferring ownership when calling a function, you can let a " -"function _borrow_ the value:" -msgstr "调用函数时,你可以让 函数“借用”值,而不是转移所有权:" +"与 `if let` 一样,[`while let`](https://doc.rust-lang.org/reference/" +"expressions/loop-expr.html#predicate-pattern-loops) 变体会针对一个模式重复测" +"试一个值:" -#: src/ownership/borrowing.md:6 +#: src/control-flow/while-let-expressions.md:6 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn add(p1: &Point, p2: &Point) -> Point {\n" -" Point(p1.0 + p2.0, p1.1 + p2.1)\n" -"}\n" -"\n" "fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = Point(10, 20);\n" -" let p3 = add(&p1, &p2);\n" -" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" -"}\n" -"```" -msgstr "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn add(p1: &Point, p2: &Point) -> Point {\n" -" Point(p1.0 + p2.0, p1.1 + p2.1)\n" -"}\n" +" let v = vec![10, 20, 30];\n" +" let mut iter = v.into_iter();\n" "\n" -"fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = Point(10, 20);\n" -" let p3 = add(&p1, &p2);\n" -" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" +" while let Some(x) = iter.next() {\n" +" println!(\"x: {x}\");\n" +" }\n" "}\n" "```" +msgstr "" -#: src/ownership/borrowing.md:22 -msgid "The `add` function _borrows_ two points and returns a new point." -msgstr "`add` 函数“借用”两个点并返回一个新点。" +#: src/control-flow/while-let-expressions.md:17 +msgid "" +"Here the iterator returned by `v.into_iter()` will return a `Option` on " +"every call to `next()`. It returns `Some(x)` until it is done, after which " +"it will return `None`. The `while let` lets us keep iterating through all " +"items." +msgstr "" +"在这里,`v.into_iter()` 返回的迭代器将在每次调用 `next()` 时返回一个 " +"`Option`。它会返回 `Some(x)` 直到完成,完成后会返回 `None`。`while let` " +"可以让我们继续遍历所有项目。" -#: src/ownership/borrowing.md:23 -msgid "The caller retains ownership of the inputs." -msgstr "调用方会保留输入的所有权。" +#: src/control-flow/while-let-expressions.md:26 +msgid "" +"Point out that the `while let` loop will keep going as long as the value " +"matches the pattern." +msgstr "需要指出的是,只要值与模式匹配,`while let` 循环就会一直进行下去。" -#: src/ownership/borrowing.md:27 -msgid "Notes on stack returns:" -msgstr "关于栈返回的说明:" +#: src/control-flow/while-let-expressions.md:27 +msgid "" +"You could rewrite the `while let` loop as an infinite loop with an if " +"statement that breaks when there is no value to unwrap for `iter.next()`. " +"The `while let` provides syntactic sugar for the above scenario." +msgstr "" +"你可以使用 if 语句将 `while let` 循环重写为无限循环,当 `iter.next()` 没有值" +"可以解封时中断。`while let` 为上述情况提供了语法糖。" -#: src/ownership/borrowing.md:28 +#: src/control-flow/match-expressions.md:3 msgid "" -"Demonstrate that the return from `add` is cheap because the compiler can " -"eliminate the copy operation. Change the above code to print stack addresses " -"and run it on the [Playground](https://play.rust-lang.org/). In the " -"\"DEBUG\" optimization level, the addresses should change, while they stay " -"the same when changing to the \"RELEASE\" setting:" +"The [`match` keyword](https://doc.rust-lang.org/reference/expressions/match-" +"expr.html) is used to match a value against one or more patterns. In that " +"sense, it works like a series of `if let` expressions:" msgstr "" -"证明从 `add` 返回的开销很低,因为编译器可以消除复制操作。更改上述代码以输出栈" -"地址,并在 [Playground](https://play.rust-lang.org/) 上运行它。在“调试”优化级" -"别中,地址应发生变化,而在改成“发布”设置时保持不变:" +"[`match` 关键字](https://doc.rust-lang.org/reference/expressions/match-expr." +"html) 用于将一个值与一个或多个模式进行匹配。从这个意义上讲,它的工作方式 类似" +"于一系列的 `if let` 表达式:" -#: src/ownership/borrowing.md:30 +#: src/control-flow/match-expressions.md:7 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn add(p1: &Point, p2: &Point) -> Point {\n" -" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n" -" println!(\"&p.0: {:p}\", &p.0);\n" -" p\n" -"}\n" -"\n" "fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = Point(10, 20);\n" -" let p3 = add(&p1, &p2);\n" -" println!(\"&p3.0: {:p}\", &p3.0);\n" -" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" +" match std::env::args().next().as_deref() {\n" +" Some(\"cat\") => println!(\"Will do cat things\"),\n" +" Some(\"ls\") => println!(\"Will ls some files\"),\n" +" Some(\"mv\") => println!(\"Let's move some files\"),\n" +" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" +" None => println!(\"Hmm, no program name?\"),\n" +" _ => println!(\"Unknown program name!\"),\n" +" }\n" "}\n" "```" msgstr "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn add(p1: &Point, p2: &Point) -> Point {\n" -" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n" -" println!(\"&p.0: {:p}\", &p.0);\n" -" p\n" -"}\n" -"\n" "fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = Point(10, 20);\n" -" let p3 = add(&p1, &p2);\n" -" println!(\"&p3.0: {:p}\", &p3.0);\n" -" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" +" match std::env::args().next().as_deref() {\n" +" Some(\"cat\") => println!(\"Will do cat things\"),\n" +" Some(\"ls\") => println!(\"Will ls some files\"),\n" +" Some(\"mv\") => println!(\"Let's move some files\"),\n" +" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" +" None => println!(\"Hmm, no program name?\"),\n" +" _ => println!(\"Unknown program name!\"),\n" +" }\n" "}\n" "```" -#: src/ownership/borrowing.md:48 -msgid "The Rust compiler can do return value optimization (RVO)." -msgstr "Rust 编译器能够执行返回值优化 (RVO)。" +#: src/control-flow/match-expressions.md:20 +msgid "" +"Like `if let`, each match arm must have the same type. The type is the last " +"expression of the block, if any. In the example above, the type is `()`." +msgstr "" +"与 `if let` 类似,每个匹配分支必须有相同的类型。该类型是块的最后一个 表达式" +"(如有)。在上例中,类型是 `()`。" -#: src/ownership/borrowing.md:49 +#: src/control-flow/match-expressions.md:28 +msgid "Save the match expression to a variable and print it out." +msgstr "将 match 表达式保存到一个变量中并输出结果。" + +#: src/control-flow/match-expressions.md:29 +msgid "Remove `.as_deref()` and explain the error." +msgstr "移除 `.as_deref()` 并说明错误。" + +#: src/control-flow/match-expressions.md:30 msgid "" -"In C++, copy elision has to be defined in the language specification because " -"constructors can have side effects. In Rust, this is not an issue at all. If " -"RVO did not happen, Rust will always performs a simple and efficient " -"`memcpy` copy." +"`std::env::args().next()` returns an `Option`, but we cannot match " +"against `String`." msgstr "" -"在 C++ 中,必须在语言规范中定义复制省略,因为构造函数可能会有附带效应。在 " -"Rust 中,这完全不是问题。如果 RVO 未发生,Rust 将始终执行简单且高效的 " -"`memcpy` 复制。" +"`std::env::args().next()` 会返回 `Option`,但无法与 `String` 进行匹" +"配。" -#: src/ownership/shared-unique-borrows.md:3 -msgid "Rust puts constraints on the ways you can borrow values:" -msgstr "Rust 限制了借用值的方式:" +#: src/control-flow/match-expressions.md:31 +msgid "" +"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " +"this turns `Option` into `Option<&str>`." +msgstr "" +"`as_deref()` 会将 `Option` 转换为 `Option<&T::Target>`。在我们的示例中,这" +"会将 `Option` 转换为 `Option<&str>`。" -#: src/ownership/shared-unique-borrows.md:5 -msgid "You can have one or more `&T` values at any given time, _or_" -msgstr "在任何给定时间,你都可以有一个或多个 `&T` 值,或者" +#: src/control-flow/match-expressions.md:32 +msgid "" +"We can now use pattern matching to match against the `&str` inside `Option`." +msgstr "现在,我们可以使用模式匹配来匹配 `Option` 中的 `&str`。" -#: src/ownership/shared-unique-borrows.md:6 -msgid "You can have exactly one `&mut T` value." -msgstr "你可以有且只有一个 `&mut T` 值。" +#: src/pattern-matching.md:3 +msgid "" +"The `match` keyword let you match a value against one or more _patterns_. " +"The comparisons are done from top to bottom and the first match wins." +msgstr "" +"使用关键词 `match` 对一个值进行模式匹配。进行匹配时,会从上至下依次进行比较," +"并选定第一个匹配成功的结果。" -#: src/ownership/shared-unique-borrows.md:8 +#: src/pattern-matching.md:6 +msgid "The patterns can be simple values, similarly to `switch` in C and C++:" +msgstr "模式 (pattern) 可以是简单的值,其用法类似于 C 与 C++ 中的 `switch` :" + +#: src/pattern-matching.md:8 msgid "" -"```rust,editable,compile_fail\n" +"```rust,editable\n" "fn main() {\n" -" let mut a: i32 = 10;\n" -" let b: &i32 = &a;\n" +" let input = 'x';\n" "\n" -" {\n" -" let c: &mut i32 = &mut a;\n" -" *c = 20;\n" +" match input {\n" +" 'q' => println!(\"Quitting\"),\n" +" 'a' | 's' | 'w' | 'd' => println!(\"Moving around\"),\n" +" '0'..='9' => println!(\"Number input\"),\n" +" _ => println!(\"Something else\"),\n" " }\n" -"\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\n" "}\n" "```" msgstr "" -"```rust,editable,compile_fail\n" -"fn main() {\n" -" let mut a: i32 = 10;\n" -" let b: &i32 = &a;\n" -"\n" -" {\n" -" let c: &mut i32 = &mut a;\n" -" *c = 20;\n" -" }\n" -"\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\n" -"}\n" -"```" -#: src/ownership/shared-unique-borrows.md:25 -msgid "" -"The above code does not compile because `a` is borrowed as mutable (through " -"`c`) and as immutable (through `b`) at the same time." -msgstr "" -"上述代码无法编译,因为 `a` 同时作为可变值(通过 `c`)和不可变值(通过 `b`)被" -"借用。" +#: src/pattern-matching.md:21 +msgid "The `_` pattern is a wildcard pattern which matches any value." +msgstr "模式 `_` 是外卡 (wildcard) 模式。它可以匹配任何值。" -#: src/ownership/shared-unique-borrows.md:26 +#: src/pattern-matching.md:26 msgid "" -"Move the `println!` statement for `b` before the scope that introduces `c` " -"to make the code compile." -msgstr "" -"将`b` 的 `println!` 语句移到引入 `c` 的作用域之前,这段代码就可以编译。" +"You might point out how some specific characters are being used when in a " +"pattern" +msgstr "您可以指出某些特定字符在模式中是如何使用的" -#: src/ownership/shared-unique-borrows.md:27 -msgid "" -"After that change, the compiler realizes that `b` is only ever used before " -"the new mutable borrow of `a` through `c`. This is a feature of the borrow " -"checker called \"non-lexical lifetimes\"." -msgstr "" -"这样更改后,编译器会发现 `b` 只在通过 `c` 对 `a` 进行新可变借用之前使用过。这" -"是借用检查器的一个功能,名为“非词法作用域生命周期”。" +#: src/pattern-matching.md:27 +msgid "`|` as an `or`" +msgstr "`|` 可以看成 `or`" -#: src/ownership/lifetimes.md:3 -msgid "A borrowed value has a _lifetime_:" -msgstr "借用的值是有“生命周期”的:" +#: src/pattern-matching.md:28 +msgid "`..` can expand as much as it needs to be" +msgstr "`..` 可以根据需要进行扩展" -#: src/ownership/lifetimes.md:5 -msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." -msgstr "生命周期可以是隐式的:add(p1: &Point, p2: &Point) -> Point\\`。" +#: src/pattern-matching.md:29 +msgid "`1..=5` represents an inclusive range" +msgstr "`1..=5` 表示包含范围" -#: src/ownership/lifetimes.md:6 -msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." -msgstr "生命周期也可以是显式的:`&'a Point`、`&'document str`。" +#: src/pattern-matching.md:30 +msgid "`_` is a wild card" +msgstr "`_` is 代表任意值" -#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:23 +#: src/pattern-matching.md:31 msgid "" -"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " -"lifetime `a`\"." -msgstr "将 `&'a Point` 读取为“借用的 `Point,至少 在 `a\\` 生命周期内有效。" - -#: src/ownership/lifetimes.md:9 -msgid "" -"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " -"yourself." -msgstr "生命周期始终由编译器推断出来:你不能自行 分配生命周期。" +"It can be useful to show how binding works, by for instance replacing a " +"wildcard character with a variable, or removing the quotes around `q`." +msgstr "" +"例如,将通配符替换为变量,或去掉 `q` 的引号,就能很好地展示绑定是如何工作的。" -#: src/ownership/lifetimes.md:11 -msgid "" -"Lifetime annotations create constraints; the compiler verifies that there is " -"a valid solution." -msgstr "生命周期注释会创建约束条件;编译器会验证 是否存在有效的解决方案。" +#: src/pattern-matching.md:32 +msgid "You can demonstrate matching on a reference." +msgstr "您可以在引用上演示匹配。" -#: src/ownership/lifetimes.md:13 +#: src/pattern-matching.md:33 msgid "" -"Lifetimes for function arguments and return values must be fully specified, " -"but Rust allows these to be elidied in most cases with [a few simple rules]" -"(https://doc.rust-lang.org/nomicon/lifetime-elision.html)." +"This might be a good time to bring up the concept of irrefutable patterns, " +"as the term can show up in error messages." msgstr "" +"现在也许是提出不可辩驳模式 (irrefutable patterns)概念的好时机,因为该术语可" +"能会出现在错误信息中。" -#: src/ownership/lifetimes-function-calls.md:3 +#: src/pattern-matching/destructuring-enums.md:3 msgid "" -"In addition to borrowing its arguments, a function can return a borrowed " -"value:" -msgstr "除了借用其参数之外,函数还可以返回借用的值:" +"Patterns can also be used to bind variables to parts of your values. This is " +"how you inspect the structure of your types. Let us start with a simple " +"`enum` type:" +msgstr "" +"模式还可用于将变量与部分值绑定。这就是检查类型结构的方法。让我们从一个简单的 " +"`enum` 类型开始:" -#: src/ownership/lifetimes-function-calls.md:5 +#: src/pattern-matching/destructuring-enums.md:6 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" -" if p1.0 < p2.0 { p1 } else { p2 }\n" -"}\n" -"\n" -"fn main() {\n" -" let p1: Point = Point(10, 10);\n" -" let p2: Point = Point(20, 20);\n" -" let p3: &Point = left_most(&p1, &p2);\n" -" println!(\"left-most point: {:?}\", p3);\n" +"enum Result {\n" +" Ok(i32),\n" +" Err(String),\n" "}\n" -"```" -msgstr "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" "\n" -"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" -" if p1.0 < p2.0 { p1 } else { p2 }\n" +"fn divide_in_two(n: i32) -> Result {\n" +" if n % 2 == 0 {\n" +" Result::Ok(n / 2)\n" +" } else {\n" +" Result::Err(format!(\"cannot divide {n} into two equal parts\"))\n" +" }\n" "}\n" "\n" "fn main() {\n" -" let p1: Point = Point(10, 10);\n" -" let p2: Point = Point(20, 20);\n" -" let p3: &Point = left_most(&p1, &p2);\n" -" println!(\"left-most point: {:?}\", p3);\n" +" let n = 100;\n" +" match divide_in_two(n) {\n" +" Result::Ok(half) => println!(\"{n} divided in two is {half}\"),\n" +" Result::Err(msg) => println!(\"sorry, an error happened: {msg}\"),\n" +" }\n" "}\n" "```" +msgstr "" -#: src/ownership/lifetimes-function-calls.md:21 -msgid "`'a` is a generic parameter, it is inferred by the compiler." -msgstr "`'a` 是一个泛型形参,由编译器推断出来。" - -#: src/ownership/lifetimes-function-calls.md:22 -msgid "Lifetimes start with `'` and `'a` is a typical default name." -msgstr "以 `'` 和 `'a` 开头的生命周期是典型的默认名称。" +#: src/pattern-matching/destructuring-enums.md:29 +msgid "" +"Here we have used the arms to _destructure_ the `Result` value. In the first " +"arm, `half` is bound to the value inside the `Ok` variant. In the second " +"arm, `msg` is bound to the error message." +msgstr "" +"在这里,我们使用 “arms“ 来 _重组_ `Result` 值。在第一个 arm 中,`half` 被绑" +"定到 `Ok` 变量中的值。在第二个 arm 中,`msg` 与错误信息绑定。" -#: src/ownership/lifetimes-function-calls.md:25 +#: src/pattern-matching/destructuring-enums.md:36 msgid "" -"The _at least_ part is important when parameters are in different scopes." -msgstr "当参数在不同的作用域时,“至少”部分至关重要。" +"The `if`/`else` expression is returning an enum that is later unpacked with " +"a `match`." +msgstr "`if`/`else` 表达式将返回一个枚举,随后用 `match` 解包。" -#: src/ownership/lifetimes-function-calls.md:31 +#: src/pattern-matching/destructuring-enums.md:37 msgid "" -"Move the declaration of `p2` and `p3` into a a new scope (`{ ... }`), " -"resulting in the following code:" +"You can try adding a third variant to the enum definition and displaying the " +"errors when running the code. Point out the places where your code is now " +"inexhaustive and how the compiler tries to give you hints." msgstr "" +"您可以尝试在枚举定义中添加第三个变量,并在运行代码时显示错误。指出您的代码在" +"哪些地方不详尽,以及编译器是如何给您提示的。" -#: src/ownership/lifetimes-function-calls.md:32 +#: src/pattern-matching/destructuring-structs.md:3 +msgid "You can also destructure `structs`:" +msgstr "您还可以解构 `structs` 结构:" + +#: src/pattern-matching/destructuring-structs.md:5 msgid "" -"```rust,ignore\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" -" if p1.0 < p2.0 { p1 } else { p2 }\n" +"```rust,editable\n" +"struct Foo {\n" +" x: (u32, u32),\n" +" y: u32,\n" "}\n" "\n" +"#[rustfmt::skip]\n" "fn main() {\n" -" let p1: Point = Point(10, 10);\n" -" let p3: &Point;\n" -" {\n" -" let p2: Point = Point(20, 20);\n" -" p3 = left_most(&p1, &p2);\n" +" let foo = Foo { x: (1, 2), y: 3 };\n" +" match foo {\n" +" Foo { x: (1, b), y } => println!(\"x.0 = 1, b = {b}, y = {y}\"),\n" +" Foo { y: 2, x: i } => println!(\"y = 2, x = {i:?}\"),\n" +" Foo { y, .. } => println!(\"y = {y}, other fields were " +"ignored\"),\n" " }\n" -" println!(\"left-most point: {:?}\", p3);\n" "}\n" "```" msgstr "" -"```rust,ignore\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" -" if p1.0 < p2.0 { p1 } else { p2 }\n" -"}\n" -"\n" -"fn main() {\n" -" let p1: Point = Point(10, 10);\n" -" let p3: &Point;\n" -" {\n" -" let p2: Point = Point(20, 20);\n" -" p3 = left_most(&p1, &p2);\n" -" }\n" -" println!(\"left-most point: {:?}\", p3);\n" -"}\n" -"```" - -#: src/ownership/lifetimes-function-calls.md:50 -msgid "Note how this does not compile since `p3` outlives `p2`." -msgstr "请注意:由于 `p3` 的生命周期比 `p2` 长,因此无法编译。" -#: src/ownership/lifetimes-function-calls.md:52 -msgid "" -"Reset the workspace and change the function signature to `fn left_most<'a, " -"'b>(p1: &'a Point, p2: &'a Point) -> &'b Point`. This will not compile " -"because the relationship between the lifetimes `'a` and `'b` is unclear." -msgstr "" -"重置工作区,然后将函数签名更改为 `fn left_most<'a, 'b>(p1: &'a Point, p2: " -"&'a Point) -> &'b Point`。这不会被编译,因为 `'a` 和 `'b` 生命周期之间的关系" -"不明确。" +#: src/pattern-matching/destructuring-structs.md:23 +msgid "Change the literal values in `foo` to match with the other patterns." +msgstr "更改 `foo` 中的字面值,使其与其他模式相匹配。" -#: src/ownership/lifetimes-function-calls.md:53 -msgid "Another way to explain it:" -msgstr "另一种解释方式:" +#: src/pattern-matching/destructuring-structs.md:24 +msgid "Add a new field to `Foo` and make changes to the pattern as needed." +msgstr "在 `Foo` 中添加一个新字段,并根据需要对模式进行修改。" -#: src/ownership/lifetimes-function-calls.md:54 +#: src/pattern-matching/destructuring-structs.md:25 msgid "" -"Two references to two values are borrowed by a function and the function " -"returns another reference." -msgstr "对两个值的两个引用被一个函数借用,该函数返回 另一个引用。" +"The distinction between a capture and a constant expression can be hard to " +"spot. Try changing the `2` in the second arm to a variable, and see that it " +"subtly doesn't work. Change it to a `const` and see it working again." +msgstr "" +"捕获表达式和常量表达式之间的区别可能很难发现。试着将第二个 arm 中的 `2` 改为" +"变量,就会发现它微妙地不起作用了。将其改为 `const`,又能正常工作了。" -#: src/ownership/lifetimes-function-calls.md:56 +#: src/pattern-matching/destructuring-arrays.md:3 msgid "" -"It must have come from one of those two inputs (or from a global variable)." -msgstr "它必须是来自这两个输入中的一个(或来自一个全局变量)。" +"You can destructure arrays, tuples, and slices by matching on their elements:" +msgstr "你可以通过元素匹配来解构数组、元组和切片:" -#: src/ownership/lifetimes-function-calls.md:57 +#: src/pattern-matching/destructuring-arrays.md:5 msgid "" -"Which one is it? The compiler needs to to know, so at the call site the " -"returned reference is not used for longer than a variable from where the " -"reference came from." +"```rust,editable\n" +"#[rustfmt::skip]\n" +"fn main() {\n" +" let triple = [0, -2, 3];\n" +" println!(\"Tell me about {triple:?}\");\n" +" match triple {\n" +" [0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" +" [1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" +" _ => println!(\"All elements were ignored\"),\n" +" }\n" +"}\n" +"```" msgstr "" -#: src/ownership/lifetimes-data-structures.md:3 +#: src/pattern-matching/destructuring-arrays.md:21 msgid "" -"If a data type stores borrowed data, it must be annotated with a lifetime:" -msgstr "如果数据类型存储了借用的数据,则必须对其添加生命周期注释:" +"Destructuring of slices of unknown length also works with patterns of fixed " +"length." +msgstr "对未知长度的切片进行解构也可以使用固定长度的模式。" -#: src/ownership/lifetimes-data-structures.md:5 +#: src/pattern-matching/destructuring-arrays.md:24 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Highlight<'doc>(&'doc str);\n" -"\n" -"fn erase(text: String) {\n" -" println!(\"Bye {text}!\");\n" +"fn main() {\n" +" inspect(&[0, -2, 3]);\n" +" inspect(&[0, -2, 3, 4]);\n" "}\n" "\n" -"fn main() {\n" -" let text = String::from(\"The quick brown fox jumps over the lazy dog." -"\");\n" -" let fox = Highlight(&text[4..19]);\n" -" let dog = Highlight(&text[35..43]);\n" -" // erase(text);\n" -" println!(\"{fox:?}\");\n" -" println!(\"{dog:?}\");\n" +"#[rustfmt::skip]\n" +"fn inspect(slice: &[i32]) {\n" +" println!(\"Tell me about {slice:?}\");\n" +" match slice {\n" +" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" +" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" +" _ => println!(\"All elements were ignored\"),\n" +" }\n" "}\n" "```" msgstr "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Highlight<'doc>(&'doc str);\n" -"\n" -"fn erase(text: String) {\n" -" println!(\"Bye {text}!\");\n" +"fn main() {\n" +" inspect(&[0, -2, 3]);\n" +" inspect(&[0, -2, 3, 4]);\n" "}\n" "\n" -"fn main() {\n" -" let text = String::from(\"The quick brown fox jumps over the lazy dog." -"\");\n" -" let fox = Highlight(&text[4..19]);\n" -" let dog = Highlight(&text[35..43]);\n" -" // erase(text);\n" -" println!(\"{fox:?}\");\n" -" println!(\"{dog:?}\");\n" +"#[rustfmt::skip]\n" +"fn inspect(slice: &[i32]) {\n" +" println!(\"Tell me about {slice:?}\");\n" +" match slice {\n" +" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" +" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" +" _ => println!(\"All elements were ignored\"),\n" +" }\n" "}\n" "```" -#: src/ownership/lifetimes-data-structures.md:25 -msgid "" -"In the above example, the annotation on `Highlight` enforces that the data " -"underlying the contained `&str` lives at least as long as any instance of " -"`Highlight` that uses that data." -msgstr "" -"在上述示例中,`Highlight` 注释会强制包含 `&str` 的底层数据的生命周期至少与使" -"用该数据的任何 `Highlight` 实例一样长。" +#: src/pattern-matching/destructuring-arrays.md:41 +msgid "Create a new pattern using `_` to represent an element. " +msgstr "使用 `_` 创建一个新的模式来代表一个元素。 " -#: src/ownership/lifetimes-data-structures.md:26 +#: src/pattern-matching/destructuring-arrays.md:42 +msgid "Add more values to the array." +msgstr "向数组中添加更多的值。" + +#: src/pattern-matching/destructuring-arrays.md:43 msgid "" -"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " -"the borrow checker throws an error." +"Point out that how `..` will expand to account for different number of " +"elements." +msgstr "指出 `..` 是如何扩展以适应不同数量的元素的。" + +#: src/pattern-matching/destructuring-arrays.md:44 +msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" +msgstr "展示使用模式 `[.., b]` 和 `[a@..,b]` 来匹配切片的尾部" + +#: src/pattern-matching/match-guards.md:3 +msgid "" +"When matching, you can add a _guard_ to a pattern. This is an arbitrary " +"Boolean expression which will be executed if the pattern matches:" msgstr "" -"如果 `text` 在 `fox`(或 `dog`)的生命周期结束前被消耗,借用检查器将抛出一个" -"错误。" +"在匹配时,可以为模式添加 _guard_。这是一个任意布尔表达式,如果模式匹配,它就" +"会被执行:" -#: src/ownership/lifetimes-data-structures.md:27 +#: src/pattern-matching/match-guards.md:6 msgid "" -"Types with borrowed data force users to hold on to the original data. This " -"can be useful for creating lightweight views, but it generally makes them " -"somewhat harder to use." +"```rust,editable\n" +"#[rustfmt::skip]\n" +"fn main() {\n" +" let pair = (2, -2);\n" +" println!(\"Tell me about {pair:?}\");\n" +" match pair {\n" +" (x, y) if x == y => println!(\"These are twins\"),\n" +" (x, y) if x + y == 0 => println!(\"Antimatter, kaboom!\"),\n" +" (x, _) if x % 2 == 1 => println!(\"The first one is odd\"),\n" +" _ => println!(\"No correlation...\"),\n" +" }\n" +"}\n" +"```" msgstr "" -"借用数据的类型会迫使用户保留原始数据。这对于创建轻量级视图很有用,但通常会使" -"它们更难使用。" -#: src/ownership/lifetimes-data-structures.md:28 -msgid "When possible, make data structures own their data directly." -msgstr "如有可能,让数据结构直接拥有自己的数据。" +#: src/pattern-matching/match-guards.md:23 +msgid "" +"Match guards as a separate syntax feature are important and necessary when " +"we wish to concisely express more complex ideas than patterns alone would " +"allow." +msgstr "" +"当我们想简明扼要地表达比模式更复杂的想法时,guard 匹配作为一种单独的语法特征" +"就显得非常重要和必要。" -#: src/ownership/lifetimes-data-structures.md:29 +#: src/pattern-matching/match-guards.md:24 msgid "" -"Some structs with multiple references inside can have more than one lifetime " -"annotation. This can be necessary if there is a need to describe lifetime " -"relationships between the references themselves, in addition to the lifetime " -"of the struct itself. Those are very advanced use cases." +"They are not the same as separate `if` expression inside of the match arm. " +"An `if` expression inside of the branch block (after `=>`) happens after the " +"match arm is selected. Failing the `if` condition inside of that block won't " +"result in other arms of the original `match` expression being considered." msgstr "" -"一些包含多个引用的结构可以有多个生命周期注释。除了结构体本身的生命周期之外," -"如果需要描述引用之间的生命周期关系,则可能需要这样做。这些都是非常高级的用" -"例。" +"它们与匹配 arm 内的单独 `if` 表达式不同。分支代码块内部的 `if` 表达式(在 " +"`=>` 之后)发生在匹配 `arm` 被选中之后。如果该代码块内的 `if` 条件失败,原始" +"匹配表达式的其他分支也不会被考虑。" + +#: src/pattern-matching/match-guards.md:26 +msgid "You can use the variables defined in the pattern in your if expression." +msgstr "您可以在 if 表达式中使用模式中定义的变量。" + +#: src/pattern-matching/match-guards.md:27 +msgid "" +"The condition defined in the guard applies to every expression in a pattern " +"with an `|`." +msgstr "在 gurad 中定义的条件适用于模式中带有 `|` 的每个表达式。" #: src/exercises/day-1/afternoon.md:1 msgid "Day 1: Afternoon Exercises" -msgstr "" +msgstr "Day 1:练习题" #: src/exercises/day-1/afternoon.md:3 msgid "We will look at two things:" -msgstr "" +msgstr "我们将关注两件事:" #: src/exercises/day-1/afternoon.md:5 -msgid "A small book library," -msgstr "" +msgid "The Luhn algorithm," +msgstr "Luhn 算法," #: src/exercises/day-1/afternoon.md:7 -msgid "Iterators and ownership (hard)." +msgid "An exercise on pattern matching." +msgstr "一个关于枚举和模式匹配的练习。" + +#: src/exercises/day-1/afternoon.md:11 src/exercises/day-2/afternoon.md:7 +#: src/exercises/bare-metal/afternoon.md:7 +#: src/exercises/concurrency/afternoon.md:13 +msgid "" +"After looking at the exercises, you can look at the [solutions](solutions-" +"afternoon.md) provided." +msgstr "读完习题后,可以阅读本书提供的[题解](solutions-afternoon.md) 。" + +#: src/exercises/day-1/luhn.md:3 +msgid "" +"The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is used " +"to validate credit card numbers. The algorithm takes a string as input and " +"does the following to validate the credit card number:" msgstr "" +"[Luhn 算法](https://en.wikipedia.org/wiki/Luhn_algorithm) 用于验证信用卡号" +"码。该算法将一个字符串作为输入,并执行以下操作来验证信用卡号码:" + +#: src/exercises/day-1/luhn.md:7 +msgid "Ignore all spaces. Reject number with less than two digits." +msgstr "忽略所有空格。剔除少于两位数的数字。" -#: src/exercises/day-1/book-library.md:3 +#: src/exercises/day-1/luhn.md:9 msgid "" -"We will learn much more about structs and the `Vec` type tomorrow. For " -"now, you just need to know part of its API:" +"Moving from **right to left**, double every second digit: for the number " +"`1234`, we double `3` and `1`. For the number `98765`, we double `6` and `8`." msgstr "" +"从**右向左**,每第二个数字加倍:对于数字`1234`,我们将`3`和`1`加倍。对于数字 " +"`98765`, 我们把`6`和`8`加倍。" -#: src/exercises/day-1/book-library.md:6 +#: src/exercises/day-1/luhn.md:12 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut vec = vec![10, 20];\n" -" vec.push(30);\n" -" let midpoint = vec.len() / 2;\n" -" println!(\"middle value: {}\", vec[midpoint]);\n" -" for item in &vec {\n" -" println!(\"item: {item}\");\n" -" }\n" -"}\n" -"```" +"After doubling a digit, sum the digits if the result is greater than 9. So " +"doubling `7` becomes `14` which becomes `1 + 4 = 5`." msgstr "" +"将一个数字加倍后,如果结果大于 9,则将数字相加。因此,将 `7` 加倍就变成了 " +"`14`,而 `14` 又变成了 `1 + 4 = 5`。" + +#: src/exercises/day-1/luhn.md:15 +msgid "Sum all the undoubled and doubled digits." +msgstr "将所有未加倍和加倍的数字相加。" -#: src/exercises/day-1/book-library.md:18 +#: src/exercises/day-1/luhn.md:17 +msgid "The credit card number is valid if the sum ends with `0`." +msgstr "如果总和为 `0`,则信用卡号有效。" + +#: src/exercises/day-1/luhn.md:19 +msgid "" +"Copy the code below to and implement the " +"function." +msgstr "将下面的代码复制到 并执行该功能。" + +#: src/exercises/day-1/luhn.md:21 msgid "" -"Use this to create a library application. Copy the code below to and update the types to make it compile:" +"Try to solve the problem the \"simple\" way first, using `for` loops and " +"integers. Then, revisit the solution and try to implement it with iterators." msgstr "" +"首先尝试用 “简单 “的方法解决问题,使用 `for` 循环和整数。然后,重新审视解决方" +"案,尝试使用迭代器来实现它。" -#: src/exercises/day-1/book-library.md:21 +#: src/exercises/day-1/luhn.md:25 msgid "" -"```rust,should_panic\n" -"struct Library {\n" -" books: Vec,\n" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" +"\n" +"pub fn luhn(cc_number: &str) -> bool {\n" +" unimplemented!()\n" "}\n" "\n" -"struct Book {\n" -" title: String,\n" -" year: u16,\n" +"#[test]\n" +"fn test_non_digit_cc_number() {\n" +" assert!(!luhn(\"foo\"));\n" +" assert!(!luhn(\"foo 0 0\"));\n" "}\n" "\n" -"impl Book {\n" -" // This is a constructor, used below.\n" -" fn new(title: &str, year: u16) -> Book {\n" -" Book {\n" -" title: String::from(title),\n" -" year,\n" -" }\n" -" }\n" +"#[test]\n" +"fn test_empty_cc_number() {\n" +" assert!(!luhn(\"\"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" "}\n" "\n" -"// Implement the methods below. Update the `self` parameter to\n" -"// indicate the method's required level of ownership over the object:\n" -"//\n" -"// - `&self` for shared read-only access,\n" -"// - `&mut self` for unique and mutable access,\n" -"// - `self` for unique access by value.\n" -"impl Library {\n" -" fn new() -> Library {\n" -" todo!(\"Initialize and return a `Library` value\")\n" -" }\n" +"#[test]\n" +"fn test_single_digit_cc_number() {\n" +" assert!(!luhn(\"0\"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_two_digit_cc_number() {\n" +" assert!(luhn(\" 0 0 \"));\n" +"}\n" "\n" -" //fn len(self) -> usize {\n" -" // todo!(\"Return the length of `self.books`\")\n" -" //}\n" +"#[test]\n" +"fn test_valid_cc_number() {\n" +" assert!(luhn(\"4263 9826 4026 9299\"));\n" +" assert!(luhn(\"4539 3195 0343 6467\"));\n" +" assert!(luhn(\"7992 7398 713\"));\n" +"}\n" "\n" -" //fn is_empty(self) -> bool {\n" -" // todo!(\"Return `true` if `self.books` is empty\")\n" -" //}\n" +"#[test]\n" +"fn test_invalid_cc_number() {\n" +" assert!(!luhn(\"4223 9826 4026 9299\"));\n" +" assert!(!luhn(\"4539 3195 0343 6476\"));\n" +" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +"}\n" "\n" -" //fn add_book(self, book: Book) {\n" -" // todo!(\"Add a new book to `self.books`\")\n" -" //}\n" +"#[allow(dead_code)]\n" +"fn main() {}\n" +"```" +msgstr "" + +#: src/exercises/day-1/pattern-matching.md:1 +msgid "Exercise: Expression Evaluation" +msgstr "练习: 表达式评估" + +#: src/exercises/day-1/pattern-matching.md:3 +msgid "Let's write a simple recursive evaluator for arithmetic expressions. " +msgstr "让我们为算术表达式编写一个简单的递归求值器。 " + +#: src/exercises/day-1/pattern-matching.md:5 +msgid "" +"```rust\n" +"/// An operation to perform on two subexpressions.\n" +"#[derive(Debug)]\n" +"enum Operation {\n" +" Add,\n" +" Sub,\n" +" Mul,\n" +" Div,\n" +"}\n" "\n" -" //fn print_books(self) {\n" -" // todo!(\"Iterate over `self.books` and each book's title and " -"year\")\n" -" //}\n" +"/// An expression, in tree form.\n" +"#[derive(Debug)]\n" +"enum Expression {\n" +" /// An operation on two subexpressions.\n" +" Op {\n" +" op: Operation,\n" +" left: Box,\n" +" right: Box,\n" +" },\n" "\n" -" //fn oldest_book(self) -> Option<&Book> {\n" -" // todo!(\"Return a reference to the oldest book (if any)\")\n" -" //}\n" +" /// A literal value\n" +" Value(i64),\n" "}\n" "\n" -"// This shows the desired behavior. Uncomment the code below and\n" -"// implement the missing methods. You will need to update the\n" -"// method signatures, including the \"self\" parameter! You may\n" -"// also need to update the variable bindings within main.\n" -"fn main() {\n" -" let library = Library::new();\n" +"/// The result of evaluating an expression.\n" +"#[derive(Debug, PartialEq, Eq)]\n" +"enum Res {\n" +" /// Evaluation was successful, with the given result.\n" +" Ok(i64),\n" +" /// Evaluation failed, with the given error message.\n" +" Err(String),\n" +"}\n" +"// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.\n" +"use Res::{Err, Ok};\n" "\n" -" //println!(\"The library is empty: {}\", library.is_empty());\n" -" //\n" -" //library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" //library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" //\n" -" //println!(\"The library is no longer empty: {}\", library.is_empty());\n" -" //\n" -" //\n" -" //library.print_books();\n" -" //\n" -" //match library.oldest_book() {\n" -" // Some(book) => println!(\"The oldest book is {}\", book.title),\n" -" // None => println!(\"The library is empty!\"),\n" -" //}\n" -" //\n" -" //println!(\"The library has {} books\", library.len());\n" -" //library.print_books();\n" +"fn eval(e: Expression) -> Res {\n" +" todo!()\n" +"}\n" +"\n" +"#[test]\n" +"fn test_value() {\n" +" assert_eq!(eval(Expression::Value(19)), Ok(19));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_sum() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(20)),\n" +" }),\n" +" Ok(30)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_recursion() {\n" +" let term1 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(9)),\n" +" };\n" +" let term2 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(3)),\n" +" right: Box::new(Expression::Value(4)),\n" +" }),\n" +" right: Box::new(Expression::Value(5)),\n" +" };\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(term1),\n" +" right: Box::new(term2),\n" +" }),\n" +" Ok(85)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_error() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Div,\n" +" left: Box::new(Expression::Value(99)),\n" +" right: Box::new(Expression::Value(0)),\n" +" }),\n" +" Err(String::from(\"division by zero\"))\n" +" );\n" "}\n" "```" msgstr "" -#: src/exercises/day-1/book-library.md:102 -msgid "[Solution](solutions-afternoon.md#designing-a-library)" -msgstr "" - -#: src/exercises/day-1/iterators-and-ownership.md:3 +#: src/exercises/day-1/pattern-matching.md:100 msgid "" -"The ownership model of Rust affects many APIs. An example of this is the " -"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " -"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " -"traits." +"The `Box` type here is a smart pointer, and will be covered in detail later " +"in the course. An expression can be \"boxed\" with `Box::new` as seen in the " +"tests. To evaluate a boxed expression, use the deref operator to \"unbox\" " +"it: `eval(*boxed_expr)`." msgstr "" +"这里的 `Box` 类型是一个智能指针,将在课程后面详细介绍。如测试所示,表达式可以" +"用 `Box::new` 进行 “盒化”。要评估一个盒装表达式,可以使用 deref 操作符来 “解" +"盒”: `eval(*boxed_expr)`." -#: src/exercises/day-1/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 -msgid "`Iterator`" +#: src/exercises/day-1/pattern-matching.md:105 +msgid "" +"Some expressions cannot be evaluated and will return an error. The `Res` " +"type represents either a successful value or an error with a message. This " +"is very similar to the standard-library `Result` which we will see later." msgstr "" +"有些表达式无法求值,会返回错误。`Res` 类型代表成功值或带信息的错误。这与标准" +"库中的 `Result` 非常相似,我们稍后会看到。" -#: src/exercises/day-1/iterators-and-ownership.md:10 +#: src/exercises/day-1/pattern-matching.md:109 msgid "" -"Traits are like interfaces: they describe behavior (methods) for a type. The " -"`Iterator` trait simply says that you can call `next` until you get `None` " -"back:" +"Copy and paste the code into the Rust playground, and begin implementing " +"`eval`. The final product should pass the tests. It may be helpful to use " +"`todo!()` and get the tests to pass one-by-one." msgstr "" +"将代码复制并粘贴到 Rust playground 中,然后开始执行 `eval`。最终结果应能通过" +"测试。使用 `todo!()` 并让测试逐一通过可能会有帮助。" -#: src/exercises/day-1/iterators-and-ownership.md:13 +#: src/exercises/day-1/pattern-matching.md:113 msgid "" -"```rust\n" -"pub trait Iterator {\n" -" type Item;\n" -" fn next(&mut self) -> Option;\n" -"}\n" -"```" +"If you finish early, try writing a test that results in an integer overflow. " +"How could you handle this with `Res::Err` instead of a panic?" msgstr "" +"如果您提前完成了测试,请尝试编写一个会导致整数溢出的测试。如何用 `Res::Err` " +"而不是 panic 来处理?" -#: src/exercises/day-1/iterators-and-ownership.md:20 -msgid "You use this trait like this:" -msgstr "" +#: src/welcome-day-2.md:1 +msgid "Welcome to Day 2" +msgstr "欢迎来到第二天" + +#: src/welcome-day-2.md:3 +msgid "Now that we have seen a fair amount of Rust, we will continue with:" +msgstr "现在我们已经了解了相当多的Rust,接下来我们将学习:" -#: src/exercises/day-1/iterators-and-ownership.md:22 +#: src/welcome-day-2.md:5 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let v: Vec = vec![10, 20, 30];\n" -" let mut iter = v.iter();\n" -"\n" -" println!(\"v[0]: {:?}\", iter.next());\n" -" println!(\"v[1]: {:?}\", iter.next());\n" -" println!(\"v[2]: {:?}\", iter.next());\n" -" println!(\"No more items: {:?}\", iter.next());\n" -"}\n" -"```" -msgstr "" +"Memory management: stack vs heap, manual memory management, scope-based " +"memory management, and garbage collection." +msgstr "内存管理:栈与堆,手动内存管理,基于作用域的内存管理,以及垃圾回收。" -#: src/exercises/day-1/iterators-and-ownership.md:34 -msgid "What is the type returned by the iterator? Test your answer here:" +#: src/welcome-day-2.md:8 +msgid "" +"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." msgstr "" +"所有权:移动(move)的语义,复制(copy)和克隆(clone),借用(borrow),以及" +"生命周期。" -#: src/exercises/day-1/iterators-and-ownership.md:36 +#: src/welcome-day-2.md:10 +#, fuzzy +msgid "Structs and methods." +msgstr "结构体(struct), 枚举(enum), 方法(method)。" + +#: src/welcome-day-2.md:12 msgid "" -"```rust,editable,compile_fail\n" -"fn main() {\n" -" let v: Vec = vec![10, 20, 30];\n" -" let mut iter = v.iter();\n" -"\n" -" let v0: Option<..> = iter.next();\n" -" println!(\"v0: {v0:?}\");\n" -"}\n" -"```" +"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " +"`Rc` and `Arc`." msgstr "" +"标准库: `字符串(String)`, `选项(Option)` 和 `结果(Result)`, `动态数组" +"(Vec)`, `散列表(HashMap)`, `引用计数(Rc)` 和 `共享引用计数(Arc)`。" -#: src/exercises/day-1/iterators-and-ownership.md:46 -msgid "Why is this type used?" -msgstr "" +#: src/welcome-day-2.md:15 +msgid "Modules: visibility, paths, and filesystem hierarchy." +msgstr "模块: 可见性, 路径和文件系统的层次结构。" -#: src/exercises/day-1/iterators-and-ownership.md:48 -msgid "`IntoIterator`" -msgstr "" +#: src/memory-management.md:3 +msgid "Traditionally, languages have fallen into two broad categories:" +msgstr "传统上,语言分为两大类:" -#: src/exercises/day-1/iterators-and-ownership.md:50 -msgid "" -"The `Iterator` trait tells you how to _iterate_ once you have created an " -"iterator. The related trait `IntoIterator` tells you how to create the " -"iterator:" -msgstr "" +#: src/memory-management.md:5 +msgid "Full control via manual memory management: C, C++, Pascal, ..." +msgstr "通过手动内存管理实现完全控制:C、C++、Pascal…" -#: src/exercises/day-1/iterators-and-ownership.md:53 +#: src/memory-management.md:6 msgid "" -"```rust\n" -"pub trait IntoIterator {\n" -" type Item;\n" -" type IntoIter: Iterator;\n" -"\n" -" fn into_iter(self) -> Self::IntoIter;\n" -"}\n" -"```" -msgstr "" +"Full safety via automatic memory management at runtime: Java, Python, Go, " +"Haskell, ..." +msgstr "运行时通过自动内存管理实现完全安全:Java、Python、Go、Haskell…" + +#: src/memory-management.md:8 +msgid "Rust offers a new mix:" +msgstr "Rust 提供了一个全新的组合:" -#: src/exercises/day-1/iterators-and-ownership.md:62 +#: src/memory-management.md:10 msgid "" -"The syntax here means that every implementation of `IntoIterator` must " -"declare two types:" -msgstr "" +"Full control _and_ safety via compile time enforcement of correct memory " +"management." +msgstr "通过编译时强制执行正确的内存>管理来实现完全控制与安全。" -#: src/exercises/day-1/iterators-and-ownership.md:65 -msgid "`Item`: the type we iterate over, such as `i8`," -msgstr "" +#: src/memory-management.md:13 +msgid "It does this with an explicit ownership concept." +msgstr "它通过一个明确的所有权(ownership)概念来实现此目的。" -#: src/exercises/day-1/iterators-and-ownership.md:66 -msgid "`IntoIter`: the `Iterator` type returned by the `into_iter` method." -msgstr "" +#: src/memory-management.md:15 +msgid "First, let's refresh how memory management works." +msgstr "首先,我们回顾一下内存管理的工作原理。" -#: src/exercises/day-1/iterators-and-ownership.md:68 -msgid "" -"Note that `IntoIter` and `Item` are linked: the iterator must have the same " -"`Item` type, which means that it returns `Option`" -msgstr "" +#: src/memory-management/stack-vs-heap.md:1 +msgid "The Stack vs The Heap" +msgstr "栈与堆" -#: src/exercises/day-1/iterators-and-ownership.md:71 -msgid "Like before, what is the type returned by the iterator?" +#: src/memory-management/stack-vs-heap.md:3 +msgid "Stack: Continuous area of memory for local variables." +msgstr "栈:局部变量的连续内存区域。" + +#: src/memory-management/stack-vs-heap.md:4 +msgid "Values have fixed sizes known at compile time." +msgstr "值在编译时具有已知的固定大小。" + +#: src/memory-management/stack-vs-heap.md:5 +msgid "Extremely fast: just move a stack pointer." +msgstr "速度极快:只需移动一个栈指针。" + +#: src/memory-management/stack-vs-heap.md:6 +msgid "Easy to manage: follows function calls." +msgstr "易于管理:遵循函数调用规则。" + +#: src/memory-management/stack-vs-heap.md:7 +msgid "Great memory locality." +msgstr "优秀的内存局部性。" + +#: src/memory-management/stack-vs-heap.md:9 +msgid "Heap: Storage of values outside of function calls." +msgstr "堆:函数调用之外的值的存储。" + +#: src/memory-management/stack-vs-heap.md:10 +msgid "Values have dynamic sizes determined at runtime." +msgstr "值具有动态大小,具体大小需在运行时确定。" + +#: src/memory-management/stack-vs-heap.md:11 +msgid "Slightly slower than the stack: some book-keeping needed." +msgstr "比栈稍慢:需要向系统申请空间。" + +#: src/memory-management/stack-vs-heap.md:12 +msgid "No guarantee of memory locality." +msgstr "不保证内存局部性。" + +#: src/memory-management/stack.md:1 +#, fuzzy +msgid "Stack and Heap Example" +msgstr "栈 vs 堆" + +#: src/memory-management/stack.md:3 +#, fuzzy +msgid "" +"Creating a `String` puts fixed-sized metadata on the stack and dynamically " +"sized data, the actual string, on the heap:" msgstr "" +"创建 `String` 时将固定大小的数据存储在栈上, 并将动态大小的数据存储在堆上:" -#: src/exercises/day-1/iterators-and-ownership.md:73 +#: src/memory-management/stack.md:6 msgid "" -"```rust,editable,compile_fail\n" +"```rust,editable\n" "fn main() {\n" -" let v: Vec = vec![String::from(\"foo\"), String::" -"from(\"bar\")];\n" -" let mut iter = v.into_iter();\n" -"\n" -" let v0: Option<..> = iter.next();\n" -" println!(\"v0: {v0:?}\");\n" +" let s1 = String::from(\"Hello\");\n" +"}\n" +"```" +msgstr "" +"```rust,editable\n" +"fn main() {\n" +" let s1 = String::from(\"Hello\");\n" "}\n" "```" + +#: src/memory-management/stack.md:28 +msgid "" +"Mention that a `String` is backed by a `Vec`, so it has a capacity and " +"length and can grow if mutable via reallocation on the heap." msgstr "" +"指出 `String` 底层由 `Vec` 实现,因此它具有容量和长度,如果值可变,则可以通过" +"在堆上重新分配存储空间进行增长。" -#: src/exercises/day-1/iterators-and-ownership.md:83 -msgid "`for` Loops" +#: src/memory-management/stack.md:30 +msgid "" +"If students ask about it, you can mention that the underlying memory is heap " +"allocated using the [System Allocator](https://doc.rust-lang.org/std/alloc/" +"struct.System.html) and custom allocators can be implemented using the " +"[Allocator API](https://doc.rust-lang.org/std/alloc/index.html)" msgstr "" +"如果学员提出相关问题,你可以提及我们不仅能使用\\[系统分配器\\]在堆上分配底层" +"内存,还能使用 [Allocator API](https://doc.rust-lang.org/std/alloc/index." +"html) 实现自定义分配器" -#: src/exercises/day-1/iterators-and-ownership.md:85 +#: src/memory-management/stack.md:32 msgid "" -"Now that we know both `Iterator` and `IntoIterator`, we can build `for` " -"loops. They call `into_iter()` on an expression and iterates over the " -"resulting iterator:" +"We can inspect the memory layout with `unsafe` code. However, you should " +"point out that this is rightfully unsafe!" msgstr "" +"我们可以使用 `unsafe` 代码检查内存布局。不过,你应该指出,这种做法不安全!" -#: src/exercises/day-1/iterators-and-ownership.md:89 +#: src/memory-management/stack.md:34 +#, fuzzy msgid "" "```rust,editable\n" "fn main() {\n" -" let v: Vec = vec![String::from(\"foo\"), String::" -"from(\"bar\")];\n" -"\n" -" for word in &v {\n" -" println!(\"word: {word}\");\n" -" }\n" -"\n" -" for word in v {\n" -" println!(\"word: {word}\");\n" +" let mut s1 = String::from(\"Hello\");\n" +" s1.push(' ');\n" +" s1.push_str(\"world\");\n" +" // DON'T DO THIS AT HOME! For educational purposes only.\n" +" // String provides no guarantees about its layout, so this could lead " +"to\n" +" // undefined behavior.\n" +" unsafe {\n" +" let (ptr, capacity, len): (usize, usize, usize) = std::mem::" +"transmute(s1);\n" +" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n" " }\n" "}\n" "```" msgstr "" +"```rust,editable\n" +"fn main() {\n" +" let mut s1 = String::from(\"Hello\");\n" +" s1.push(' ');\n" +" s1.push_str(\"world\");\n" +" // DON'T DO THIS AT HOME! For educational purposes only.\n" +" // String provides no guarantees about its layout, so this could lead " +"to\n" +" // undefined behavior.\n" +" unsafe {\n" +" let (capacity, ptr, len): (usize, usize, usize) = std::mem::" +"transmute(s1);\n" +" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n" +" }\n" +"}\n" +"```" -#: src/exercises/day-1/iterators-and-ownership.md:103 -msgid "What is the type of `word` in each loop?" -msgstr "" +#: src/memory-management/manual.md:3 +msgid "You allocate and deallocate heap memory yourself." +msgstr "你自己实现堆内存分配和释放。" -#: src/exercises/day-1/iterators-and-ownership.md:105 +#: src/memory-management/manual.md:5 msgid "" -"Experiment with the code above and then consult the documentation for [`impl " -"IntoIterator for &Vec`](https://doc.rust-lang.org/std/vec/struct.Vec." -"html#impl-IntoIterator-for-%26%27a%20Vec%3CT%2C%20A%3E) and [`impl " -"IntoIterator for Vec`](https://doc.rust-lang.org/std/vec/struct.Vec." -"html#impl-IntoIterator-for-Vec%3CT%2C%20A%3E) to check your answers." -msgstr "" - -#: src/welcome-day-2.md:1 -msgid "Welcome to Day 2" -msgstr "欢迎来到第二天" - -#: src/welcome-day-2.md:3 -msgid "Now that we have seen a fair amount of Rust, we will continue with:" -msgstr "现在我们已经了解了相当多的Rust,接下来我们将学习:" +"If not done with care, this can lead to crashes, bugs, security " +"vulnerabilities, and memory leaks." +msgstr "稍有不慎,这可能会导致崩溃、bug、安全漏洞和内存泄漏。" -#: src/welcome-day-2.md:5 -msgid "Structs, enums, methods." -msgstr "结构体(struct), 枚举(enum), 方法(method)。" +#: src/memory-management/manual.md:7 +msgid "C Example" +msgstr "C++ 示例" -#: src/welcome-day-2.md:7 -msgid "Pattern matching: destructuring enums, structs, and arrays." -msgstr "模式匹配: 解构枚举, 结构体和数组(array)。" +#: src/memory-management/manual.md:9 +msgid "You must call `free` on every pointer you allocate with `malloc`:" +msgstr "你必须对使用 `malloc` 分配的每个指针调用 `free`:" -#: src/welcome-day-2.md:9 +#: src/memory-management/manual.md:11 +#, fuzzy msgid "" -"Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and " -"`continue`." +"```c\n" +"void foo(size_t n) {\n" +" int* int_array = malloc(n * sizeof(int));\n" +" //\n" +" // ... lots of code\n" +" //\n" +" free(int_array);\n" +"}\n" +"```" msgstr "" -"控制流的构造: `if`, `if let`, `while`, `while let`, `break`, 和 `continue`。" +"```c\n" +"void foo(size_t n) {\n" +" int* int_array = (int*)malloc(n * sizeof(int));\n" +" //\n" +" // ... lots of code\n" +" //\n" +" free(int_array);\n" +"}\n" +"```" -#: src/welcome-day-2.md:12 +#: src/memory-management/manual.md:21 +#, fuzzy msgid "" -"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " -"`Rc` and `Arc`." +"Memory is leaked if the function returns early between `malloc` and `free`: " +"the pointer is lost and we cannot deallocate the memory. Worse, freeing the " +"pointer twice, or accessing a freed pointer can lead to exploitable security " +"vulnerabilities." msgstr "" -"标准库: `字符串(String)`, `选项(Option)` 和 `结果(Result)`, `动态数组" -"(Vec)`, `散列表(HashMap)`, `引用计数(Rc)` 和 `共享引用计数(Arc)`。" +"如果函数在 `malloc` 和 `free` 之间提前返回,则会导致内存泄漏: 指针丢失,而我" +"们无法释放对应的内存。" -#: src/welcome-day-2.md:15 -msgid "Modules: visibility, paths, and filesystem hierarchy." -msgstr "模块: 可见性, 路径和文件系统的层次结构。" +#: src/memory-management/scope-based.md:3 +msgid "" +"Constructors and destructors let you hook into the lifetime of an object." +msgstr "构造函数和析构函数让你可以钩入对象的生命周期。" -#: src/structs.md:3 -msgid "Like C and C++, Rust has support for custom structs:" -msgstr "与 C 和 C++ 一样,Rust 支持自定义结构体:" +#: src/memory-management/scope-based.md:5 +msgid "" +"By wrapping a pointer in an object, you can free memory when the object is " +"destroyed. The compiler guarantees that this happens, even if an exception " +"is raised." +msgstr "" +"通过将指针封装在对象中,你可以在该对象 被销毁时释放内存。编译器可保证这一点的" +"实现,即使引发了异常也不例外。" -#: src/structs.md:5 +#: src/memory-management/scope-based.md:9 msgid "" -"```rust,editable\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"\n" -"fn main() {\n" -" let mut peter = Person {\n" -" name: String::from(\"Peter\"),\n" -" age: 27,\n" -" };\n" -" println!(\"{} is {} years old\", peter.name, peter.age);\n" -" \n" -" peter.age = 28;\n" -" println!(\"{} is {} years old\", peter.name, peter.age);\n" -" \n" -" let jackie = Person {\n" -" name: String::from(\"Jackie\"),\n" -" ..peter\n" -" };\n" -" println!(\"{} is {} years old\", jackie.name, jackie.age);\n" +"This is often called _resource acquisition is initialization_ (RAII) and " +"gives you smart pointers." +msgstr "" +"这通常称为“资源获取即初始化 (resource acquisition is initialization, " +"RAII)”, 并为你提供智能指针。" + +#: src/memory-management/scope-based.md:12 +msgid "C++ Example" +msgstr "C++ 示例" + +#: src/memory-management/scope-based.md:14 +msgid "" +"```c++\n" +"void say_hello(std::unique_ptr person) {\n" +" std::cout << \"Hello \" << person->name << std::endl;\n" "}\n" "```" msgstr "" +"```c++\n" +"void say_hello(std::unique_ptr person) {\n" +" std::cout << \"Hello \" << person->name << std::endl;\n" +"}\n" +"```" -#: src/structs.md:31 src/enums.md:33 src/enums/sizes.md:29 src/methods.md:30 -#: src/methods/example.md:46 src/pattern-matching.md:25 -#: src/pattern-matching/match-guards.md:22 src/control-flow/blocks.md:42 -msgid "Key Points:" -msgstr "关键点:" +#: src/memory-management/scope-based.md:20 +msgid "" +"The `std::unique_ptr` object is allocated on the stack, and points to memory " +"allocated on the heap." +msgstr "`std::unique_ptr` 对象在栈上分配内存,并指向在堆上分配的内存。" -#: src/structs.md:33 -msgid "Structs work like in C or C++." -msgstr "" +#: src/memory-management/scope-based.md:22 +msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." +msgstr "在 `say_hello` 结束时,`std::unique_ptr` 析构函数将运行。" -#: src/structs.md:34 -msgid "Like in C++, and unlike in C, no typedef is needed to define a type." -msgstr "" +#: src/memory-management/scope-based.md:23 +msgid "The destructor frees the `Person` object it points to." +msgstr "析构函数释放它所指向的 `Person` 对象。" -#: src/structs.md:35 -msgid "Unlike in C++, there is no inheritance between structs." -msgstr "" +#: src/memory-management/scope-based.md:25 +msgid "" +"Special move constructors are used when passing ownership to a function:" +msgstr "将所有权传递给函数时,使用特殊的 move 构造函数:" -#: src/structs.md:36 +#: src/memory-management/scope-based.md:27 msgid "" -"Methods are defined in an `impl` block, which we will see in following " -"slides." +"```c++\n" +"std::unique_ptr person = find_person(\"Carla\");\n" +"say_hello(std::move(person));\n" +"```" msgstr "" +"```c++\n" +"std::unique_ptr person = find_person(\"Carla\");\n" +"say_hello(std::move(person));\n" +"```" -#: src/structs.md:37 +#: src/memory-management/garbage-collection.md:1 +msgid "Automatic Memory Management" +msgstr "自动内存管理" + +#: src/memory-management/garbage-collection.md:3 msgid "" -"This may be a good time to let people know there are different types of " -"structs. " +"An alternative to manual and scope-based memory management is automatic " +"memory management:" +msgstr "自动内存管理是手动和基于作用域的内存管理 的替代方案:" + +#: src/memory-management/garbage-collection.md:6 +msgid "The programmer never allocates or deallocates memory explicitly." +msgstr "程序员从不显式分配或取消分配内存。" + +#: src/memory-management/garbage-collection.md:7 +msgid "" +"A garbage collector finds unused memory and deallocates it for the " +"programmer." +msgstr "垃圾回收器找到未使用的内存,并为程序员将其取消分配。" + +#: src/memory-management/garbage-collection.md:9 +msgid "Java Example" +msgstr "Java 示例" + +#: src/memory-management/garbage-collection.md:11 +msgid "The `person` object is not deallocated after `sayHello` returns:" +msgstr "`sayHello` 返回后,`person` 对象未被取消分配:" + +#: src/memory-management/garbage-collection.md:13 +msgid "" +"```java\n" +"void sayHello(Person person) {\n" +" System.out.println(\"Hello \" + person.getName());\n" +"}\n" +"```" msgstr "" +"```java\n" +"void sayHello(Person person) {\n" +" System.out.println(\"Hello \" + person.getName());\n" +"}\n" +"```" -#: src/structs.md:38 +#: src/memory-management/rust.md:1 +msgid "Memory Management in Rust" +msgstr "Rust 中的内存管理" + +#: src/memory-management/rust.md:3 +msgid "Memory management in Rust is a mix:" +msgstr "Rust 中的内存管理是一种混合模式:" + +#: src/memory-management/rust.md:5 +msgid "Safe and correct like Java, but without a garbage collector." +msgstr "像 Java 一样安全又正确,但没有垃圾回收器。" + +#: src/memory-management/rust.md:6 +msgid "Scope-based like C++, but the compiler enforces full adherence." +msgstr "像 C++ 一样基于作用域,但编译器会强制完全遵循规则。" + +#: src/memory-management/rust.md:7 msgid "" -"Zero-sized structs `e.g., struct Foo;` might be used when implementing a " -"trait on some type but don’t have any data that you want to store in the " -"value itself. " +"A Rust user can choose the right abstraction for the situation, some even " +"have no cost at runtime like C." msgstr "" +"Rust 用户可以根据具体情况选择合适的抽象,有些甚至没有像 C 那样的运行时开销。" -#: src/structs.md:39 +#: src/memory-management/rust.md:9 +#, fuzzy +msgid "Rust achieves this by modeling _ownership_ explicitly." +msgstr "它通过对“所有权”进行显式建模来实现这一点。" + +#: src/memory-management/rust.md:13 msgid "" -"The next slide will introduce Tuple structs, used when the field names are " -"not important." +"If asked how at this point, you can mention that in Rust this is usually " +"handled by RAII wrapper types such as [Box](https://doc.rust-lang.org/std/" +"boxed/struct.Box.html), [Vec](https://doc.rust-lang.org/std/vec/struct.Vec." +"html), [Rc](https://doc.rust-lang.org/std/rc/struct.Rc.html), or [Arc]" +"(https://doc.rust-lang.org/std/sync/struct.Arc.html). These encapsulate " +"ownership and memory allocation via various means, and prevent the potential " +"errors in C." msgstr "" +"如果此时被问及如何操作,你可以提及在 Rust 中,这通常由 RAII 封装容器类型(例" +"如 [Box](https://doc.rust-lang.org/std/boxed/struct.Box.html)、[Vec](https://" +"doc.rust-lang.org/std/vec/struct.Vec.html)、[Rc](https://doc.rust-lang.org/" +"std/rc/struct.Rc.html) 或 [Arc](https://doc.rust-lang.org/std/sync/struct." +"Arc.html))处理。这些类型通过各种方式封装了所有权和内存分配,并防止了 C 中潜" +"在错误的发生。" -#: src/structs.md:40 +#: src/memory-management/rust.md:15 msgid "" -"The syntax `..peter` allows us to copy the majority of the fields from the " -"old struct without having to explicitly type it all out. It must always be " -"the last element." +"You may be asked about destructors here, the [Drop](https://doc.rust-lang." +"org/std/ops/trait.Drop.html) trait is the Rust equivalent." msgstr "" +"你可能会被问及析构函数,此处 [Drop](https://doc.rust-lang.org/std/ops/trait." +"Drop.html) trait 是 Rust 等效项。" -#: src/structs/tuple-structs.md:3 -msgid "If the field names are unimportant, you can use a tuple struct:" -msgstr "如果字段名称不重要,您可以使用元组结构体:" +#: src/ownership.md:3 +msgid "" +"All variable bindings have a _scope_ where they are valid and it is an error " +"to use a variable outside its scope:" +msgstr "所有变量绑定都有一个有效的“作用域”,使用 超出其作用域的变量是错误的:" -#: src/structs/tuple-structs.md:5 +#: src/ownership.md:6 msgid "" -"```rust,editable\n" +"```rust,editable,compile_fail\n" "struct Point(i32, i32);\n" "\n" "fn main() {\n" -" let p = Point(17, 23);\n" -" println!(\"({}, {})\", p.0, p.1);\n" +" {\n" +" let p = Point(3, 4);\n" +" println!(\"x: {}\", p.0);\n" +" }\n" +" println!(\"y: {}\", p.1);\n" "}\n" "```" msgstr "" - -#: src/structs/tuple-structs.md:14 -msgid "This is often used for single-field wrappers (called newtypes):" -msgstr "这通常用于单字段封装容器(称为 newtype):" - -#: src/structs/tuple-structs.md:16 -msgid "" "```rust,editable,compile_fail\n" -"struct PoundsOfForce(f64);\n" -"struct Newtons(f64);\n" -"\n" -"fn compute_thruster_force() -> PoundsOfForce {\n" -" todo!(\"Ask a rocket scientist at NASA\")\n" -"}\n" -"\n" -"fn set_thruster_force(force: Newtons) {\n" -" // ...\n" -"}\n" +"struct Point(i32, i32);\n" "\n" "fn main() {\n" -" let force = compute_thruster_force();\n" -" set_thruster_force(force);\n" +" {\n" +" let p = Point(3, 4);\n" +" println!(\"x: {}\", p.0);\n" +" }\n" +" println!(\"y: {}\", p.1);\n" "}\n" -"\n" "```" -msgstr "" -#: src/structs/tuple-structs.md:37 +#: src/ownership.md:18 msgid "" -"Newtypes are a great way to encode additional information about the value in " -"a primitive type, for example:" -msgstr "" -"如需对基元类型中的值的额外信息进行编码,使用 newtype 是一种非常好的方式,例" -"如:" +"At the end of the scope, the variable is _dropped_ and the data is freed." +msgstr "作用域结束时,变量会“被丢弃”,数据会被释放。" -#: src/structs/tuple-structs.md:38 -msgid "The number is measured in some units: `Newtons` in the example above." -msgstr "数字会以某些单位来衡量:上方示例中为 `Newtons`。" +#: src/ownership.md:19 +msgid "A destructor can run here to free up resources." +msgstr "析构函数可在此运行以释放资源。" -#: src/structs/tuple-structs.md:39 -msgid "" -"The value passed some validation when it was created, so you no longer have " -"to validate it again at every use: 'PhoneNumber(String)`or`OddNumber(u32)\\`." -msgstr "" -"值在创建时已通过一些验证,因此您不再需要在每次使用时都再次验证它:" -"`PhoneNumber(String)` 或 `OddNumber(u32)`。" +#: src/ownership.md:20 +msgid "We say that the variable _owns_ the value." +msgstr "指出变量“拥有”值。" -#: src/structs/tuple-structs.md:40 +#: src/ownership/move-semantics.md:3 +#, fuzzy +msgid "An assignment will transfer _ownership_ between variables:" +msgstr "赋值操作将在变量之间转移所有权:" + +#: src/ownership/move-semantics.md:5 msgid "" -"Demonstrate how to add a `f64` value to a `Newtons` type by accessing the " -"single field in the newtype." +"```rust,editable\n" +"fn main() {\n" +" let s1: String = String::from(\"Hello!\");\n" +" let s2: String = s1;\n" +" println!(\"s2: {s2}\");\n" +" // println!(\"s1: {s1}\");\n" +"}\n" +"```" msgstr "" -"展示如何通过访问 newtype 中的单个字段,将 `f64` 值添加到 `Newtons` 类型。" +"```rust,editable\n" +"fn main() {\n" +" let s1: String = String::from(\"Hello!\");\n" +" let s2: String = s1;\n" +" println!(\"s2: {s2}\");\n" +" // println!(\"s1: {s1}\");\n" +"}\n" +"```" -#: src/structs/tuple-structs.md:41 -msgid "" -"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " -"for instance using booleans as integers." -msgstr "Rust 通常不喜欢不明确的内容,例如自动解封或将布尔值用作整数。" +#: src/ownership/move-semantics.md:14 +msgid "The assignment of `s1` to `s2` transfers ownership." +msgstr "将 `s1` 赋值给 `s2`,即转移了所有权。" -#: src/structs/tuple-structs.md:42 -msgid "Operator overloading is discussed on Day 3 (generics). " -msgstr "" +#: src/ownership/move-semantics.md:15 +#, fuzzy +msgid "When `s1` goes out of scope, nothing happens: it does not own anything." +msgstr "当 `s1` 离开作用域时,什么都不会发生:它没有所有权。" -#: src/structs/field-shorthand.md:3 +#: src/ownership/move-semantics.md:16 +msgid "When `s2` goes out of scope, the string data is freed." +msgstr "当 `s2` 离开作用域时,字符串数据被释放。" + +#: src/ownership/move-semantics.md:17 +msgid "There is always _exactly_ one variable binding which owns a value." +msgstr "变量绑定在任一时刻有且“只有”一个值。" + +#: src/ownership/move-semantics.md:21 msgid "" -"If you already have variables with the right names, then you can create the " -"struct using a shorthand:" -msgstr "如果您已有名称正确的变量,则可以使用简写形式创建结构体:" +"Mention that this is the opposite of the defaults in C++, which copies by " +"value unless you use `std::move` (and the move constructor is defined!)." +msgstr "" +"指出这与 C++ 中的默认值相反。除非你使用 `std::move`(并已定义 move 构造函" +"数!),否则 C++ 中的默认值是按值复制的。" -#: src/structs/field-shorthand.md:6 +#: src/ownership/move-semantics.md:23 msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"\n" -"impl Person {\n" -" fn new(name: String, age: u8) -> Person {\n" -" Person { name, age }\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let peter = Person::new(String::from(\"Peter\"), 27);\n" -" println!(\"{peter:?}\");\n" -"}\n" -"```" +"It is only the ownership that moves. Whether any machine code is generated " +"to manipulate the data itself is a matter of optimization, and such copies " +"are aggressively optimized away." msgstr "" -#: src/structs/field-shorthand.md:27 +#: src/ownership/move-semantics.md:25 msgid "" -"The `new` function could be written using `Self` as a type, as it is " -"interchangeable with the struct type name" +"Simple values (such as integers) can be marked `Copy` (see later slides)." msgstr "" -#: src/structs/field-shorthand.md:29 +#: src/ownership/move-semantics.md:27 +msgid "In Rust, clones are explicit (by using `clone`)." +msgstr "在 Rust 中,克隆是显式的(通过使用 `clone`)。" + +#: src/ownership/moved-strings-rust.md:3 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"impl Person {\n" -" fn new(name: String, age: u8) -> Self {\n" -" Self { name, age }\n" -" }\n" +"fn main() {\n" +" let s1: String = String::from(\"Rust\");\n" +" let s2: String = s1;\n" "}\n" "```" msgstr "" +"```rust,editable\n" +"fn main() {\n" +" let s1: String = String::from(\"Rust\");\n" +" let s2: String = s1;\n" +"}\n" +"```" -#: src/structs/field-shorthand.md:41 +#: src/ownership/moved-strings-rust.md:10 +msgid "The heap data from `s1` is reused for `s2`." +msgstr "`s1` 中的堆数据会被 `s2` 重复使用。" + +#: src/ownership/moved-strings-rust.md:11 +msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." +msgstr "当 `s1` 离开作用域时,什么都不会发生(它已被移出)。" + +#: src/ownership/moved-strings-rust.md:13 +msgid "Before move to `s2`:" +msgstr "移动到 `s2` 中之前:" + +#: src/ownership/moved-strings-rust.md:30 +msgid "After move to `s2`:" +msgstr "移动到 `s2` 中之后:" + +#: src/ownership/moved-strings-rust.md:32 msgid "" -"Implement the `Default` trait for the struct. Define some fields and use the " -"default values for the other fields." +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" +": : : :\n" +": s1 \"(inaccessible)\" : : :\n" +": +-----------+-------+ : : +----+----+----+----+ :\n" +": | ptr | o---+---+--+--+-->| R | u | s | t | :\n" +": | len | 4 | : | : +----+----+----+----+ :\n" +": | capacity | 4 | : | : :\n" +": +-----------+-------+ : | : :\n" +": : | `- - - - - - - - - - - - - -'\n" +": s2 : |\n" +": +-----------+-------+ : |\n" +": | ptr | o---+---+--'\n" +": | len | 4 | :\n" +": | capacity | 4 | :\n" +": +-----------+-------+ :\n" +": :\n" +"`- - - - - - - - - - - - - -'\n" +"```" msgstr "" +"```bob\n" +" 栈 堆\n" +".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" +": : : :\n" +": s1 \"(无法访问)\" : : :\n" +": +-----------+-------+ : : +----+----+----+----+ :\n" +": | ptr | o---+---+--+--+-->| R | u | s | t | :\n" +": | len | 4 | : | : +----+----+----+----+ :\n" +": | capacity | 4 | : | : :\n" +": +-----------+-------+ : | : :\n" +": : | `- - - - - - - - - - - - - -'\n" +": s2 : |\n" +": +-----------+-------+ : |\n" +": | ptr | o---+---+--'\n" +": | len | 4 | :\n" +": | capacity | 4 | :\n" +": +-----------+-------+ :\n" +": :\n" +"`- - - - - - - - - - - - - -'\n" +"```" -#: src/structs/field-shorthand.md:43 +#: src/ownership/double-free-modern-cpp.md:1 +#, fuzzy +msgid "Defensive Copies in Modern C++" +msgstr "现代 C++ 中的双重释放" + +#: src/ownership/double-free-modern-cpp.md:3 +msgid "Modern C++ solves this differently:" +msgstr "现代 C++ 以不同的方式解决此问题:" + +#: src/ownership/double-free-modern-cpp.md:5 msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"impl Default for Person {\n" -" fn default() -> Person {\n" -" Person {\n" -" name: \"Bot\".to_string(),\n" -" age: 0,\n" -" }\n" -" }\n" -"}\n" -"fn create_default() {\n" -" let tmp = Person {\n" -" ..Default::default()\n" -" };\n" -" let tmp = Person {\n" -" name: \"Sam\".to_string(),\n" -" ..Default::default()\n" -" };\n" -"}\n" +"```c++\n" +"std::string s1 = \"Cpp\";\n" +"std::string s2 = s1; // Duplicate the data in s1.\n" "```" msgstr "" +"```c++\n" +"std::string s1 = \"Cpp\";\n" +"std::string s2 = s1; // 复制 s1 中的数据。\n" +"```" -#: src/structs/field-shorthand.md:68 -msgid "Methods are defined in the `impl` block." +#: src/ownership/double-free-modern-cpp.md:10 +msgid "" +"The heap data from `s1` is duplicated and `s2` gets its own independent copy." +msgstr "`s1` 中的堆数据被复制,`s2` 获得自己的独立副本。" + +#: src/ownership/double-free-modern-cpp.md:11 +msgid "When `s1` and `s2` go out of scope, they each free their own memory." +msgstr "当 `s1` 和 `s2` 离开作用域时,它们会各自释放自己的内存。" + +#: src/ownership/double-free-modern-cpp.md:13 +msgid "Before copy-assignment:" +msgstr "复制-赋值之前:" + +#: src/ownership/double-free-modern-cpp.md:30 +msgid "After copy-assignment:" +msgstr "复制-赋值之后:" + +#: src/ownership/double-free-modern-cpp.md:57 +msgid "" +"C++ has made a slightly different choice than Rust. Because `=` copies data, " +"the string data has to be cloned. Otherwise we would get a double-free when " +"either string goes out of scope." msgstr "" -#: src/structs/field-shorthand.md:69 +#: src/ownership/double-free-modern-cpp.md:61 msgid "" -"Use struct update syntax to define a new structure using `peter`. Note that " -"the variable `peter` will no longer be accessible afterwards." +"C++ also has [`std::move`](https://en.cppreference.com/w/cpp/utility/move), " +"which is used to indicate when a value may be moved from. If the example had " +"been `s2 = std::move(s1)`, no heap allocation would take place. After the " +"move, `s1` would be in a valid but unspecified state. Unlike Rust, the " +"programmer is allowed to keep using `s1`." msgstr "" -#: src/structs/field-shorthand.md:70 +#: src/ownership/double-free-modern-cpp.md:66 msgid "" -"Use `{:#?}` when printing structs to request the `Debug` representation." +"Unlike Rust, `=` in C++ can run arbitrary code as determined by the type " +"which is being copied or moved." msgstr "" -#: src/enums.md:3 +#: src/ownership/moves-function-calls.md:3 msgid "" -"The `enum` keyword allows the creation of a type which has a few different " -"variants:" -msgstr "`enum` 关键字允许创建具有几个 不同变体的类型:" +"When you pass a value to a function, the value is assigned to the function " +"parameter. This transfers ownership:" +msgstr "你将值传递给函数时,该值会被赋给函数 参数。这就转移了所有权:" -#: src/enums.md:6 +#: src/ownership/moves-function-calls.md:6 msgid "" "```rust,editable\n" -"fn generate_random_number() -> i32 {\n" -" 4 // Chosen by fair dice roll. Guaranteed to be random.\n" +"fn say_hello(name: String) {\n" +" println!(\"Hello {name}\")\n" "}\n" "\n" -"#[derive(Debug)]\n" -"enum CoinFlip {\n" -" Heads,\n" -" Tails,\n" -"}\n" -"\n" -"fn flip_coin() -> CoinFlip {\n" -" let random_number = generate_random_number();\n" -" if random_number % 2 == 0 {\n" -" return CoinFlip::Heads;\n" -" } else {\n" -" return CoinFlip::Tails;\n" -" }\n" +"fn main() {\n" +" let name = String::from(\"Alice\");\n" +" say_hello(name);\n" +" // say_hello(name);\n" +"}\n" +"```" +msgstr "" +"```rust,editable\n" +"fn say_hello(name: String) {\n" +" println!(\"Hello {name}\")\n" "}\n" "\n" "fn main() {\n" -" println!(\"You got: {:?}\", flip_coin());\n" +" let name = String::from(\"Alice\");\n" +" say_hello(name);\n" +" // say_hello(name);\n" "}\n" "```" -msgstr "" - -#: src/enums.md:36 -msgid "Enumerations allow you to collect a set of values under one type" -msgstr "枚举允许你从一种类型下收集一组值" -#: src/enums.md:37 +#: src/ownership/moves-function-calls.md:20 msgid "" -"This page offers an enum type `CoinFlip` with two variants `Heads` and " -"`Tail`. You might note the namespace when using variants." +"With the first call to `say_hello`, `main` gives up ownership of `name`. " +"Afterwards, `name` cannot be used anymore within `main`." msgstr "" -"本页提供了一个枚举类型 `CoinFlip`,其中包含 `Heads` 和`Tail`两个变体。在使用" -"变体时,你可能会注意到命名空间。" +"首次调用 `say_hello` 时,`main` 便放弃了 `name` 的所有权。此后,`main` 中不能" +"再使用 `name`。" -#: src/enums.md:38 -msgid "This might be a good time to compare Structs and Enums:" -msgstr "这可能是比较结构体和枚举的好时机:" +#: src/ownership/moves-function-calls.md:21 +msgid "" +"The heap memory allocated for `name` will be freed at the end of the " +"`say_hello` function." +msgstr "在 `say_hello` 函数结束时,系统会释放为 `name` 分配的堆内存。" -#: src/enums.md:39 +#: src/ownership/moves-function-calls.md:22 msgid "" -"In both, you can have a simple version without fields (unit struct) or one " -"with different types of fields (variant payloads). " +"`main` can retain ownership if it passes `name` as a reference (`&name`) and " +"if `say_hello` accepts a reference as a parameter." msgstr "" -"在这两者中,你可以获得一个不含字段的简单版本(单位结构体),或一个包含不同类" -"型字段的版本(变体载荷)。" +"如果 `main` 将 `name` 作为引用 (`&name`) 传递过去,且 `say_hello` 接受作为参" +"数的引用,则可保留所有权。" -#: src/enums.md:40 -msgid "In both, associated functions are defined within an `impl` block." -msgstr "在这两者中,关联的函数都在 `impl` 块中定义。" +#: src/ownership/moves-function-calls.md:23 +msgid "" +"Alternatively, `main` can pass a clone of `name` in the first call (`name." +"clone()`)." +msgstr "此外,`main` 也可以在首次调用时传递 `name` 的克隆 (`name.clone()`)。" -#: src/enums.md:41 +#: src/ownership/moves-function-calls.md:24 msgid "" -"You could even implement the different variants of an enum with separate " -"structs but then they wouldn’t be the same type as they would if they were " -"all defined in an enum. " +"Rust makes it harder than C++ to inadvertently create copies by making move " +"semantics the default, and by forcing programmers to make clones explicit." msgstr "" -"你甚至可以使用单独的结构体实现枚举的不同变体,但这样一来,如果它们都已在枚举" -"中定义,类型与之前也不一样。" +"相较于 C++,Rust 通过将移动语义设为默认值,并强制程序员进行显式克隆,更难以无" +"意中创建副本。" -#: src/enums/variant-payloads.md:3 +#: src/ownership/copy-clone.md:3 msgid "" -"You can define richer enums where the variants carry data. You can then use " -"the `match` statement to extract the data from each variant:" -msgstr "" -"你可以定义更丰富的枚举,其中变体会携带数据。然后,你可以使用 `match` 语句从每" -"个变体中提取数据:" +"While move semantics are the default, certain types are copied by default:" +msgstr "虽然移动语义是默认的,但默认情况下会复制某些类型:" -#: src/enums/variant-payloads.md:6 +#: src/ownership/copy-clone.md:5 msgid "" "```rust,editable\n" -"enum WebEvent {\n" -" PageLoad, // Variant without payload\n" -" KeyPress(char), // Tuple struct variant\n" -" Click { x: i64, y: i64 }, // Full struct variant\n" -"}\n" -"\n" -"#[rustfmt::skip]\n" -"fn inspect(event: WebEvent) {\n" -" match event {\n" -" WebEvent::PageLoad => println!(\"page loaded\"),\n" -" WebEvent::KeyPress(c) => println!(\"pressed '{c}'\"),\n" -" WebEvent::Click { x, y } => println!(\"clicked at x={x}, y={y}\"),\n" -" }\n" -"}\n" -"\n" "fn main() {\n" -" let load = WebEvent::PageLoad;\n" -" let press = WebEvent::KeyPress('x');\n" -" let click = WebEvent::Click { x: 20, y: 80 };\n" -"\n" -" inspect(load);\n" -" inspect(press);\n" -" inspect(click);\n" +" let x = 42;\n" +" let y = x;\n" +" println!(\"x: {x}\");\n" +" println!(\"y: {y}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"enum WebEvent {\n" -" PageLoad, // Variant without payload\n" -" KeyPress(char), // Tuple struct variant\n" -" Click { x: i64, y: i64 }, // Full struct variant\n" +"fn main() {\n" +" let x = 42;\n" +" let y = x;\n" +" println!(\"x: {x}\");\n" +" println!(\"y: {y}\");\n" "}\n" +"```" + +#: src/ownership/copy-clone.md:14 +msgid "These types implement the `Copy` trait." +msgstr "这些类型实现了 `Copy` trait。" + +#: src/ownership/copy-clone.md:16 +msgid "You can opt-in your own types to use copy semantics:" +msgstr "你可以选择自己的类型来使用复制语义:" + +#: src/ownership/copy-clone.md:18 +msgid "" +"```rust,editable\n" +"#[derive(Copy, Clone, Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"#[rustfmt::skip]\n" -"fn inspect(event: WebEvent) {\n" -" match event {\n" -" WebEvent::PageLoad => println!(\"page loaded\"),\n" -" WebEvent::KeyPress(c) => println!(\"pressed '{c}'\"),\n" -" WebEvent::Click { x, y } => println!(\"clicked at x={x}, y={y}\"),\n" -" }\n" +"fn main() {\n" +" let p1 = Point(3, 4);\n" +" let p2 = p1;\n" +" println!(\"p1: {p1:?}\");\n" +" println!(\"p2: {p2:?}\");\n" "}\n" +"```" +msgstr "" +"```rust,editable\n" +"#[derive(Copy, Clone, Debug)]\n" +"struct Point(i32, i32);\n" "\n" "fn main() {\n" -" let load = WebEvent::PageLoad;\n" -" let press = WebEvent::KeyPress('x');\n" -" let click = WebEvent::Click { x: 20, y: 80 };\n" -"\n" -" inspect(load);\n" -" inspect(press);\n" -" inspect(click);\n" +" let p1 = Point(3, 4);\n" +" let p2 = p1;\n" +" println!(\"p1: {p1:?}\");\n" +" println!(\"p2: {p2:?}\");\n" "}\n" "```" -#: src/enums/variant-payloads.md:35 -msgid "" -"The values in the enum variants can only be accessed after being pattern " -"matched. The pattern binds references to the fields in the \"match arm\" " -"after the `=>`." -msgstr "" -"枚举变体中的值只有在被模式匹配后,才可访问。模式将引用绑定到 `=>` 之后" -"的“match 分支”中的字段。" +#: src/ownership/copy-clone.md:30 +msgid "After the assignment, both `p1` and `p2` own their own data." +msgstr "赋值之后,`p1` 和 `p2` 都拥有自己的数据。" -#: src/enums/variant-payloads.md:36 -msgid "" -"The expression is matched against the patterns from top to bottom. There is " -"no fall-through like in C or C++." -msgstr "表达式会从上到下与模式匹配。没有像 C 或 C++ 中那样的跳转。" +#: src/ownership/copy-clone.md:31 +msgid "We can also use `p1.clone()` to explicitly copy the data." +msgstr "我们还可以使用 `p1.clone()` 显式复制数据。" -#: src/enums/variant-payloads.md:37 +#: src/ownership/copy-clone.md:35 +msgid "Copying and cloning are not the same thing:" +msgstr "复制和克隆是两码事:" + +#: src/ownership/copy-clone.md:37 msgid "" -"The match expression has a value. The value is the last expression in the " -"match arm which was executed." -msgstr "匹配表达式拥有一个值。值是 match 分支中被执行的最后一个表达式。" +"Copying refers to bitwise copies of memory regions and does not work on " +"arbitrary objects." +msgstr "复制是指内存区域的按位复制,不适用于任意对象。" -#: src/enums/variant-payloads.md:38 +#: src/ownership/copy-clone.md:38 msgid "" -"Starting from the top we look for what pattern matches the value then run " -"the code following the arrow. Once we find a match, we stop. " -msgstr "" -"从顶部开始,查找与该值匹配的模式,然后沿箭头运行代码。一旦找到匹配,我们便会" -"停止。" +"Copying does not allow for custom logic (unlike copy constructors in C++)." +msgstr "复制不允许自定义逻辑(不同于 C++ 中的复制构造函数)。" -#: src/enums/variant-payloads.md:39 +#: src/ownership/copy-clone.md:39 msgid "" -"Demonstrate what happens when the search is inexhaustive. Note the advantage " -"the Rust compiler provides by confirming when all cases are handled. " -msgstr "" -"展示搜索不详尽时会发生的情况。请注意 Rust 编译器的优势,即确认所有情况何时都" -"得到了处理。" +"Cloning is a more general operation and also allows for custom behavior by " +"implementing the `Clone` trait." +msgstr "克隆是一种更通用的操作,也允许通过实现 `Clone` trait 来自定义行为。" -#: src/enums/variant-payloads.md:40 -msgid "`match` inspects a hidden discriminant field in the `enum`." -msgstr "`match` 会检查 `enum` 中的隐藏的判别字段。" +#: src/ownership/copy-clone.md:40 +msgid "Copying does not work on types that implement the `Drop` trait." +msgstr "复制不适用于实现 `Drop` trait 的类型。" -#: src/enums/variant-payloads.md:41 +#: src/ownership/copy-clone.md:42 src/ownership/lifetimes-function-calls.md:29 +msgid "In the above example, try the following:" +msgstr "在上述示例中,请尝试以下操作:" + +#: src/ownership/copy-clone.md:44 msgid "" -"It is possible to retrieve the discriminant by calling `std::mem::" -"discriminant()`" -msgstr "可以通过调用 `std::mem::discriminant()` 来检索判别" +"Add a `String` field to `struct Point`. It will not compile because `String` " +"is not a `Copy` type." +msgstr "" +"在 `struct Point` 中添加 `String` 字段。由于 `String` 不属于 `Copy` 类型,因" +"此无法编译。" -#: src/enums/variant-payloads.md:42 +#: src/ownership/copy-clone.md:45 msgid "" -"This is useful, for example, if implementing `PartialEq` for structs where " -"comparing field values doesn't affect equality." -msgstr "这很有用,例如如果为结构体实现 `PartialEq`,比较字段值不会影响等式。" +"Remove `Copy` from the `derive` attribute. The compiler error is now in the " +"`println!` for `p1`." +msgstr "" +"从 `derive` 属性中移除 `Copy`。现在,编译器错误位于 `p1`的 `println!` 中。" -#: src/enums/variant-payloads.md:43 +#: src/ownership/copy-clone.md:46 +msgid "Show that it works if you clone `p1` instead." +msgstr "指出如果你改为克隆 `p1`,则可按预期运行。" + +#: src/ownership/copy-clone.md:48 msgid "" -"`WebEvent::Click { ... }` is not exactly the same as `WebEvent::" -"Click(Click)` with a top level `struct Click { ... }`. The inlined version " -"cannot implement traits, for example." +"If students ask about `derive`, it is sufficient to say that this is a way " +"to generate code in Rust at compile time. In this case the default " +"implementations of `Copy` and `Clone` traits are generated." msgstr "" -"`WebEvent::Click { ... }` 与含顶层 `struct Click { ... }` 的 `WebEvent::" -"Click(Click)` 不完全相同。例如,内嵌版本无法实现 trait。" +"如果学员问起 `derive`,只需说这是一种 在编译时生成 Rust 代码的方法。在这种情" +"况下,系统会生成 `Copy` 和 `Clone` trait 的默认实现。" -#: src/enums/sizes.md:3 +#: src/ownership/borrowing.md:3 msgid "" -"Rust enums are packed tightly, taking constraints due to alignment into " -"account:" -msgstr "Rust 枚举被紧密地打包,考虑到了对齐的影响,因此存在一些限制:" +"Instead of transferring ownership when calling a function, you can let a " +"function _borrow_ the value:" +msgstr "调用函数时,你可以让 函数“借用”值,而不是转移所有权:" -#: src/enums/sizes.md:5 +#: src/ownership/borrowing.md:6 msgid "" "```rust,editable\n" -"use std::mem::{align_of, size_of};\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"macro_rules! dbg_size {\n" -" ($t:ty) => {\n" -" println!(\"{}: size {} bytes, align: {} bytes\",\n" -" stringify!($t), size_of::<$t>(), align_of::<$t>());\n" -" };\n" -"}\n" -"\n" -"enum Foo {\n" -" A,\n" -" B,\n" +"fn add(p1: &Point, p2: &Point) -> Point {\n" +" Point(p1.0 + p2.0, p1.1 + p2.1)\n" "}\n" "\n" "fn main() {\n" -" dbg_size!(Foo);\n" +" let p1 = Point(3, 4);\n" +" let p2 = Point(10, 20);\n" +" let p3 = add(&p1, &p2);\n" +" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"use std::mem::{align_of, size_of};\n" -"\n" -"macro_rules! dbg_size {\n" -" ($t:ty) => {\n" -" println!(\"{}: size {} bytes, align: {} bytes\",\n" -" stringify!($t), size_of::<$t>(), align_of::<$t>());\n" -" };\n" -"}\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"enum Foo {\n" -" A,\n" -" B,\n" +"fn add(p1: &Point, p2: &Point) -> Point {\n" +" Point(p1.0 + p2.0, p1.1 + p2.1)\n" "}\n" "\n" "fn main() {\n" -" dbg_size!(Foo);\n" +" let p1 = Point(3, 4);\n" +" let p2 = Point(10, 20);\n" +" let p3 = add(&p1, &p2);\n" +" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" "}\n" "```" -#: src/enums/sizes.md:25 -msgid "" -"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." -"html)." -msgstr "" -"请参阅 [Rust 引用](https://doc.rust-lang.org/reference/type-layout.html)。" +#: src/ownership/borrowing.md:22 +msgid "The `add` function _borrows_ two points and returns a new point." +msgstr "`add` 函数“借用”两个点并返回一个新点。" -#: src/enums/sizes.md:31 -#, fuzzy -msgid "" -"Internally Rust is using a field (discriminant) to keep track of the enum " -"variant." -msgstr "在内部,Rust 使用字段(判别)来跟踪枚举变体。" +#: src/ownership/borrowing.md:23 +msgid "The caller retains ownership of the inputs." +msgstr "调用方会保留输入的所有权。" + +#: src/ownership/borrowing.md:27 +msgid "Notes on stack returns:" +msgstr "关于栈返回的说明:" -#: src/enums/sizes.md:33 +#: src/ownership/borrowing.md:28 #, fuzzy msgid "" -"You can control the discriminant if needed (e.g., for compatibility with C):" -msgstr "你可以根据需要控制判别(例如,与 C 的兼容性):" +"Demonstrate that the return from `add` is cheap because the compiler can " +"eliminate the copy operation. Change the above code to print stack addresses " +"and run it on the [Playground](https://play.rust-lang.org/) or look at the " +"assembly in [Godbolt](https://rust.godbolt.org/). In the \"DEBUG\" " +"optimization level, the addresses should change, while they stay the same " +"when changing to the \"RELEASE\" setting:" +msgstr "" +"证明从 `add` 返回的开销很低,因为编译器可以消除复制操作。更改上述代码以输出栈" +"地址,并在 [Playground](https://play.rust-lang.org/) 上运行它。在“调试”优化级" +"别中,地址应发生变化,而在改成“发布”设置时保持不变:" -#: src/enums/sizes.md:35 +#: src/ownership/borrowing.md:30 #, fuzzy msgid "" "```rust,editable\n" -"#[repr(u32)]\n" -"enum Bar {\n" -" A, // 0\n" -" B = 10000,\n" -" C, // 10001\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" +"\n" +"fn add(p1: &Point, p2: &Point) -> Point {\n" +" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n" +" println!(\"&p.0: {:p}\", &p.0);\n" +" p\n" "}\n" "\n" -"fn main() {\n" -" println!(\"A: {}\", Bar::A as u32);\n" -" println!(\"B: {}\", Bar::B as u32);\n" -" println!(\"C: {}\", Bar::C as u32);\n" +"pub fn main() {\n" +" let p1 = Point(3, 4);\n" +" let p2 = Point(10, 20);\n" +" let p3 = add(&p1, &p2);\n" +" println!(\"&p3.0: {:p}\", &p3.0);\n" +" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"#[repr(u32)]\n" -"enum Bar {\n" -" A, // 0\n" -" B = 10000,\n" -" C, // 10001\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" +"\n" +"fn add(p1: &Point, p2: &Point) -> Point {\n" +" let p = Point(p1.0 + p2.0, p1.1 + p2.1);\n" +" println!(\"&p.0: {:p}\", &p.0);\n" +" p\n" "}\n" "\n" "fn main() {\n" -" println!(\"A: {}\", Bar::A as u32);\n" -" println!(\"B: {}\", Bar::B as u32);\n" -" println!(\"C: {}\", Bar::C as u32);\n" +" let p1 = Point(3, 4);\n" +" let p2 = Point(10, 20);\n" +" let p3 = add(&p1, &p2);\n" +" println!(\"&p3.0: {:p}\", &p3.0);\n" +" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" "}\n" "```" -#: src/enums/sizes.md:50 +#: src/ownership/borrowing.md:48 +msgid "The Rust compiler can do return value optimization (RVO)." +msgstr "Rust 编译器能够执行返回值优化 (RVO)。" + +#: src/ownership/borrowing.md:49 #, fuzzy msgid "" -"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " -"bytes." +"In C++, copy elision has to be defined in the language specification because " +"constructors can have side effects. In Rust, this is not an issue at all. If " +"RVO did not happen, Rust will always perform a simple and efficient `memcpy` " +"copy." msgstr "" -"如果不使用 `repr`,判别类型会占用 2 个字节,因为 10001 是一个 2 个字节的数" -"值。" +"在 C++ 中,必须在语言规范中定义复制省略,因为构造函数可能会有附带效应。在 " +"Rust 中,这完全不是问题。如果 RVO 未发生,Rust 将始终执行简单且高效的 " +"`memcpy` 复制。" -#: src/enums/sizes.md:54 -#, fuzzy -msgid "Try out other types such as" -msgstr "试试其他类型,例如:" +#: src/ownership/shared-unique-borrows.md:3 +msgid "Rust puts constraints on the ways you can borrow values:" +msgstr "Rust 限制了借用值的方式:" -#: src/enums/sizes.md:56 -#, fuzzy -msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," -msgstr "`dbg_size!(bool)`:大小占用 1 个字节,对齐占用 1 个字节;" +#: src/ownership/shared-unique-borrows.md:5 +msgid "You can have one or more `&T` values at any given time, _or_" +msgstr "在任何给定时间,你都可以有一个或多个 `&T` 值,或者" -#: src/enums/sizes.md:57 -#, fuzzy +#: src/ownership/shared-unique-borrows.md:6 +msgid "You can have exactly one `&mut T` value." +msgstr "你可以有且只有一个 `&mut T` 值。" + +#: src/ownership/shared-unique-borrows.md:8 msgid "" -"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " -"see below)," +"```rust,editable,compile_fail\n" +"fn main() {\n" +" let mut a: i32 = 10;\n" +" let b: &i32 = &a;\n" +"\n" +" {\n" +" let c: &mut i32 = &mut a;\n" +" *c = 20;\n" +" }\n" +"\n" +" println!(\"a: {a}\");\n" +" println!(\"b: {b}\");\n" +"}\n" +"```" msgstr "" -"`dbg_size!(Option)`:大小占用 1 个字节,对齐占用 1 个字节(小众优化,请" -"参阅下文);" +"```rust,editable,compile_fail\n" +"fn main() {\n" +" let mut a: i32 = 10;\n" +" let b: &i32 = &a;\n" +"\n" +" {\n" +" let c: &mut i32 = &mut a;\n" +" *c = 20;\n" +" }\n" +"\n" +" println!(\"a: {a}\");\n" +" println!(\"b: {b}\");\n" +"}\n" +"```" -#: src/enums/sizes.md:58 -#, fuzzy -msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," +#: src/ownership/shared-unique-borrows.md:25 +msgid "" +"The above code does not compile because `a` is borrowed as mutable (through " +"`c`) and as immutable (through `b`) at the same time." msgstr "" -"`dbg_size!(&i32)`:大小占用 8 个字节,对齐占用 8 个字节(在 64 位设备上);" +"上述代码无法编译,因为 `a` 同时作为可变值(通过 `c`)和不可变值(通过 `b`)被" +"借用。" -#: src/enums/sizes.md:59 -#, fuzzy +#: src/ownership/shared-unique-borrows.md:26 msgid "" -"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " -"optimization, see below)." +"Move the `println!` statement for `b` before the scope that introduces `c` " +"to make the code compile." msgstr "" -"`dbg_size!(Option<&i32>)`:大小占用 8 个字节,对齐占用 8 个字节(Null 指针优" -"化,请参阅下文)。" +"将`b` 的 `println!` 语句移到引入 `c` 的作用域之前,这段代码就可以编译。" -#: src/enums/sizes.md:61 -#, fuzzy +#: src/ownership/shared-unique-borrows.md:27 msgid "" -"Niche optimization: Rust will merge use unused bit patterns for the enum " -"discriminant." -msgstr "小众优化:Rust 将对枚举判别合并使用 未使用的位模式。" +"After that change, the compiler realizes that `b` is only ever used before " +"the new mutable borrow of `a` through `c`. This is a feature of the borrow " +"checker called \"non-lexical lifetimes\"." +msgstr "" +"这样更改后,编译器会发现 `b` 只在通过 `c` 对 `a` 进行新可变借用之前使用过。这" +"是借用检查器的一个功能,名为“非词法作用域生命周期”。" -#: src/enums/sizes.md:64 -#, fuzzy +#: src/ownership/lifetimes.md:3 +msgid "A borrowed value has a _lifetime_:" +msgstr "借用的值是有“生命周期”的:" + +#: src/ownership/lifetimes.md:5 +msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." +msgstr "生命周期可以是隐式的:add(p1: &Point, p2: &Point) -> Point\\`。" + +#: src/ownership/lifetimes.md:6 +msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." +msgstr "生命周期也可以是显式的:`&'a Point`、`&'document str`。" + +#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:23 msgid "" -"Null pointer optimization: For [some types](https://doc.rust-lang.org/std/" -"option/#representation), Rust guarantees that `size_of::()` equals " -"`size_of::>()`." -msgstr "" -"Null 指针优化:对于[某些 类型](https://doc.rust-lang.org/std/option/" -"#representation),Rust 保证 `size_of::()` 等效于 `size_of::" -">()`。" +"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " +"lifetime `a`\"." +msgstr "将 `&'a Point` 读取为“借用的 `Point,至少 在 `a\\` 生命周期内有效。" + +#: src/ownership/lifetimes.md:9 +msgid "" +"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " +"yourself." +msgstr "生命周期始终由编译器推断出来:你不能自行 分配生命周期。" + +#: src/ownership/lifetimes.md:11 +msgid "" +"Lifetime annotations create constraints; the compiler verifies that there is " +"a valid solution." +msgstr "生命周期注释会创建约束条件;编译器会验证 是否存在有效的解决方案。" -#: src/enums/sizes.md:68 +#: src/ownership/lifetimes.md:13 #, fuzzy msgid "" -"Example code if you want to show how the bitwise representation _may_ look " -"like in practice. It's important to note that the compiler provides no " -"guarantees regarding this representation, therefore this is totally unsafe." +"Lifetimes for function arguments and return values must be fully specified, " +"but Rust allows lifetimes to be elided in most cases with [a few simple " +"rules](https://doc.rust-lang.org/nomicon/lifetime-elision.html)." msgstr "" -"如果你想展示位表示方式在实践中“可能”会是什么样子,请参考示例代码。 请务必注" -"意,编译器对此表示法不提供任何保证,因此这是完全不安全的。" +"必须完全指定函数参数和返回值的生命周期, 但 Rust 允许在大多数情况下通过\\[一" +"些简单的 规则\\](https://doc.rust-lang.org/nomicon/lifetime-elision.html)来" +"省略此操作。" -#: src/enums/sizes.md:71 -#, fuzzy +#: src/ownership/lifetimes-function-calls.md:3 +msgid "" +"In addition to borrowing its arguments, a function can return a borrowed " +"value:" +msgstr "除了借用其参数之外,函数还可以返回借用的值:" + +#: src/ownership/lifetimes-function-calls.md:5 msgid "" "```rust,editable\n" -"use std::mem::transmute;\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"macro_rules! dbg_bits {\n" -" ($e:expr, $bit_type:ty) => {\n" -" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " -"$bit_type>($e));\n" -" };\n" +"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" +" if p1.0 < p2.0 { p1 } else { p2 }\n" "}\n" "\n" "fn main() {\n" -" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" -" // representation of types.\n" -" unsafe {\n" -" println!(\"Bitwise representation of bool\");\n" -" dbg_bits!(false, u8);\n" -" dbg_bits!(true, u8);\n" +" let p1: Point = Point(10, 10);\n" +" let p2: Point = Point(20, 20);\n" +" let p3: &Point = left_most(&p1, &p2);\n" +" println!(\"left-most point: {:?}\", p3);\n" +"}\n" +"```" +msgstr "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -" println!(\"Bitwise representation of Option\");\n" -" dbg_bits!(None::, u8);\n" -" dbg_bits!(Some(false), u8);\n" -" dbg_bits!(Some(true), u8);\n" -"\n" -" println!(\"Bitwise representation of Option>\");\n" -" dbg_bits!(Some(Some(false)), u8);\n" -" dbg_bits!(Some(Some(true)), u8);\n" -" dbg_bits!(Some(None::), u8);\n" -" dbg_bits!(None::>, u8);\n" -"\n" -" println!(\"Bitwise representation of Option<&i32>\");\n" -" dbg_bits!(None::<&i32>, usize);\n" -" dbg_bits!(Some(&0i32), usize);\n" -" }\n" +"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" +" if p1.0 < p2.0 { p1 } else { p2 }\n" "}\n" -"```" -msgstr "" -"```\n" -" ```rust,editable\n" -" use std::mem::transmute;\n" "\n" -" macro_rules! dbg_bits {\n" -" ($e:expr, $bit_type:ty) => {\n" -" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " -"$bit_type>($e));\n" -" };\n" -" }\n" -"\n" -" fn main() {\n" -" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" -" // representation of types.\n" -" unsafe {\n" -" println!(\"Bitwise representation of bool\");\n" -" dbg_bits!(false, u8);\n" -" dbg_bits!(true, u8);\n" -"\n" -" println!(\"Bitwise representation of Option\");\n" -" dbg_bits!(None::, u8);\n" -" dbg_bits!(Some(false), u8);\n" -" dbg_bits!(Some(true), u8);\n" -"\n" -" println!(\"Bitwise representation of Option>\");\n" -" dbg_bits!(Some(Some(false)), u8);\n" -" dbg_bits!(Some(Some(true)), u8);\n" -" dbg_bits!(Some(None::), u8);\n" -" dbg_bits!(None::>, u8);\n" -"\n" -" println!(\"Bitwise representation of Option<&i32>\");\n" -" dbg_bits!(None::<&i32>, usize);\n" -" dbg_bits!(Some(&0i32), usize);\n" -" }\n" -" }\n" -" ```\n" -"\n" -" 如果你想讨论当我们把 256 个以上的 `Option` 串联在一起时会发生什么,可以用更" -"复杂的示例。\n" -"\n" -" ```rust,editable\n" -" #![recursion_limit = \"1000\"]\n" -"\n" -" use std::mem::transmute;\n" -" \n" -" macro_rules! dbg_bits {\n" -" ($e:expr, $bit_type:ty) => {\n" -" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " -"$bit_type>($e));\n" -" };\n" -" }\n" -"\n" -" // 用于封装 2^n Some() 中的值的宏,其中 n 的 \"@\" 符号的个数。\n" -" // 增加递归限制需要评估这个宏。\n" -" macro_rules! many_options {\n" -" ($value:expr) => { Some($value) };\n" -" ($value:expr, @) => {\n" -" Some(Some($value))\n" -" };\n" -" ($value:expr, @ $($more:tt)+) => {\n" -" many_options!(many_options!($value, $($more)+), $($more)+)\n" -" };\n" -" }\n" -"\n" -" fn main() {\n" -" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" -" // representation of types.\n" -" unsafe {\n" -" assert_eq!(many_options!(false), Some(false));\n" -" assert_eq!(many_options!(false, @), Some(Some(false)));\n" -" assert_eq!(many_options!(false, @@), " -"Some(Some(Some(Some(false)))));\n" -"\n" -" println!(\"Bitwise representation of a chain of 128 Option's.\");\n" -" dbg_bits!(many_options!(false, @@@@@@@), u8);\n" -" dbg_bits!(many_options!(true, @@@@@@@), u8);\n" -"\n" -" println!(\"Bitwise representation of a chain of 256 Option's.\");\n" -" dbg_bits!(many_options!(false, @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(true, @@@@@@@@), u16);\n" -"\n" -" println!(\"Bitwise representation of a chain of 257 Option's.\");\n" -" dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(None::, @@@@@@@@), u16);\n" -" }\n" -" }\n" -" ```\n" +"fn main() {\n" +" let p1: Point = Point(10, 10);\n" +" let p2: Point = Point(20, 20);\n" +" let p3: &Point = left_most(&p1, &p2);\n" +" println!(\"left-most point: {:?}\", p3);\n" +"}\n" "```" -#: src/enums/sizes.md:106 +#: src/ownership/lifetimes-function-calls.md:21 +msgid "`'a` is a generic parameter, it is inferred by the compiler." +msgstr "`'a` 是一个泛型形参,由编译器推断出来。" + +#: src/ownership/lifetimes-function-calls.md:22 +msgid "Lifetimes start with `'` and `'a` is a typical default name." +msgstr "以 `'` 和 `'a` 开头的生命周期是典型的默认名称。" + +#: src/ownership/lifetimes-function-calls.md:25 msgid "" -"More complex example if you want to discuss what happens when we chain more " -"than 256 `Option`s together." -msgstr "" +"The _at least_ part is important when parameters are in different scopes." +msgstr "当参数在不同的作用域时,“至少”部分至关重要。" -#: src/enums/sizes.md:108 +#: src/ownership/lifetimes-function-calls.md:31 msgid "" -"```rust,editable\n" -"#![recursion_limit = \"1000\"]\n" +"Move the declaration of `p2` and `p3` into a new scope (`{ ... }`), " +"resulting in the following code:" +msgstr "将 `p2` 和 `p3` 的声明移至新作用域 (`{ ... }`),以产生以下代码:" + +#: src/ownership/lifetimes-function-calls.md:32 +msgid "" +"```rust,ignore\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"use std::mem::transmute;\n" +"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" +" if p1.0 < p2.0 { p1 } else { p2 }\n" +"}\n" "\n" -"macro_rules! dbg_bits {\n" -" ($e:expr, $bit_type:ty) => {\n" -" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " -"$bit_type>($e));\n" -" };\n" +"fn main() {\n" +" let p1: Point = Point(10, 10);\n" +" let p3: &Point;\n" +" {\n" +" let p2: Point = Point(20, 20);\n" +" p3 = left_most(&p1, &p2);\n" +" }\n" +" println!(\"left-most point: {:?}\", p3);\n" "}\n" +"```" +msgstr "" +"```rust,ignore\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"// Macro to wrap a value in 2^n Some() where n is the number of \"@\" " -"signs.\n" -"// Increasing the recursion limit is required to evaluate this macro.\n" -"macro_rules! many_options {\n" -" ($value:expr) => { Some($value) };\n" -" ($value:expr, @) => {\n" -" Some(Some($value))\n" -" };\n" -" ($value:expr, @ $($more:tt)+) => {\n" -" many_options!(many_options!($value, $($more)+), $($more)+)\n" -" };\n" +"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" +" if p1.0 < p2.0 { p1 } else { p2 }\n" "}\n" "\n" "fn main() {\n" -" // TOTALLY UNSAFE. Rust provides no guarantees about the bitwise\n" -" // representation of types.\n" -" unsafe {\n" -" assert_eq!(many_options!(false), Some(false));\n" -" assert_eq!(many_options!(false, @), Some(Some(false)));\n" -" assert_eq!(many_options!(false, @@), " -"Some(Some(Some(Some(false)))));\n" -"\n" -" println!(\"Bitwise representation of a chain of 128 Option's.\");\n" -" dbg_bits!(many_options!(false, @@@@@@@), u8);\n" -" dbg_bits!(many_options!(true, @@@@@@@), u8);\n" -"\n" -" println!(\"Bitwise representation of a chain of 256 Option's.\");\n" -" dbg_bits!(many_options!(false, @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(true, @@@@@@@@), u16);\n" -"\n" -" println!(\"Bitwise representation of a chain of 257 Option's.\");\n" -" dbg_bits!(many_options!(Some(false), @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(Some(true), @@@@@@@@), u16);\n" -" dbg_bits!(many_options!(None::, @@@@@@@@), u16);\n" +" let p1: Point = Point(10, 10);\n" +" let p3: &Point;\n" +" {\n" +" let p2: Point = Point(20, 20);\n" +" p3 = left_most(&p1, &p2);\n" " }\n" +" println!(\"left-most point: {:?}\", p3);\n" "}\n" "```" + +#: src/ownership/lifetimes-function-calls.md:50 +msgid "Note how this does not compile since `p3` outlives `p2`." +msgstr "请注意:由于 `p3` 的生命周期比 `p2` 长,因此无法编译。" + +#: src/ownership/lifetimes-function-calls.md:52 +msgid "" +"Reset the workspace and change the function signature to `fn left_most<'a, " +"'b>(p1: &'a Point, p2: &'a Point) -> &'b Point`. This will not compile " +"because the relationship between the lifetimes `'a` and `'b` is unclear." msgstr "" +"重置工作区,然后将函数签名更改为 `fn left_most<'a, 'b>(p1: &'a Point, p2: " +"&'a Point) -> &'b Point`。这不会被编译,因为 `'a` 和 `'b` 生命周期之间的关系" +"不明确。" -#: src/methods.md:3 +#: src/ownership/lifetimes-function-calls.md:53 +msgid "Another way to explain it:" +msgstr "另一种解释方式:" + +#: src/ownership/lifetimes-function-calls.md:54 msgid "" -"Rust allows you to associate functions with your new types. You do this with " -"an `impl` block:" +"Two references to two values are borrowed by a function and the function " +"returns another reference." +msgstr "对两个值的两个引用被一个函数借用,该函数返回 另一个引用。" + +#: src/ownership/lifetimes-function-calls.md:56 +msgid "" +"It must have come from one of those two inputs (or from a global variable)." +msgstr "它必须是来自这两个输入中的一个(或来自一个全局变量)。" + +#: src/ownership/lifetimes-function-calls.md:57 +msgid "" +"Which one is it? The compiler needs to know, so at the call site the " +"returned reference is not used for longer than a variable from where the " +"reference came from." msgstr "" +"是哪一个呢?编译器需要知道这一点,因此在调用点,返回的引用 的使用时间不会超过" +"引用的来源中的变量。" -#: src/methods.md:6 +#: src/ownership/lifetimes-data-structures.md:3 +msgid "" +"If a data type stores borrowed data, it must be annotated with a lifetime:" +msgstr "如果数据类型存储了借用的数据,则必须对其添加生命周期注释:" + +#: src/ownership/lifetimes-data-structures.md:5 msgid "" "```rust,editable\n" "#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" +"struct Highlight<'doc>(&'doc str);\n" "\n" -"impl Person {\n" -" fn say_hello(&self) {\n" -" println!(\"Hello, my name is {}\", self.name);\n" -" }\n" +"fn erase(text: String) {\n" +" println!(\"Bye {text}!\");\n" "}\n" "\n" "fn main() {\n" -" let peter = Person {\n" -" name: String::from(\"Peter\"),\n" -" age: 27,\n" -" };\n" -" peter.say_hello();\n" +" let text = String::from(\"The quick brown fox jumps over the lazy dog." +"\");\n" +" let fox = Highlight(&text[4..19]);\n" +" let dog = Highlight(&text[35..43]);\n" +" // erase(text);\n" +" println!(\"{fox:?}\");\n" +" println!(\"{dog:?}\");\n" "}\n" "```" msgstr "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Highlight<'doc>(&'doc str);\n" +"\n" +"fn erase(text: String) {\n" +" println!(\"Bye {text}!\");\n" +"}\n" +"\n" +"fn main() {\n" +" let text = String::from(\"The quick brown fox jumps over the lazy dog." +"\");\n" +" let fox = Highlight(&text[4..19]);\n" +" let dog = Highlight(&text[35..43]);\n" +" // erase(text);\n" +" println!(\"{fox:?}\");\n" +" println!(\"{dog:?}\");\n" +"}\n" +"```" -#: src/methods.md:31 -msgid "It can be helpful to introduce methods by comparing them to functions." +#: src/ownership/lifetimes-data-structures.md:25 +msgid "" +"In the above example, the annotation on `Highlight` enforces that the data " +"underlying the contained `&str` lives at least as long as any instance of " +"`Highlight` that uses that data." msgstr "" +"在上述示例中,`Highlight` 注释会强制包含 `&str` 的底层数据的生命周期至少与使" +"用该数据的任何 `Highlight` 实例一样长。" -#: src/methods.md:32 +#: src/ownership/lifetimes-data-structures.md:26 msgid "" -"Methods are called on an instance of a type (such as a struct or enum), the " -"first parameter represents the instance as `self`." +"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " +"the borrow checker throws an error." msgstr "" +"如果 `text` 在 `fox`(或 `dog`)的生命周期结束前被消耗,借用检查器将抛出一个" +"错误。" -#: src/methods.md:33 +#: src/ownership/lifetimes-data-structures.md:27 msgid "" -"Developers may choose to use methods to take advantage of method receiver " -"syntax and to help keep them more organized. By using methods we can keep " -"all the implementation code in one predictable place." +"Types with borrowed data force users to hold on to the original data. This " +"can be useful for creating lightweight views, but it generally makes them " +"somewhat harder to use." msgstr "" +"借用数据的类型会迫使用户保留原始数据。这对于创建轻量级视图很有用,但通常会使" +"它们更难使用。" -#: src/methods.md:34 -msgid "Point out the use of the keyword `self`, a method receiver. " -msgstr "" +#: src/ownership/lifetimes-data-structures.md:28 +msgid "When possible, make data structures own their data directly." +msgstr "如有可能,让数据结构直接拥有自己的数据。" -#: src/methods.md:35 +#: src/ownership/lifetimes-data-structures.md:29 msgid "" -"Show that it is an abbreviated term for `self:&Self` and perhaps show how " -"the struct name could also be used. " +"Some structs with multiple references inside can have more than one lifetime " +"annotation. This can be necessary if there is a need to describe lifetime " +"relationships between the references themselves, in addition to the lifetime " +"of the struct itself. Those are very advanced use cases." msgstr "" +"一些包含多个引用的结构可以有多个生命周期注释。除了结构体本身的生命周期之外," +"如果需要描述引用之间的生命周期关系,则可能需要这样做。这些都是非常高级的用" +"例。" -#: src/methods.md:36 -msgid "" -"Explain that `Self` is a type alias for the type the `impl` block is in and " -"can be used elsewhere in the block." -msgstr "" +#: src/structs.md:3 +msgid "Like C and C++, Rust has support for custom structs:" +msgstr "与 C 和 C++ 一样,Rust 支持自定义结构体:" -#: src/methods.md:37 +#: src/structs.md:5 msgid "" -"Note how `self` is used like other structs and dot notation can be used to " -"refer to individual fields." -msgstr "" - -#: src/methods.md:38 -msgid "" -"This might be a good time to demonstrate how the `&self` differs from `self` " -"by modifying the code and trying to run say_hello twice." +"```rust,editable\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" +"\n" +"fn main() {\n" +" let mut peter = Person {\n" +" name: String::from(\"Peter\"),\n" +" age: 27,\n" +" };\n" +" println!(\"{} is {} years old\", peter.name, peter.age);\n" +" \n" +" peter.age = 28;\n" +" println!(\"{} is {} years old\", peter.name, peter.age);\n" +" \n" +" let jackie = Person {\n" +" name: String::from(\"Jackie\"),\n" +" ..peter\n" +" };\n" +" println!(\"{} is {} years old\", jackie.name, jackie.age);\n" +"}\n" +"```" msgstr "" -#: src/methods.md:39 -msgid "We describe the distinction between method receivers next." +#: src/structs.md:33 +msgid "Structs work like in C or C++." msgstr "" -#: src/methods/receiver.md:3 -msgid "" -"The `&self` above indicates that the method borrows the object immutably. " -"There are other possible receivers for a method:" +#: src/structs.md:34 +msgid "Like in C++, and unlike in C, no typedef is needed to define a type." msgstr "" -#: src/methods/receiver.md:6 -msgid "" -"`&self`: borrows the object from the caller using a shared and immutable " -"reference. The object can be used again afterwards." +#: src/structs.md:35 +msgid "Unlike in C++, there is no inheritance between structs." msgstr "" -#: src/methods/receiver.md:8 +#: src/structs.md:36 msgid "" -"`&mut self`: borrows the object from the caller using a unique and mutable " -"reference. The object can be used again afterwards." +"Methods are defined in an `impl` block, which we will see in following " +"slides." msgstr "" -#: src/methods/receiver.md:10 +#: src/structs.md:37 msgid "" -"`self`: takes ownership of the object and moves it away from the caller. The " -"method becomes the owner of the object. The object will be dropped " -"(deallocated) when the method returns, unless its ownership is explicitly " -"transmitted. Complete ownership does not automatically mean mutability." -msgstr "" - -#: src/methods/receiver.md:14 -msgid "`mut self`: same as above, but the method can mutate the object. " +"This may be a good time to let people know there are different types of " +"structs. " msgstr "" -#: src/methods/receiver.md:15 +#: src/structs.md:38 msgid "" -"No receiver: this becomes a static method on the struct. Typically used to " -"create constructors which are called `new` by convention." +"Zero-sized structs `e.g., struct Foo;` might be used when implementing a " +"trait on some type but don’t have any data that you want to store in the " +"value itself. " msgstr "" -#: src/methods/receiver.md:18 +#: src/structs.md:39 msgid "" -"Beyond variants on `self`, there are also [special wrapper types](https://" -"doc.rust-lang.org/reference/special-types-and-traits.html) allowed to be " -"receiver types, such as `Box`." +"The next slide will introduce Tuple structs, used when the field names are " +"not important." msgstr "" -#: src/methods/receiver.md:24 +#: src/structs.md:40 msgid "" -"Consider emphasizing \"shared and immutable\" and \"unique and mutable\". " -"These constraints always come together in Rust due to borrow checker rules, " -"and `self` is no exception. It isn't possible to reference a struct from " -"multiple locations and call a mutating (`&mut self`) method on it." +"The syntax `..peter` allows us to copy the majority of the fields from the " +"old struct without having to explicitly type it all out. It must always be " +"the last element." msgstr "" -#: src/methods/example.md:3 +#: src/structs/tuple-structs.md:3 +msgid "If the field names are unimportant, you can use a tuple struct:" +msgstr "如果字段名称不重要,您可以使用元组结构体:" + +#: src/structs/tuple-structs.md:5 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Race {\n" -" name: String,\n" -" laps: Vec,\n" -"}\n" -"\n" -"impl Race {\n" -" fn new(name: &str) -> Race { // No receiver, a static method\n" -" Race { name: String::from(name), laps: Vec::new() }\n" -" }\n" +"struct Point(i32, i32);\n" "\n" -" fn add_lap(&mut self, lap: i32) { // Exclusive borrowed read-write " -"access to self\n" -" self.laps.push(lap);\n" -" }\n" +"fn main() {\n" +" let p = Point(17, 23);\n" +" println!(\"({}, {})\", p.0, p.1);\n" +"}\n" +"```" +msgstr "" + +#: src/structs/tuple-structs.md:14 +msgid "This is often used for single-field wrappers (called newtypes):" +msgstr "这通常用于单字段封装容器(称为 newtype):" + +#: src/structs/tuple-structs.md:16 +msgid "" +"```rust,editable,compile_fail\n" +"struct PoundsOfForce(f64);\n" +"struct Newtons(f64);\n" "\n" -" fn print_laps(&self) { // Shared and read-only borrowed access to self\n" -" println!(\"Recorded {} laps for {}:\", self.laps.len(), self.name);\n" -" for (idx, lap) in self.laps.iter().enumerate() {\n" -" println!(\"Lap {idx}: {lap} sec\");\n" -" }\n" -" }\n" +"fn compute_thruster_force() -> PoundsOfForce {\n" +" todo!(\"Ask a rocket scientist at NASA\")\n" +"}\n" "\n" -" fn finish(self) { // Exclusive ownership of self\n" -" let total = self.laps.iter().sum::();\n" -" println!(\"Race {} is finished, total lap time: {}\", self.name, " -"total);\n" -" }\n" +"fn set_thruster_force(force: Newtons) {\n" +" // ...\n" "}\n" "\n" "fn main() {\n" -" let mut race = Race::new(\"Monaco Grand Prix\");\n" -" race.add_lap(70);\n" -" race.add_lap(68);\n" -" race.print_laps();\n" -" race.add_lap(71);\n" -" race.print_laps();\n" -" race.finish();\n" -" // race.add_lap(42);\n" +" let force = compute_thruster_force();\n" +" set_thruster_force(force);\n" "}\n" +"\n" "```" msgstr "" -#: src/methods/example.md:47 -msgid "All four methods here use a different method receiver." -msgstr "" - -#: src/methods/example.md:48 +#: src/structs/tuple-structs.md:37 msgid "" -"You can point out how that changes what the function can do with the " -"variable values and if/how it can be used again in `main`." +"Newtypes are a great way to encode additional information about the value in " +"a primitive type, for example:" msgstr "" +"如需对基元类型中的值的额外信息进行编码,使用 newtype 是一种非常好的方式,例" +"如:" -#: src/methods/example.md:49 +#: src/structs/tuple-structs.md:38 +msgid "The number is measured in some units: `Newtons` in the example above." +msgstr "数字会以某些单位来衡量:上方示例中为 `Newtons`。" + +#: src/structs/tuple-structs.md:39 msgid "" -"You can showcase the error that appears when trying to call `finish` twice." +"The value passed some validation when it was created, so you no longer have " +"to validate it again at every use: 'PhoneNumber(String)`or`OddNumber(u32)\\`." msgstr "" +"值在创建时已通过一些验证,因此您不再需要在每次使用时都再次验证它:" +"`PhoneNumber(String)` 或 `OddNumber(u32)`。" -#: src/methods/example.md:50 +#: src/structs/tuple-structs.md:40 msgid "" -"Note that although the method receivers are different, the non-static " -"functions are called the same way in the main body. Rust enables automatic " -"referencing and dereferencing when calling methods. Rust automatically adds " -"in the `&`, `*`, `muts` so that that object matches the method signature." +"Demonstrate how to add a `f64` value to a `Newtons` type by accessing the " +"single field in the newtype." msgstr "" +"展示如何通过访问 newtype 中的单个字段,将 `f64` 值添加到 `Newtons` 类型。" -#: src/methods/example.md:51 +#: src/structs/tuple-structs.md:41 msgid "" -"You might point out that `print_laps` is using a vector that is iterated " -"over. We describe vectors in more detail in the afternoon. " -msgstr "" +"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " +"for instance using booleans as integers." +msgstr "Rust 通常不喜欢不明确的内容,例如自动解封或将布尔值用作整数。" -#: src/pattern-matching.md:3 +#: src/structs/tuple-structs.md:42 +msgid "Operator overloading is discussed on Day 3 (generics)." +msgstr "运算符过载在第 3 天(泛型)讨论。" + +#: src/structs/tuple-structs.md:43 msgid "" -"The `match` keyword let you match a value against one or more _patterns_. " -"The comparisons are done from top to bottom and the first match wins." +"The example is a subtle reference to the [Mars Climate Orbiter](https://en." +"wikipedia.org/wiki/Mars_Climate_Orbiter) failure." msgstr "" -"使用关键词 `match` 对一个值进行模式匹配。进行匹配时,会从上至下依次进行比较," -"并选定第一个匹配成功的结果。" +"此示例巧妙地引用了[火星气候探测者号](https://zh.wikipedia.org/wiki/" +"%E7%81%AB%E6%98%9F%E6%B0%A3%E5%80%99%E6%8E%A2%E6%B8%AC%E8%80%85%E8%99%9F) 的" +"失败事故。" -#: src/pattern-matching.md:6 -msgid "The patterns can be simple values, similarly to `switch` in C and C++:" -msgstr "模式 (pattern) 可以是简单的值,其用法类似于 C 与 C++ 中的 `switch` 。" +#: src/structs/field-shorthand.md:3 +msgid "" +"If you already have variables with the right names, then you can create the " +"struct using a shorthand:" +msgstr "如果您已有名称正确的变量,则可以使用简写形式创建结构体:" -#: src/pattern-matching.md:8 +#: src/structs/field-shorthand.md:6 msgid "" "```rust,editable\n" -"fn main() {\n" -" let input = 'x';\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" "\n" -" match input {\n" -" 'q' => println!(\"Quitting\"),\n" -" 'a' | 's' | 'w' | 'd' => println!(\"Moving around\"),\n" -" '0'..='9' => println!(\"Number input\"),\n" -" _ => println!(\"Something else\"),\n" +"impl Person {\n" +" fn new(name: String, age: u8) -> Person {\n" +" Person { name, age }\n" " }\n" "}\n" +"\n" +"fn main() {\n" +" let peter = Person::new(String::from(\"Peter\"), 27);\n" +" println!(\"{peter:?}\");\n" +"}\n" "```" msgstr "" -#: src/pattern-matching.md:21 -msgid "The `_` pattern is a wildcard pattern which matches any value." -msgstr "模式 `_` 是外卡 (wildcard) 模式。它可以匹配任何值。" - -#: src/pattern-matching.md:26 -#, fuzzy +#: src/structs/field-shorthand.md:27 msgid "" -"You might point out how some specific characters are being used when in a " -"pattern" +"The `new` function could be written using `Self` as a type, as it is " +"interchangeable with the struct type name" msgstr "" -"你可以解释一些用于表达模式的特殊字符的用法 \\*`|` 表示或 (or) \\*`..` 可以展" -"开为任意一个或多个值 \\*`1..=5` 代表了一个闭区间范围" -#: src/pattern-matching.md:27 -#, fuzzy -msgid "`|` as an `or`" +#: src/structs/field-shorthand.md:41 +msgid "" +"Implement the `Default` trait for the struct. Define some fields and use the " +"default values for the other fields." msgstr "" -"解释模式匹配中的绑定的原理可能会很有帮助。比如可以用一个变量替代外卡,或者去" -"除 `q` 外面的引号。" - -#: src/pattern-matching.md:28 -#, fuzzy -msgid "`..` can expand as much as it needs to be" -msgstr "你可以展示如何匹配一个引用。" -#: src/pattern-matching.md:29 -#, fuzzy -msgid "`1..=5` represents an inclusive range" +#: src/structs/field-shorthand.md:43 +msgid "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" +"impl Default for Person {\n" +" fn default() -> Person {\n" +" Person {\n" +" name: \"Bot\".to_string(),\n" +" age: 0,\n" +" }\n" +" }\n" +"}\n" +"fn create_default() {\n" +" let tmp = Person {\n" +" ..Person::default()\n" +" };\n" +" let tmp = Person {\n" +" name: \"Sam\".to_string(),\n" +" ..Person::default()\n" +" };\n" +"}\n" +"```" msgstr "" -"现在是一个讲解不可反驳 (irrefutable) 模式的好时机。因为这个术语可能会出现在错" -"误信息中。" -#: src/pattern-matching.md:30 -msgid "`_` is a wild card" +#: src/structs/field-shorthand.md:68 +msgid "Methods are defined in the `impl` block." msgstr "" -#: src/pattern-matching.md:31 +#: src/structs/field-shorthand.md:69 msgid "" -"It can be useful to show how binding works, by for instance replacing a " -"wildcard character with a variable, or removing the quotes around `q`." -msgstr "" - -#: src/pattern-matching.md:32 -msgid "You can demonstrate matching on a reference." +"Use struct update syntax to define a new structure using `peter`. Note that " +"the variable `peter` will no longer be accessible afterwards." msgstr "" -#: src/pattern-matching.md:33 +#: src/structs/field-shorthand.md:70 msgid "" -"This might be a good time to bring up the concept of irrefutable patterns, " -"as the term can show up in error messages." +"Use `{:#?}` when printing structs to request the `Debug` representation." msgstr "" -#: src/pattern-matching/destructuring-enums.md:3 +#: src/methods.md:3 msgid "" -"Patterns can also be used to bind variables to parts of your values. This is " -"how you inspect the structure of your types. Let us start with a simple " -"`enum` type:" +"Rust allows you to associate functions with your new types. You do this with " +"an `impl` block:" msgstr "" -#: src/pattern-matching/destructuring-enums.md:6 +#: src/methods.md:6 msgid "" "```rust,editable\n" -"enum Result {\n" -" Ok(i32),\n" -" Err(String),\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" "}\n" "\n" -"fn divide_in_two(n: i32) -> Result {\n" -" if n % 2 == 0 {\n" -" Result::Ok(n / 2)\n" -" } else {\n" -" Result::Err(format!(\"cannot divide {n} into two equal parts\"))\n" +"impl Person {\n" +" fn say_hello(&self) {\n" +" println!(\"Hello, my name is {}\", self.name);\n" " }\n" "}\n" "\n" "fn main() {\n" -" let n = 100;\n" -" match divide_in_two(n) {\n" -" Result::Ok(half) => println!(\"{n} divided in two is {half}\"),\n" -" Result::Err(msg) => println!(\"sorry, an error happened: {msg}\"),\n" -" }\n" +" let peter = Person {\n" +" name: String::from(\"Peter\"),\n" +" age: 27,\n" +" };\n" +" peter.say_hello();\n" "}\n" "```" msgstr "" -#: src/pattern-matching/destructuring-enums.md:29 -msgid "" -"Here we have used the arms to _destructure_ the `Result` value. In the first " -"arm, `half` is bound to the value inside the `Ok` variant. In the second " -"arm, `msg` is bound to the error message." +#: src/methods.md:31 +msgid "It can be helpful to introduce methods by comparing them to functions." msgstr "" -#: src/pattern-matching/destructuring-enums.md:36 +#: src/methods.md:32 msgid "" -"The `if`/`else` expression is returning an enum that is later unpacked with " -"a `match`." +"Methods are called on an instance of a type (such as a struct or enum), the " +"first parameter represents the instance as `self`." msgstr "" -#: src/pattern-matching/destructuring-enums.md:37 +#: src/methods.md:33 msgid "" -"You can try adding a third variant to the enum definition and displaying the " -"errors when running the code. Point out the places where your code is now " -"inexhaustive and how the compiler tries to give you hints." +"Developers may choose to use methods to take advantage of method receiver " +"syntax and to help keep them more organized. By using methods we can keep " +"all the implementation code in one predictable place." msgstr "" -#: src/pattern-matching/destructuring-structs.md:3 -msgid "You can also destructure `structs`:" +#: src/methods.md:34 +msgid "Point out the use of the keyword `self`, a method receiver." msgstr "" -#: src/pattern-matching/destructuring-structs.md:5 +#: src/methods.md:35 msgid "" -"```rust,editable\n" -"struct Foo {\n" -" x: (u32, u32),\n" -" y: u32,\n" -"}\n" -"\n" -"#[rustfmt::skip]\n" -"fn main() {\n" -" let foo = Foo { x: (1, 2), y: 3 };\n" -" match foo {\n" -" Foo { x: (1, b), y } => println!(\"x.0 = 1, b = {b}, y = {y}\"),\n" -" Foo { y: 2, x: i } => println!(\"y = 2, x = {i:?}\"),\n" -" Foo { y, .. } => println!(\"y = {y}, other fields were " -"ignored\"),\n" -" }\n" -"}\n" -"```" +"Show that it is an abbreviated term for `self: Self` and perhaps show how " +"the struct name could also be used." msgstr "" -#: src/pattern-matching/destructuring-structs.md:23 -msgid "Change the literal values in `foo` to match with the other patterns." +#: src/methods.md:36 +msgid "" +"Explain that `Self` is a type alias for the type the `impl` block is in and " +"can be used elsewhere in the block." msgstr "" -#: src/pattern-matching/destructuring-structs.md:24 -msgid "Add a new field to `Foo` and make changes to the pattern as needed." +#: src/methods.md:37 +msgid "" +"Note how `self` is used like other structs and dot notation can be used to " +"refer to individual fields." msgstr "" -#: src/pattern-matching/destructuring-structs.md:25 +#: src/methods.md:38 msgid "" -"The distinction between a capture and a constant expression can be hard to " -"spot. Try changing the `2` in the second arm to a variable, and see that it " -"subtly doesn't work. Change it to a `const` and see it working again." +"This might be a good time to demonstrate how the `&self` differs from `self` " +"by modifying the code and trying to run say_hello twice." msgstr "" -#: src/pattern-matching/destructuring-arrays.md:3 -msgid "" -"You can destructure arrays, tuples, and slices by matching on their elements:" -msgstr "你可以通过元素匹配来解构数组、元组和切片:" +#: src/methods.md:39 +msgid "We describe the distinction between method receivers next." +msgstr "" -#: src/pattern-matching/destructuring-arrays.md:5 +#: src/methods/receiver.md:3 msgid "" -"```rust,editable\n" -"#[rustfmt::skip]\n" -"fn main() {\n" -" let triple = [0, -2, 3];\n" -" println!(\"Tell me about {triple:?}\");\n" -" match triple {\n" -" [0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" -" [1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" -" _ => println!(\"All elements were ignored\"),\n" -" }\n" -"}\n" -"```" +"The `&self` above indicates that the method borrows the object immutably. " +"There are other possible receivers for a method:" msgstr "" -#: src/pattern-matching/destructuring-arrays.md:21 +#: src/methods/receiver.md:6 msgid "" -"Destructuring of slices of unknown length also works with patterns of fixed " -"length." -msgstr "对未知长度的切片进行解构也可以使用固定长度的模式。" +"`&self`: borrows the object from the caller using a shared and immutable " +"reference. The object can be used again afterwards." +msgstr "" -#: src/pattern-matching/destructuring-arrays.md:24 +#: src/methods/receiver.md:8 msgid "" -"```rust,editable\n" -"fn main() {\n" -" inspect(&[0, -2, 3]);\n" -" inspect(&[0, -2, 3, 4]);\n" -"}\n" -"\n" -"#[rustfmt::skip]\n" -"fn inspect(slice: &[i32]) {\n" -" println!(\"Tell me about {slice:?}\");\n" -" match slice {\n" -" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" -" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" -" _ => println!(\"All elements were ignored\"),\n" -" }\n" -"}\n" -"```" +"`&mut self`: borrows the object from the caller using a unique and mutable " +"reference. The object can be used again afterwards." msgstr "" -"```rust,editable\n" -"fn main() {\n" -" inspect(&[0, -2, 3]);\n" -" inspect(&[0, -2, 3, 4]);\n" -"}\n" -"\n" -"#[rustfmt::skip]\n" -"fn inspect(slice: &[i32]) {\n" -" println!(\"Tell me about {slice:?}\");\n" -" match slice {\n" -" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" -" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" -" _ => println!(\"All elements were ignored\"),\n" -" }\n" -"}\n" -"```" - -#: src/pattern-matching/destructuring-arrays.md:41 -msgid "Create a new pattern using `_` to represent an element. " -msgstr "使用 `_` 创建一个新的模式来代表一个元素。" - -#: src/pattern-matching/destructuring-arrays.md:42 -msgid "Add more values to the array." -msgstr "向数组中添加更多的值。" -#: src/pattern-matching/destructuring-arrays.md:43 +#: src/methods/receiver.md:10 msgid "" -"Point out that how `..` will expand to account for different number of " -"elements." -msgstr "指出 `..` 是如何扩展以适应不同数量的元素的。 " - -#: src/pattern-matching/destructuring-arrays.md:44 -msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" -msgstr "展示使用模式 `[.., b]` 和 `[a@..,b]` 来匹配切片的尾部。" +"`self`: takes ownership of the object and moves it away from the caller. The " +"method becomes the owner of the object. The object will be dropped " +"(deallocated) when the method returns, unless its ownership is explicitly " +"transmitted. Complete ownership does not automatically mean mutability." +msgstr "" -#: src/pattern-matching/match-guards.md:3 -msgid "" -"When matching, you can add a _guard_ to a pattern. This is an arbitrary " -"Boolean expression which will be executed if the pattern matches:" +#: src/methods/receiver.md:14 +msgid "`mut self`: same as above, but the method can mutate the object. " msgstr "" -#: src/pattern-matching/match-guards.md:6 +#: src/methods/receiver.md:15 msgid "" -"```rust,editable\n" -"#[rustfmt::skip]\n" -"fn main() {\n" -" let pair = (2, -2);\n" -" println!(\"Tell me about {pair:?}\");\n" -" match pair {\n" -" (x, y) if x == y => println!(\"These are twins\"),\n" -" (x, y) if x + y == 0 => println!(\"Antimatter, kaboom!\"),\n" -" (x, _) if x % 2 == 1 => println!(\"The first one is odd\"),\n" -" _ => println!(\"No correlation...\"),\n" -" }\n" -"}\n" -"```" +"No receiver: this becomes a static method on the struct. Typically used to " +"create constructors which are called `new` by convention." msgstr "" -#: src/pattern-matching/match-guards.md:23 +#: src/methods/receiver.md:18 msgid "" -"Match guards as a separate syntax feature are important and necessary when " -"we wish to concisely express more complex ideas than patterns alone would " -"allow." +"Beyond variants on `self`, there are also [special wrapper types](https://" +"doc.rust-lang.org/reference/special-types-and-traits.html) allowed to be " +"receiver types, such as `Box`." msgstr "" -#: src/pattern-matching/match-guards.md:24 +#: src/methods/receiver.md:24 msgid "" -"They are not the same as separate `if` expression inside of the match arm. " -"An `if` expression inside of the branch block (after `=>`) happens after the " -"match arm is selected. Failing the `if` condition inside of that block won't " -"result in other arms of the original `match` expression being considered." +"Consider emphasizing \"shared and immutable\" and \"unique and mutable\". " +"These constraints always come together in Rust due to borrow checker rules, " +"and `self` is no exception. It isn't possible to reference a struct from " +"multiple locations and call a mutating (`&mut self`) method on it." msgstr "" -#: src/pattern-matching/match-guards.md:26 -msgid "You can use the variables defined in the pattern in your if expression." +#: src/methods/example.md:3 +msgid "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Race {\n" +" name: String,\n" +" laps: Vec,\n" +"}\n" +"\n" +"impl Race {\n" +" fn new(name: &str) -> Race { // No receiver, a static method\n" +" Race { name: String::from(name), laps: Vec::new() }\n" +" }\n" +"\n" +" fn add_lap(&mut self, lap: i32) { // Exclusive borrowed read-write " +"access to self\n" +" self.laps.push(lap);\n" +" }\n" +"\n" +" fn print_laps(&self) { // Shared and read-only borrowed access to self\n" +" println!(\"Recorded {} laps for {}:\", self.laps.len(), self.name);\n" +" for (idx, lap) in self.laps.iter().enumerate() {\n" +" println!(\"Lap {idx}: {lap} sec\");\n" +" }\n" +" }\n" +"\n" +" fn finish(self) { // Exclusive ownership of self\n" +" let total = self.laps.iter().sum::();\n" +" println!(\"Race {} is finished, total lap time: {}\", self.name, " +"total);\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let mut race = Race::new(\"Monaco Grand Prix\");\n" +" race.add_lap(70);\n" +" race.add_lap(68);\n" +" race.print_laps();\n" +" race.add_lap(71);\n" +" race.print_laps();\n" +" race.finish();\n" +" // race.add_lap(42);\n" +"}\n" +"```" msgstr "" -#: src/pattern-matching/match-guards.md:27 +#: src/methods/example.md:47 +msgid "All four methods here use a different method receiver." +msgstr "" + +#: src/methods/example.md:48 msgid "" -"The condition defined in the guard applies to every expression in a pattern " -"with an `|`." +"You can point out how that changes what the function can do with the " +"variable values and if/how it can be used again in `main`." +msgstr "" + +#: src/methods/example.md:49 +msgid "" +"You can showcase the error that appears when trying to call `finish` twice." +msgstr "" + +#: src/methods/example.md:50 +msgid "" +"Note that although the method receivers are different, the non-static " +"functions are called the same way in the main body. Rust enables automatic " +"referencing and dereferencing when calling methods. Rust automatically adds " +"in the `&`, `*`, `muts` so that that object matches the method signature." +msgstr "" + +#: src/methods/example.md:51 +msgid "" +"You might point out that `print_laps` is using a vector that is iterated " +"over. We describe vectors in more detail in the afternoon. " msgstr "" #: src/exercises/day-2/morning.md:1 @@ -7265,12 +7565,130 @@ msgid "We will look at implementing methods in two contexts:" msgstr "我们将考虑以下两种场景:" #: src/exercises/day-2/morning.md:5 -msgid "Simple struct which tracks health statistics." -msgstr "实现一个简单的结构体中的方法,用于追踪健康统计数据。" +msgid "Storing books and querying the collection" +msgstr "" #: src/exercises/day-2/morning.md:7 -msgid "Multiple structs and enums for a drawing library." -msgstr "实现多个结构体和枚举中的方法,用于绘图库。" +msgid "Keeping track of health statistics for patients" +msgstr "" + +#: src/exercises/day-2/book-library.md:3 +msgid "" +"We will learn much more about structs and the `Vec` type tomorrow. For " +"now, you just need to know part of its API:" +msgstr "" + +#: src/exercises/day-2/book-library.md:6 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" let mut vec = vec![10, 20];\n" +" vec.push(30);\n" +" let midpoint = vec.len() / 2;\n" +" println!(\"middle value: {}\", vec[midpoint]);\n" +" for item in &vec {\n" +" println!(\"item: {item}\");\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-2/book-library.md:18 +#, fuzzy +msgid "" +"Use this to model a library's book collection. Copy the code below to " +" and update the types to make it compile:" +msgstr "将下面的代码复制到 并实现上述函数:" + +#: src/exercises/day-2/book-library.md:21 +msgid "" +"```rust,should_panic\n" +"struct Library {\n" +" books: Vec,\n" +"}\n" +"\n" +"struct Book {\n" +" title: String,\n" +" year: u16,\n" +"}\n" +"\n" +"impl Book {\n" +" // This is a constructor, used below.\n" +" fn new(title: &str, year: u16) -> Book {\n" +" Book {\n" +" title: String::from(title),\n" +" year,\n" +" }\n" +" }\n" +"}\n" +"\n" +"// Implement the methods below. Notice how the `self` parameter\n" +"// changes type to indicate the method's required level of ownership\n" +"// over the object:\n" +"//\n" +"// - `&self` for shared read-only access,\n" +"// - `&mut self` for unique and mutable access,\n" +"// - `self` for unique access by value.\n" +"impl Library {\n" +" fn new() -> Library {\n" +" todo!(\"Initialize and return a `Library` value\")\n" +" }\n" +"\n" +" fn len(&self) -> usize {\n" +" todo!(\"Return the length of `self.books`\")\n" +" }\n" +"\n" +" fn is_empty(&self) -> bool {\n" +" todo!(\"Return `true` if `self.books` is empty\")\n" +" }\n" +"\n" +" fn add_book(&mut self, book: Book) {\n" +" todo!(\"Add a new book to `self.books`\")\n" +" }\n" +"\n" +" fn print_books(&self) {\n" +" todo!(\"Iterate over `self.books` and print each book's title and " +"year\")\n" +" }\n" +"\n" +" fn oldest_book(&self) -> Option<&Book> {\n" +" todo!(\"Return a reference to the oldest book (if any)\")\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let mut library = Library::new();\n" +"\n" +" println!(\n" +" \"The library is empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +"\n" +" println!(\n" +" \"The library is no longer empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.print_books();\n" +"\n" +" match library.oldest_book() {\n" +" Some(book) => println!(\"The oldest book is {}\", book.title),\n" +" None => println!(\"The library is empty!\"),\n" +" }\n" +"\n" +" println!(\"The library has {} books\", library.len());\n" +" library.print_books();\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-2/book-library.md:104 +msgid "[Solution](solutions-afternoon.md#designing-a-library)" +msgstr "" #: src/exercises/day-2/health-statistics.md:3 msgid "" @@ -7296,19 +7714,34 @@ msgid "" msgstr "将以下代码复制到 ,并填充缺失的方法:" #: src/exercises/day-2/health-statistics.md:13 +#, fuzzy msgid "" "```rust,should_panic\n" "// TODO: remove this when you're done with your implementation.\n" "#![allow(unused_variables, dead_code)]\n" "\n" -"struct User {\n" +"pub struct User {\n" " name: String,\n" " age: u32,\n" -" weight: f32,\n" +" height: f32,\n" +" visit_count: usize,\n" +" last_blood_pressure: Option<(u32, u32)>,\n" +"}\n" +"\n" +"pub struct Measurements {\n" +" height: f32,\n" +" blood_pressure: (u32, u32),\n" +"}\n" +"\n" +"pub struct HealthReport<'a> {\n" +" patient_name: &'a str,\n" +" visit_count: u32,\n" +" height_change: f32,\n" +" blood_pressure_change: Option<(i32, i32)>,\n" "}\n" "\n" "impl User {\n" -" pub fn new(name: String, age: u32, weight: f32) -> Self {\n" +" pub fn new(name: String, age: u32, height: f32) -> Self {\n" " unimplemented!()\n" " }\n" "\n" @@ -7320,7 +7753,11 @@ msgid "" " unimplemented!()\n" " }\n" "\n" -" pub fn weight(&self) -> f32 {\n" +" pub fn height(&self) -> f32 {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn doctor_visits(&self) -> u32 {\n" " unimplemented!()\n" " }\n" "\n" @@ -7328,7 +7765,12 @@ msgid "" " unimplemented!()\n" " }\n" "\n" -" pub fn set_weight(&mut self, new_weight: f32) {\n" +" pub fn set_height(&mut self, new_height: f32) {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn visit_doctor(&mut self, measurements: Measurements) -> " +"HealthReport {\n" " unimplemented!()\n" " }\n" "}\n" @@ -7339,9 +7781,9 @@ msgid "" "}\n" "\n" "#[test]\n" -"fn test_weight() {\n" +"fn test_height() {\n" " let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" -" assert_eq!(bob.weight(), 155.2);\n" +" assert_eq!(bob.height(), 155.2);\n" "}\n" "\n" "#[test]\n" @@ -7351,6 +7793,27 @@ msgid "" " bob.set_age(33);\n" " assert_eq!(bob.age(), 33);\n" "}\n" +"\n" +"#[test]\n" +"fn test_visit() {\n" +" let mut bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" +" assert_eq!(bob.doctor_visits(), 0);\n" +" let report = bob.visit_doctor(Measurements {\n" +" height: 156.1,\n" +" blood_pressure: (120, 80),\n" +" });\n" +" assert_eq!(report.patient_name, \"Bob\");\n" +" assert_eq!(report.visit_count, 1);\n" +" assert_eq!(report.blood_pressure_change, None);\n" +"\n" +" let report = bob.visit_doctor(Measurements {\n" +" height: 156.1,\n" +" blood_pressure: (115, 76),\n" +" });\n" +"\n" +" assert_eq!(report.visit_count, 2);\n" +" assert_eq!(report.blood_pressure_change, Some((-5, -4)));\n" +"}\n" "```" msgstr "" "```rust,should_panic\n" @@ -7409,5598 +7872,4818 @@ msgstr "" "}\n" "```" -#: src/exercises/day-2/points-polygons.md:1 -msgid "Polygon Struct" +#: src/std.md:3 +msgid "" +"Rust comes with a standard library which helps establish a set of common " +"types used by Rust library and programs. This way, two libraries can work " +"together smoothly because they both use the same `String` type." msgstr "" +"Rust 附带一个标准库,此库有助于建立一个供 Rust 库和程序 使用的常用类型集。这" +"样一来,两个库便可顺畅地搭配运作, 因为它们使用相同的 `String` 类型。" -#: src/exercises/day-2/points-polygons.md:3 +#: src/std.md:7 +msgid "The common vocabulary types include:" +msgstr "常见的词汇类型包括:" + +#: src/std.md:9 msgid "" -"We will create a `Polygon` struct which contain some points. Copy the code " -"below to and fill in the missing methods to " -"make the tests pass:" +"[`Option` and `Result`](std/option-result.md) types: used for optional " +"values and [error handling](error-handling.md)." msgstr "" +"[`Option` 和 `Result`](std/option-result.md) 类型:用于可选值和 [错误处理]" +"(error-handling.md)。" + +#: src/std.md:12 +msgid "[`String`](std/string.md): the default string type used for owned data." +msgstr "[`String`](std/string.md):用于自有数据的默认字符串类型。" -#: src/exercises/day-2/points-polygons.md:7 +#: src/std.md:14 +msgid "[`Vec`](std/vec.md): a standard extensible vector." +msgstr "[`Vec`](std/vec.md):标准的可扩展矢量。" + +#: src/std.md:16 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub struct Point {\n" -" // add fields\n" -"}\n" -"\n" -"impl Point {\n" -" // add methods\n" -"}\n" -"\n" -"pub struct Polygon {\n" -" // add fields\n" -"}\n" -"\n" -"impl Polygon {\n" -" // add methods\n" -"}\n" -"\n" -"pub struct Circle {\n" -" // add fields\n" -"}\n" -"\n" -"impl Circle {\n" -" // add methods\n" -"}\n" -"\n" -"pub enum Shape {\n" -" Polygon(Polygon),\n" -" Circle(Circle),\n" -"}\n" -"\n" -"#[cfg(test)]\n" -"mod tests {\n" -" use super::*;\n" -"\n" -" fn round_two_digits(x: f64) -> f64 {\n" -" (x * 100.0).round() / 100.0\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_magnitude() {\n" -" let p1 = Point::new(12, 13);\n" -" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_dist() {\n" -" let p1 = Point::new(10, 10);\n" -" let p2 = Point::new(14, 13);\n" -" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_add() {\n" -" let p1 = Point::new(16, 16);\n" -" let p2 = p1 + Point::new(-4, 3);\n" -" assert_eq!(p2, Point::new(12, 19));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_left_most_point() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -" assert_eq!(poly.left_most_point(), Some(p1));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_iter() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -"\n" -" let points = poly.iter().cloned().collect::>();\n" -" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_shape_perimeters() {\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(Point::new(12, 13));\n" -" poly.add_point(Point::new(17, 11));\n" -" poly.add_point(Point::new(16, 16));\n" -" let shapes = vec![\n" -" Shape::from(poly),\n" -" Shape::from(Circle::new(Point::new(10, 20), 5)),\n" -" ];\n" -" let perimeters = shapes\n" -" .iter()\n" -" .map(Shape::perimeter)\n" -" .map(round_two_digits)\n" -" .collect::>();\n" -" assert_eq!(perimeters, vec![15.48, 31.42]);\n" -" }\n" -"}\n" -"\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" -"```" -msgstr "" - -#: src/exercises/day-2/points-polygons.md:117 -msgid "" -"Since the method signatures are missing from the problem statements, the key " -"part of the exercise is to specify those correctly. You don't have to modify " -"the tests." -msgstr "" +"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " +"algorithm." +msgstr "[`HashMap`](std/hashmap.md):采用可配置哈希算法的哈希映射 类型。" -#: src/exercises/day-2/points-polygons.md:120 -msgid "Other interesting parts of the exercise:" -msgstr "" +#: src/std.md:19 +msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." +msgstr "[`Box`](std/box.md):适用于堆分配数据的自有指针。" -#: src/exercises/day-2/points-polygons.md:122 +#: src/std.md:21 msgid "" -"Derive a `Copy` trait for some structs, as in tests the methods sometimes " -"don't borrow their arguments." -msgstr "" +"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " +"data." +msgstr "[`Rc`](std/rc.md):适用于堆分配数据的共享引用计数指针。" -#: src/exercises/day-2/points-polygons.md:123 +#: src/std.md:25 msgid "" -"Discover that `Add` trait must be implemented for two objects to be addable " -"via \"+\". Note that we do not discuss generics until Day 3." -msgstr "" +"In fact, Rust contains several layers of the Standard Library: `core`, " +"`alloc` and `std`. " +msgstr "Rust 实际上含有多个层级的标准库,分别是 `core`、`alloc` 和 `std`。 " -#: src/control-flow.md:3 +#: src/std.md:26 msgid "" -"As we have seen, `if` is an expression in Rust. It is used to conditionally " -"evaluate one of two blocks, but the blocks can have a value which then " -"becomes the value of the `if` expression. Other control flow expressions " -"work similarly in Rust." +"`core` includes the most basic types and functions that don't depend on " +"`libc`, allocator or even the presence of an operating system. " msgstr "" -"正如我们所知,`if` 是 Rust 中的一个表达式。它用于有条件地 评估两个块中的一" -"个,但这些块可以有一个值, 然后成为 `if` 表达式的值。其他控制流表达式在 Rust " -"中也有类似 的运作方式。" +"`core` 包括最基本的类型与函数,这些类型与函数不依赖于 `libc`、分配器 或是否存" +"在操作系统。 " -#: src/control-flow/blocks.md:3 +#: src/std.md:28 msgid "" -"A block in Rust has a value and a type: the value is the last expression of " -"the block:" -msgstr "Rust 中的块包含值和类型:值是 块的最后一个表达式:" +"`alloc` includes types which require a global heap allocator, such as `Vec`, " +"`Box` and `Arc`." +msgstr "`alloc` 包括需要全局堆分配器的类型,例如 `Vec`、`Box` 和 `Arc`。" -#: src/control-flow/blocks.md:6 +#: src/std.md:29 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let x = {\n" -" let y = 10;\n" -" println!(\"y: {y}\");\n" -" let z = {\n" -" let w = {\n" -" 3 + 4\n" -" };\n" -" println!(\"w: {w}\");\n" -" y * w\n" -" };\n" -" println!(\"z: {z}\");\n" -" z - y\n" -" };\n" -" println!(\"x: {x}\");\n" -"}\n" -"```" -msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let x = {\n" -" let y = 10;\n" -" println!(\"y: {y}\");\n" -" let z = {\n" -" let w = {\n" -" 3 + 4\n" -" };\n" -" println!(\"w: {w}\");\n" -" y * w\n" -" };\n" -" println!(\"z: {z}\");\n" -" z - y\n" -" };\n" -" println!(\"x: {x}\");\n" -"}\n" -"```" +"Embedded Rust applications often only use `core`, and sometimes `alloc`." +msgstr "嵌入式 Rust 应用通常只使用 `core`,偶尔会使用 `alloc`。" -#: src/control-flow/blocks.md:25 -msgid "" -"The same rule is used for functions: the value of the function body is the " -"return value:" -msgstr "同样的规则也适用于函数:函数主体的值 是返回值:" +#: src/std/option-result.md:1 +msgid "`Option` and `Result`" +msgstr "`Option` 和 `Result`" -#: src/control-flow/blocks.md:28 +#: src/std/option-result.md:3 +msgid "The types represent optional data:" +msgstr "这些类型表示可选数据:" + +#: src/std/option-result.md:5 +#, fuzzy msgid "" "```rust,editable\n" -"fn double(x: i32) -> i32 {\n" -" x + x\n" -"}\n" -"\n" "fn main() {\n" -" println!(\"doubled: {}\", double(7));\n" +" let numbers = vec![10, 20, 30];\n" +" let first: Option<&i8> = numbers.first();\n" +" println!(\"first: {first:?}\");\n" +"\n" +" let arr: Result<[i8; 3], Vec> = numbers.try_into();\n" +" println!(\"arr: {arr:?}\");\n" "}\n" "```" msgstr "" "```rust,editable\n" -"fn double(x: i32) -> i32 {\n" -" x + x\n" -"}\n" -"\n" "fn main() {\n" -" println!(\"doubled: {}\", double(7));\n" +" let v = vec![10, 20, 30];\n" +" let mut iter = v.into_iter();\n" +"\n" +" while let Some(x) = iter.next() {\n" +" println!(\"x: {x}\");\n" +" }\n" "}\n" "```" -#: src/control-flow/blocks.md:38 +#: src/std/option-result.md:18 +msgid "`Option` and `Result` are widely used not just in the standard library." +msgstr "`Option` 和 `Result` 的使用范围很广,不局限于标准库。" + +#: src/std/option-result.md:19 +msgid "`Option<&T>` has zero space overhead compared to `&T`." +msgstr "相较于 `&T`,`Option<&T>` 的空间开销为零。" + +#: src/std/option-result.md:20 msgid "" -"However if the last expression ends with `;`, then the resulting value and " -"type is `()`." -msgstr "不过,如果最后一个表达式以 `;` 结尾,那么生成的值和类型为 `()`。" +"`Result` is the standard type to implement error handling as we will see on " +"Day 3." +msgstr "`Result` 是用于实现错误处理的标准类型,我们将在第 3 天的课程中介绍。" -#: src/control-flow/blocks.md:43 +#: src/std/option-result.md:21 msgid "" -"The point of this slide is to show that blocks have a type and value in " -"Rust. " -msgstr "这张幻灯片的重点是说明在 Rust 中,块有类型和值。" +"`try_into` attempts to convert the vector into a fixed-sized array. This can " +"fail:" +msgstr "" -#: src/control-flow/blocks.md:44 +#: src/std/option-result.md:22 msgid "" -"You can show how the value of the block changes by changing the last line in " -"the block. For instance, adding/removing a semicolon or using a `return`." +"If the vector has the right size, `Result::Ok` is returned with the array." msgstr "" -"你可以通过更改块的最后一行,来展示块值的变化情况。例如,添加/移除分号或使用 " -"`return`。" -#: src/control-flow/if-expressions.md:1 -msgid "`if` expressions" -msgstr "`if` 表达式" +#: src/std/option-result.md:23 +msgid "Otherwise, `Result::Err` is returned with the original vector." +msgstr "" -#: src/control-flow/if-expressions.md:3 +#: src/std/string.md:3 msgid "" -"You use [`if` expressions](https://doc.rust-lang.org/reference/expressions/" -"if-expr.html#if-expressions) exactly like `if` statements in other languages:" +"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " +"standard heap-allocated growable UTF-8 string buffer:" msgstr "" -"[`if` 表达式](https://doc.rust-lang.org/reference/expressions/if-expr." -"html#if-expressions) 的用法与其他语言中的 `if` 语句完全一样。" +"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) 是标准堆" +"分配的可扩容 UTF-8 字符串缓冲区:" -#: src/control-flow/if-expressions.md:7 +#: src/std/string.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let mut x = 10;\n" -" if x % 2 == 0 {\n" -" x = x / 2;\n" -" } else {\n" -" x = 3 * x + 1;\n" -" }\n" -"}\n" -"```" -msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" if x % 2 == 0 {\n" -" x = x / 2;\n" -" } else {\n" -" x = 3 * x + 1;\n" -" }\n" -"}\n" -"```" - -#: src/control-flow/if-expressions.md:18 -msgid "" -"In addition, you can use `if` as an expression. The last expression of each " -"block becomes the value of the `if` expression:" -msgstr "" -"此外,你还可以将 `if` 用作一个表达式。每个块的最后一个表达式 将成为 `if` 表达" -"式的值:" - -#: src/control-flow/if-expressions.md:22 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" +" let mut s1 = String::new();\n" +" s1.push_str(\"Hello\");\n" +" println!(\"s1: len = {}, capacity = {}\", s1.len(), s1.capacity());\n" +"\n" +" let mut s2 = String::with_capacity(s1.len() + 1);\n" +" s2.push_str(&s1);\n" +" s2.push('!');\n" +" println!(\"s2: len = {}, capacity = {}\", s2.len(), s2.capacity());\n" +"\n" +" let s3 = String::from(\"🇨🇭\");\n" +" println!(\"s3: len = {}, number of chars = {}\", s3.len(),\n" +" s3.chars().count());\n" "}\n" "```" msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -"}\n" -"```" -#: src/control-flow/if-expressions.md:35 +#: src/std/string.md:22 msgid "" -"Because `if` is an expression and must have a particular type, both of its " -"branch blocks must have the same type. Consider showing what happens if you " -"add `;` after `x / 2` in the second example." +"`String` implements [`Deref`](https://doc.rust-lang.org/std/" +"string/struct.String.html#deref-methods-str), which means that you can call " +"all `str` methods on a `String`." msgstr "" -"由于 `if` 是一个表达式且必须有一个特定的类型,因此它的两个分支块必须有相同的" -"类型。考虑在第二个示例中将 `;` 添加到 `x / 2` 的后面,看看会出现什么情况。" - -#: src/control-flow/if-let-expressions.md:1 -msgid "`if let` expressions" -msgstr "`if let` 表达式" +"`String` 会实现 [`Deref`](https://doc.rust-lang.org/std/string/" +"struct.String.html#deref-methods-str),这意味着您可以 对 `String` 调用所有 " +"`str` 方法。" -#: src/control-flow/if-let-expressions.md:3 +#: src/std/string.md:30 msgid "" -"The [`if let` expression](https://doc.rust-lang.org/reference/expressions/if-" -"expr.html#if-let-expressions) lets you execute different code depending on " -"whether a value matches a pattern:" +"`String::new` returns a new empty string, use `String::with_capacity` when " +"you know how much data you want to push to the string." msgstr "" -"[`if let` 表达式](https://doc.rust-lang.org/reference/expressions/if-expr." -"html#if-let-expressions) 能让你根据某个值是否与模式相匹配来执行不同的代码:" -#: src/control-flow/if-let-expressions.md:7 +#: src/std/string.md:31 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let arg = std::env::args().next();\n" -" if let Some(value) = arg {\n" -" println!(\"Program name: {value}\");\n" -" } else {\n" -" println!(\"Missing name?\");\n" -" }\n" -"}\n" -"```" +"`String::len` returns the size of the `String` in bytes (which can be " +"different from its length in characters)." msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let arg = std::env::args().next();\n" -" if let Some(value) = arg {\n" -" println!(\"Program name: {value}\");\n" -" } else {\n" -" println!(\"Missing name?\");\n" -" }\n" -"}\n" -"```" -#: src/control-flow/if-let-expressions.md:18 -#: src/control-flow/while-let-expressions.md:21 -#: src/control-flow/match-expressions.md:23 +#: src/std/string.md:32 msgid "" -"See [pattern matching](../pattern-matching.md) for more details on patterns " -"in Rust." +"`String::chars` returns an iterator over the actual characters. Note that a " +"`char` can be different from what a human will consider a \"character\" due " +"to [grapheme clusters](https://docs.rs/unicode-segmentation/latest/" +"unicode_segmentation/struct.Graphemes.html)." msgstr "" -"如需详细了解 Rust 中 的模式,请参阅[模式匹配](../pattern-matching.md)。" -#: src/control-flow/if-let-expressions.md:23 +#: src/std/string.md:33 msgid "" -"`if let` can be more concise than `match`, e.g., when only one case is " -"interesting. In contrast, `match` requires all branches to be covered." +"When people refer to strings they could either be talking about `&str` or " +"`String`." msgstr "" -"`if let` 可能比 `match` 更简洁,例如,当只关注一种情况时。相比之下,`match` " -"要求覆盖所有分支。" - -#: src/control-flow/if-let-expressions.md:24 -msgid "A common usage is handling `Some` values when working with `Option`." -msgstr "使用 `Option` 时,常见的做法是处理 `Some` 值。" -#: src/control-flow/if-let-expressions.md:25 +#: src/std/string.md:34 msgid "" -"Unlike `match`, `if let` does not support guard clauses for pattern matching." -msgstr "与 `match` 不同的是,`if let` 不支持模式匹配的 guard 子句。" +"When a type implements `Deref`, the compiler will let you " +"transparently call methods from `T`." +msgstr "" -#: src/control-flow/if-let-expressions.md:26 +#: src/std/string.md:35 msgid "" -"Since 1.65, a similar [let-else](https://doc.rust-lang.org/rust-by-example/" -"flow_control/let_else.html) construct allows to do a destructuring " -"assignment, or if it fails, have a non-returning block branch (panic/return/" -"break/continue):" +"`String` implements `Deref` which transparently gives it " +"access to `str`'s methods." msgstr "" -"自 1.65 版以来,类似的 [let-else](https://doc.rust-lang.org/rust-by-example/" -"flow_control/let_else.html) 结构允许执行解构赋值,或者如果不满足条件,则有一" -"个非返回块分支 (panic/return/break/continue):" -#: src/control-flow/if-let-expressions.md:28 +#: src/std/string.md:36 +msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;." +msgstr "" + +#: src/std/string.md:37 msgid "" -"```rust,editable\n" -"fn main() {\n" -" println!(\"{:?}\", second_word_to_upper(\"foo bar\"));\n" -"}\n" -" \n" -"fn second_word_to_upper(s: &str) -> Option {\n" -" let mut it = s.split(' ');\n" -" let (Some(_), Some(item)) = (it.next(), it.next()) else {\n" -" return None;\n" -" };\n" -" Some(item.to_uppercase())\n" -"}\n" -"```" +"`String` is implemented as a wrapper around a vector of bytes, many of the " +"operations you see supported on vectors are also supported on `String`, but " +"with some extra guarantees." msgstr "" -"```rust,editable\n" -"fn main() {\n" -" println!(\"{:?}\", second_word_to_upper(\"foo bar\"));\n" -"}\n" -" \n" -"fn second_word_to_upper(s: &str) -> Option {\n" -" let mut it = s.split(' ');\n" -" let (Some(_), Some(item)) = (it.next(), it.next()) else {\n" -" return None;\n" -" };\n" -" Some(item.to_uppercase())\n" -"}\n" -"```" -#: src/control-flow/while-expressions.md:1 -msgid "`while` loops" -msgstr "`while` 循环" +#: src/std/string.md:38 +msgid "Compare the different ways to index a `String`:" +msgstr "" -#: src/control-flow/while-expressions.md:3 +#: src/std/string.md:39 msgid "" -"The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-" -"expr.html#predicate-loops) works very similar to other languages:" +"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " +"out-of-bounds." msgstr "" -"[`while` 关键字](https://doc.rust-lang.org/reference/expressions/loop-expr." -"html#predicate-loops) 的工作方式与其他语言非常相似:" -#: src/control-flow/while-expressions.md:6 +#: src/std/string.md:40 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" while x != 1 {\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -" }\n" -" println!(\"Final x: {x}\");\n" -"}\n" -"```" +"To a substring by using `s3[0..4]`, where that slice is on character " +"boundaries or not." msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" while x != 1 {\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -" }\n" -" println!(\"Final x: {x}\");\n" -"}\n" -"```" -#: src/control-flow/while-let-expressions.md:1 -msgid "`while let` loops" -msgstr "`while let` 循环" +#: src/std/vec.md:1 +msgid "`Vec`" +msgstr "`Vec`" -#: src/control-flow/while-let-expressions.md:3 +#: src/std/vec.md:3 msgid "" -"Like with `if let`, there is a [`while let`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#predicate-pattern-loops) variant which " -"repeatedly tests a value against a pattern:" +"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " +"resizable heap-allocated buffer:" msgstr "" -"与 `if let` 一样,[`with let`](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#predicate-pattern-loops) 变体会针对一个模式重复测" -"试一个值:" +"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) 是标准的可调整大小" +"堆分配缓冲区:" -#: src/control-flow/while-let-expressions.md:6 +#: src/std/vec.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" +" let mut v1 = Vec::new();\n" +" v1.push(42);\n" +" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n" "\n" -" while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\n" -" }\n" -"}\n" -"```" -msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" +" let mut v2 = Vec::with_capacity(v1.len() + 1);\n" +" v2.extend(v1.iter());\n" +" v2.push(9999);\n" +" println!(\"v2: len = {}, capacity = {}\", v2.len(), v2.capacity());\n" "\n" -" while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\n" -" }\n" +" // Canonical macro to initialize a vector with elements.\n" +" let mut v3 = vec![0, 0, 1, 2, 3, 4];\n" +"\n" +" // Retain only the even elements.\n" +" v3.retain(|x| x % 2 == 0);\n" +" println!(\"{v3:?}\");\n" +"\n" +" // Remove consecutive duplicates.\n" +" v3.dedup();\n" +" println!(\"{v3:?}\");\n" "}\n" "```" - -#: src/control-flow/while-let-expressions.md:17 -msgid "" -"Here the iterator returned by `v.iter()` will return a `Option` on " -"every call to `next()`. It returns `Some(x)` until it is done, after which " -"it will return `None`. The `while let` lets us keep iterating through all " -"items." msgstr "" -"在这里,每次 调用 `next()` 时,`v.iter()` 返回的迭代器都会返回一个 " -"`Option`。它将一直返回 `Some(x)`,直到完成。 之后它将返回 `None`。" -"`while let`能让我们持续迭代所有项。" -#: src/control-flow/while-let-expressions.md:26 +#: src/std/vec.md:29 msgid "" -"Point out that the `while let` loop will keep going as long as the value " -"matches the pattern." -msgstr "指出只要值与模式匹配,`while let` 循环就会一直进行下去。" - -#: src/control-flow/while-let-expressions.md:27 +"`Vec` implements [`Deref`](https://doc.rust-lang.org/std/vec/" +"struct.Vec.html#deref-methods-%5BT%5D), which means that you can call slice " +"methods on a `Vec`." +msgstr "" +"`Vec` 会实现 [`Deref`](https://doc.rust-lang.org/std/vec/" +"struct.Vec.html#deref-methods-%5BT%5D),这意味着您可以对 `Vec` 调用 slice 方" +"法。" + +#: src/std/vec.md:37 msgid "" -"You could rewrite the `while let` loop as an infinite loop with an if " -"statement that breaks when there is no value to unwrap for `iter.next()`. " -"The `while let` provides syntactic sugar for the above scenario." +"`Vec` is a type of collection, along with `String` and `HashMap`. The data " +"it contains is stored on the heap. This means the amount of data doesn't " +"need to be known at compile time. It can grow or shrink at runtime." msgstr "" -"你可以使用 if 语句将 `while let` 循环重写为无限循环,当 `iter.next()` 没有值" -"可以解封时中断。`while let` 为上述情况提供了语法糖。" -#: src/control-flow/for-expressions.md:1 -msgid "`for` loops" -msgstr "`for` 循环" +#: src/std/vec.md:40 +msgid "" +"Notice how `Vec` is a generic type too, but you don't have to specify `T` " +"explicitly. As always with Rust type inference, the `T` was established " +"during the first `push` call." +msgstr "" -#: src/control-flow/for-expressions.md:3 +#: src/std/vec.md:42 msgid "" -"The [`for` loop](https://doc.rust-lang.org/std/keyword.for.html) is closely " -"related to the [`while let` loop](while-let-expression.md). It will " -"automatically call `into_iter()` on the expression and then iterate over it:" +"`vec![...]` is a canonical macro to use instead of `Vec::new()` and it " +"supports adding initial elements to the vector." msgstr "" -"[`for` 循环](https://doc.rust-lang.org/std/keyword.for.html) 与 [`when let` " -"循环](when-let-expression.md)密切相关。它会 自动对表达式调用 `into_iter()`," -"然后对其进行迭代:" -#: src/control-flow/for-expressions.md:7 +#: src/std/vec.md:44 +msgid "" +"To index the vector you use `[` `]`, but they will panic if out of bounds. " +"Alternatively, using `get` will return an `Option`. The `pop` function will " +"remove the last element." +msgstr "" + +#: src/std/vec.md:46 +msgid "" +"Show iterating over a vector and mutating the value: `for e in &mut v { *e " +"+= 50; }`" +msgstr "" + +#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 +msgid "`HashMap`" +msgstr "`HashMap`" + +#: src/std/hashmap.md:3 +msgid "Standard hash map with protection against HashDoS attacks:" +msgstr "标准的哈希映射,内含针对 HashDoS 攻击的保护措施:" + +#: src/std/hashmap.md:5 msgid "" "```rust,editable\n" +"use std::collections::HashMap;\n" +"\n" "fn main() {\n" -" let v = vec![10, 20, 30];\n" +" let mut page_counts = HashMap::new();\n" +" page_counts.insert(\"Adventures of Huckleberry Finn\".to_string(), " +"207);\n" +" page_counts.insert(\"Grimms' Fairy Tales\".to_string(), 751);\n" +" page_counts.insert(\"Pride and Prejudice\".to_string(), 303);\n" "\n" -" for x in v {\n" -" println!(\"x: {x}\");\n" -" }\n" -" \n" -" for i in (0..10).step_by(2) {\n" -" println!(\"i: {i}\");\n" +" if !page_counts.contains_key(\"Les Misérables\") {\n" +" println!(\"We know about {} books, but not Les Misérables.\",\n" +" page_counts.len());\n" " }\n" -"}\n" -"```" -msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let v = vec![10, 20, 30];\n" "\n" -" for x in v {\n" -" println!(\"x: {x}\");\n" +" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " +"Wonderland\"] {\n" +" match page_counts.get(book) {\n" +" Some(count) => println!(\"{book}: {count} pages\"),\n" +" None => println!(\"{book} is unknown.\")\n" +" }\n" " }\n" -" \n" -" for i in (0..10).step_by(2) {\n" -" println!(\"i: {i}\");\n" +"\n" +" // Use the .entry() method to insert a value if nothing is found.\n" +" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " +"Wonderland\"] {\n" +" let page_count: &mut i32 = page_counts.entry(book.to_string())." +"or_insert(0);\n" +" *page_count += 1;\n" " }\n" +"\n" +" println!(\"{page_counts:#?}\");\n" "}\n" "```" +msgstr "" -#: src/control-flow/for-expressions.md:21 -msgid "You can use `break` and `continue` here as usual." -msgstr "你可以在此照常使用 `break` 和 `continue`。" - -#: src/control-flow/for-expressions.md:25 -msgid "Index iteration is not a special syntax in Rust for just that case." -msgstr "在这种情况下,索引迭代在 Rust 中并不是一个特殊的语法。" - -#: src/control-flow/for-expressions.md:26 -msgid "`(0..10)` is a range that implements an `Iterator` trait. " -msgstr "`(0..10)` 是实现 `Iterator` trait 的范围。" +#: src/std/hashmap.md:38 +msgid "" +"`HashMap` is not defined in the prelude and needs to be brought into scope." +msgstr "" -#: src/control-flow/for-expressions.md:27 +#: src/std/hashmap.md:39 msgid "" -"`step_by` is a method that returns another `Iterator` that skips every other " -"element. " -msgstr "`step_by` 是返回另一个 `Iterator` 的方法,用于逐一跳过所有其他元素。" +"Try the following lines of code. The first line will see if a book is in the " +"hashmap and if not return an alternative value. The second line will insert " +"the alternative value in the hashmap if the book is not found." +msgstr "" -#: src/control-flow/for-expressions.md:28 +#: src/std/hashmap.md:41 msgid "" -"Modify the elements in the vector and explain the compiler errors. Change " -"vector `v` to be mutable and the for loop to `for x in v.iter_mut()`." +"```rust,ignore\n" +" let pc1 = page_counts\n" +" .get(\"Harry Potter and the Sorcerer's Stone \")\n" +" .unwrap_or(&336);\n" +" let pc2 = page_counts\n" +" .entry(\"The Hunger Games\".to_string())\n" +" .or_insert(374);\n" +"```" msgstr "" -"修改矢量中的元素并说明编译器错误。将矢量 `v` 改为可变,并将 for 循环改为 " -"`for x in v.iter_mut()`。" -#: src/control-flow/loop-expressions.md:1 -msgid "`loop` expressions" -msgstr "`loop` 表达式" +#: src/std/hashmap.md:49 +msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." +msgstr "" -#: src/control-flow/loop-expressions.md:3 +#: src/std/hashmap.md:50 msgid "" -"Finally, there is a [`loop` keyword](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#infinite-loops) which creates an endless loop." +"Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`](https://" +"doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#impl-" +"From%3C%5B(K,+V);+N%5D%3E-for-HashMap%3CK,+V,+RandomState%3E), which allows " +"us to easily initialize a hash map from a literal array:" msgstr "" -"最后是用于创建无限循环的 [`loop` 关键字](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#infinite-loops) 。" - -#: src/control-flow/loop-expressions.md:6 -msgid "Here you must either `break` or `return` to stop the loop:" -msgstr "在下例中,你必须 `break` 或 `return` 才能停止循环:" -#: src/control-flow/loop-expressions.md:8 +#: src/std/hashmap.md:52 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" loop {\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -" if x == 1 {\n" -" break;\n" -" }\n" -" }\n" -" println!(\"Final x: {x}\");\n" -"}\n" +"```rust,ignore\n" +" let page_counts = HashMap::from([\n" +" (\"Harry Potter and the Sorcerer's Stone\".to_string(), 336),\n" +" (\"The Hunger Games\".to_string(), 374),\n" +" ]);\n" "```" msgstr "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" loop {\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -" if x == 1 {\n" -" break;\n" -" }\n" -" }\n" -" println!(\"Final x: {x}\");\n" -"}\n" -"```" -#: src/control-flow/loop-expressions.md:27 -msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." -msgstr "用一个值(例如 `break 8`)来中断 `loop` 并将其输出。" +#: src/std/hashmap.md:59 +msgid "" +"Alternatively HashMap can be built from any `Iterator` which yields key-" +"value tuples." +msgstr "" -#: src/control-flow/loop-expressions.md:28 +#: src/std/hashmap.md:60 msgid "" -"Note that `loop` is the only looping construct which returns a non-trivial " -"value. This is because it's guaranteed to be entered at least once (unlike " -"`while` and `for` loops)." +"We are showing `HashMap`, and avoid using `&str` as key to make " +"examples easier. Using references in collections can, of course, be done, " +"but it can lead into complications with the borrow checker." msgstr "" -"请注意,`loop` 是唯一返回有意义的值的循环结构。 这是因为它保证至少被输入一次" -"(与 `while` 和 `for` 循环不同)。" -#: src/control-flow/match-expressions.md:1 -msgid "`match` expressions" -msgstr "`match` 表达式" +#: src/std/hashmap.md:62 +msgid "" +"Try removing `to_string()` from the example above and see if it still " +"compiles. Where do you think we might run into issues?" +msgstr "" -#: src/control-flow/match-expressions.md:3 +#: src/std/hashmap.md:64 msgid "" -"The [`match` keyword](https://doc.rust-lang.org/reference/expressions/match-" -"expr.html) is used to match a value against one or more patterns. In that " -"sense, it works like a series of `if let` expressions:" +"This type has several \"method-specific\" return types, such as `std::" +"collections::hash_map::Keys`. These types often appear in searches of the " +"Rust docs. Show students the docs for this type, and the helpful link back " +"to the `keys` method." msgstr "" -"[`match` 关键字](https://doc.rust-lang.org/reference/expressions/match-expr." -"html) 用于将一个值与一个或多个模式进行匹配。从这个意义上讲,它的工作方式 类似" -"于一系列的 `if let` 表达式:" -#: src/control-flow/match-expressions.md:7 +#: src/std/box.md:1 +msgid "`Box`" +msgstr "`Box`" + +#: src/std/box.md:3 +msgid "" +"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned " +"pointer to data on the heap:" +msgstr "" +"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) 是指向堆上数据的" +"自有指针:" + +#: src/std/box.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" match std::env::args().next().as_deref() {\n" -" Some(\"cat\") => println!(\"Will do cat things\"),\n" -" Some(\"ls\") => println!(\"Will ls some files\"),\n" -" Some(\"mv\") => println!(\"Let's move some files\"),\n" -" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" -" None => println!(\"Hmm, no program name?\"),\n" -" _ => println!(\"Unknown program name!\"),\n" -" }\n" +" let five = Box::new(5);\n" +" println!(\"five: {}\", *five);\n" "}\n" "```" msgstr "" -"```rust,editable\n" -"fn main() {\n" -" match std::env::args().next().as_deref() {\n" -" Some(\"cat\") => println!(\"Will do cat things\"),\n" -" Some(\"ls\") => println!(\"Will ls some files\"),\n" -" Some(\"mv\") => println!(\"Let's move some files\"),\n" -" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" -" None => println!(\"Hmm, no program name?\"),\n" -" _ => println!(\"Unknown program name!\"),\n" -" }\n" -"}\n" -"```" -#: src/control-flow/match-expressions.md:20 +#: src/std/box.md:26 msgid "" -"Like `if let`, each match arm must have the same type. The type is the last " -"expression of the block, if any. In the example above, the type is `()`." +"`Box` implements `Deref`, which means that you can [call " +"methods from `T` directly on a `Box`](https://doc.rust-lang.org/std/ops/" +"trait.Deref.html#more-on-deref-coercion)." msgstr "" -"与 `if let` 类似,每个匹配分支必须有相同的类型。该类型是块的最后一个 表达式" -"(如有)。在上例中,类型是 `()`。" - -#: src/control-flow/match-expressions.md:28 -msgid "Save the match expression to a variable and print it out." -msgstr "将 match 表达式保存到一个变量中并输出结果。" - -#: src/control-flow/match-expressions.md:29 -msgid "Remove `.as_deref()` and explain the error." -msgstr "移除 `.as_deref()` 并说明错误。" +"`Box` 会实现 `Deref`,这意味着您可以[直接在 `Box` 上通过 " +"`T` 调用相应方法](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-" +"deref-coercion)。" -#: src/control-flow/match-expressions.md:30 +#: src/std/box.md:34 msgid "" -"`std::env::args().next()` returns an `Option`, but we cannot match " -"against `String`." +"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " +"not null. " msgstr "" -"`std::env::args().next()` 会返回 `Option`,但无法与 `String` 进行匹" -"配。" +"在 C++ 中,`Box` 与 `std::unique_ptr` 类似,除了它一定会不为 null 以外。 " -#: src/control-flow/match-expressions.md:31 +#: src/std/box.md:35 msgid "" -"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " -"this turns `Option` into `Option<&str>`." +"In the above example, you can even leave out the `*` in the `println!` " +"statement thanks to `Deref`. " msgstr "" -"`as_deref()` 会将 `Option` 转换为 `Option<&T::Target>`。在我们的示例中,这" -"会将 `Option` 转换为 `Option<&str>`。" - -#: src/control-flow/match-expressions.md:32 -msgid "" -"We can now use pattern matching to match against the `&str` inside `Option`." -msgstr "现在,我们可以使用模式匹配来匹配 `Option` 中的 `&str`。" +"在上面的示例中,因为有 `Deref`,您甚至可以在 `println!` 语句中省略 `*`。 " -#: src/control-flow/break-continue.md:1 -msgid "`break` and `continue`" -msgstr "`break` 和 `continue`" +#: src/std/box.md:36 +msgid "A `Box` can be useful when you:" +msgstr "在以下情况下,`Box` 可能会很实用:" -#: src/control-flow/break-continue.md:3 +#: src/std/box.md:37 msgid "" -"If you want to exit a loop early, use [`break`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#break-expressions)," -msgstr "" -"如果你想提前退出循环,请使用 [`break`](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#break-expressions)," +"have a type whose size that can't be known at compile time, but the Rust " +"compiler wants to know an exact size." +msgstr "在编译时间遇到无法知晓大小的类型,但 Rust 编译器需要知道确切大小。" -#: src/control-flow/break-continue.md:4 +#: src/std/box.md:38 msgid "" -"If you want to immediately start the next iteration use [`continue`](https://" -"doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions)." +"want to transfer ownership of a large amount of data. To avoid copying large " +"amounts of data on the stack, instead store the data on the heap in a `Box` " +"so only the pointer is moved." msgstr "" -"如果需要立即启动 下一次迭代,请使用 [`continue`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#continue-expressions)。" +"想要转让大量数据的所有权。为避免在堆栈上复制大量数据,请改为将数据存储在 " +"`Box` 中的堆上,以便仅移动指针。" -#: src/control-flow/break-continue.md:7 +#: src/std/box-recursive.md:1 +msgid "Box with Recursive Data Structures" +msgstr "包含递归数据结构的 Box" + +#: src/std/box-recursive.md:3 msgid "" -"Both `continue` and `break` can optionally take a label argument which is " -"used to break out of nested loops:" -msgstr "`continue` 和 `break` 都可以选择接受一个标签参数,用来 终止嵌套循环:" +"Recursive data types or data types with dynamic sizes need to use a `Box`:" +msgstr "递归数据类型或具有动态大小的数据类型需要使用 `Box`:" -#: src/control-flow/break-continue.md:10 +#: src/std/box-recursive.md:5 src/std/box-niche.md:3 msgid "" "```rust,editable\n" -"fn main() {\n" -" let v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" -" 'outer: while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\n" -" let mut i = 0;\n" -" while i < x {\n" -" println!(\"x: {x}, i: {i}\");\n" -" i += 1;\n" -" if i == 3 {\n" -" break 'outer;\n" -" }\n" -" }\n" -" }\n" +"#[derive(Debug)]\n" +"enum List {\n" +" Cons(T, Box>),\n" +" Nil,\n" "}\n" -"```" -msgstr "" -"```rust,editable\n" +"\n" "fn main() {\n" -" let v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" -" 'outer: while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\n" -" let mut i = 0;\n" -" while i < x {\n" -" println!(\"x: {x}, i: {i}\");\n" -" i += 1;\n" -" if i == 3 {\n" -" break 'outer;\n" -" }\n" -" }\n" -" }\n" +" let list: List = List::Cons(1, Box::new(List::Cons(2, Box::" +"new(List::Nil))));\n" +" println!(\"{list:?}\");\n" "}\n" "```" +msgstr "" -#: src/control-flow/break-continue.md:28 +#: src/std/box-recursive.md:18 msgid "" -"In this case we break the outer loop after 3 iterations of the inner loop." -msgstr "在本示例中,我们会在内循环 3 次迭代后终止外循环。" +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"- -.\n" +": : : :\n" +": " +"list : : :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // " +"| :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": : : :\n" +": : : :\n" +"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"- -'\n" +"```" +msgstr "" -#: src/std.md:3 +#: src/std/box-recursive.md:33 +#, fuzzy msgid "" -"Rust comes with a standard library which helps establish a set of common " -"types used by Rust library and programs. This way, two libraries can work " -"together smoothly because they both use the same `String` type." +"If `Box` was not used and we attempted to embed a `List` directly into the " +"`List`, the compiler would not compute a fixed size of the struct in memory " +"(`List` would be of infinite size)." msgstr "" -"Rust 附带一个标准库,此库有助于建立一个供 Rust 库和程序 使用的常用类型集。这" -"样一来,两个库便可顺畅地搭配运作, 因为它们使用相同的 `String` 类型。" - -#: src/std.md:7 -msgid "The common vocabulary types include:" -msgstr "常见的词汇类型包括:" +"如果这里未使用 `Box`,且我们曾尝试将一个 `List` 直接嵌入 `List`, 编译器就不" +"会计算内存中结构体的固定大小,结构体看起来会像是无限大。" -#: src/std.md:9 +#: src/std/box-recursive.md:36 msgid "" -"[`Option` and `Result`](std/option-result.md) types: used for optional " -"values and [error handling](error-handling.md)." +"`Box` solves this problem as it has the same size as a regular pointer and " +"just points at the next element of the `List` in the heap." msgstr "" -"[`Option` 和 `Result`](std/option-result.md) 类型:用于可选值和 [错误处理]" -"(error-handling.md)。" +"`Box` 大小与一般指针相同,并且只会指向堆中的下一个 `List` 元素, 因此可以解决" +"这个问题。" -#: src/std.md:12 -msgid "[`String`](std/string.md): the default string type used for owned data." -msgstr "[`String`](std/string.md):用于自有数据的默认字符串类型。" +#: src/std/box-recursive.md:39 +msgid "" +"Remove the `Box` in the List definition and show the compiler error. " +"\"Recursive with indirection\" is a hint you might want to use a Box or " +"reference of some kind, instead of storing a value directly." +msgstr "" +"将 `Box` 从 List 定义中移除后,画面上会显示编译器错误。如果您看到“Recursive " +"with indirection”错误消息,这是在提示您使用 Box 或其他类型的引用,而不是直接" +"储存值。" -#: src/std.md:14 -msgid "[`Vec`](std/vec.md): a standard extensible vector." -msgstr "[`Vec`](std/vec.md):标准的可扩展矢量。" +#: src/std/box-niche.md:16 +msgid "" +"A `Box` cannot be empty, so the pointer is always valid and non-`null`. This " +"allows the compiler to optimize the memory layout:" +msgstr "" +"`Box` 不得为空,因此指针始终有效且非 `null`。这样, 编译器就可以优化内存布" +"局:" -#: src/std.md:16 +#: src/std/box-niche.md:19 msgid "" -"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " -"algorithm." -msgstr "[`HashMap`](std/hashmap.md):采用可配置哈希算法的哈希映射 类型。" +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"-.\n" +": : : :\n" +": " +"list : : :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " +"| :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": : : :\n" +": : : :\n" +"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"-'\n" +"```" +msgstr "" -#: src/std.md:19 -msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." -msgstr "[`Box`](std/box.md):适用于堆分配数据的自有指针。" +#: src/std/rc.md:1 +msgid "`Rc`" +msgstr "`Rc`" -#: src/std.md:21 +#: src/std/rc.md:3 msgid "" -"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " -"data." -msgstr "[`Rc`](std/rc.md):适用于堆分配数据的共享引用计数指针。" +"[`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) is a reference-" +"counted shared pointer. Use this when you need to refer to the same data " +"from multiple places:" +msgstr "" +"[`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) 是引用计数的共享指" +"针。如果您需要从多个位置 引用相同的数据,请使用此指针:" -#: src/std.md:25 +#: src/std/rc.md:6 msgid "" -"In fact, Rust contains several layers of the Standard Library: `core`, " -"`alloc` and `std`. " -msgstr "Rust 实际上含有多个层级的标准库,分别是 `core`、`alloc` 和 `std`。" +"```rust,editable\n" +"use std::rc::Rc;\n" +"\n" +"fn main() {\n" +" let mut a = Rc::new(10);\n" +" let mut b = Rc::clone(&a);\n" +"\n" +" println!(\"a: {a}\");\n" +" println!(\"b: {b}\");\n" +"}\n" +"```" +msgstr "" -#: src/std.md:26 +#: src/std/rc.md:18 +#, fuzzy msgid "" -"`core` includes the most basic types and functions that don't depend on " -"`libc`, allocator or even the presence of an operating system. " +"See [`Arc`](../concurrency/shared_state/arc.md) and [`Mutex`](https://doc." +"rust-lang.org/std/sync/struct.Mutex.html) if you are in a multi-threaded " +"context." msgstr "" -"`core` 包括最基本的类型与函数,这些类型与函数不依赖于 `libc`、分配器 或是否存" -"在操作系统。" +"如果您在多线程情境中,请参阅 [`Arc`](https://doc.rust-lang.org/std/sync/" +"struct.Mutex.html)。" -#: src/std.md:28 +#: src/std/rc.md:19 msgid "" -"`alloc` includes types which require a global heap allocator, such as `Vec`, " -"`Box` and `Arc`." -msgstr "`alloc` 包括需要全局堆分配器的类型,例如 `Vec`、`Box` 和 `Arc`。" +"You can _downgrade_ a shared pointer into a [`Weak`](https://doc.rust-lang." +"org/std/rc/struct.Weak.html) pointer to create cycles that will get dropped." +msgstr "" +"您可以将共享指针_降级_为 [`Weak`](https://doc.rust-lang.org/std/rc/struct." +"Weak.html) 指针, 以便创建之后会被舍弃的循环引用。" -#: src/std.md:29 +#: src/std/rc.md:29 msgid "" -"Embedded Rust applications often only use `core`, and sometimes `alloc`." -msgstr "嵌入式 Rust 应用通常只使用 `core`,偶尔会使用 `alloc`。" - -#: src/std/option-result.md:1 -msgid "`Option` and `Result`" -msgstr "`Option` 和 `Result`" +"`Rc`'s count ensures that its contained value is valid for as long as there " +"are references." +msgstr "`Rc` 的计数可确保只要有引用,内含的值就会保持有效。" -#: src/std/option-result.md:3 -msgid "The types represent optional data:" -msgstr "这些类型表示可选数据:" +#: src/std/rc.md:30 +msgid "`Rc` in Rust is like `std::shared_ptr` in C++." +msgstr "" -#: src/std/option-result.md:5 +#: src/std/rc.md:31 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let numbers = vec![10, 20, 30];\n" -" let first: Option<&i8> = numbers.first();\n" -" println!(\"first: {first:?}\");\n" -"\n" -" let idx: Result = numbers.binary_search(&10);\n" -" println!(\"idx: {idx:?}\");\n" -"}\n" -"```" +"`Rc::clone` is cheap: it creates a pointer to the same allocation and " +"increases the reference count. Does not make a deep clone and can generally " +"be ignored when looking for performance issues in code." msgstr "" +"`Rc::clone` 的成本很低:这个做法会创建指向相同分配的指针,并增加引用计数,而" +"不会产生深层的克隆,排查代码性能问题时通常可以忽略。" -#: src/std/option-result.md:18 -msgid "`Option` and `Result` are widely used not just in the standard library." -msgstr "`Option` 和 `Result` 的使用范围很广,不局限于标准库。" +#: src/std/rc.md:32 +msgid "" +"`make_mut` actually clones the inner value if necessary (\"clone-on-write\") " +"and returns a mutable reference." +msgstr "" +"`make_mut` 实际上会在必要时克隆内部值(“clone-on-write”),并返回可变的引用。" -#: src/std/option-result.md:19 -msgid "`Option<&T>` has zero space overhead compared to `&T`." -msgstr "相较于 `&T`,`Option<&T>` 的空间开销为零。" +#: src/std/rc.md:33 +msgid "Use `Rc::strong_count` to check the reference count." +msgstr "使用 `Rc::strong_count` 可查看引用计数。" -#: src/std/option-result.md:20 +#: src/std/rc.md:34 +#, fuzzy msgid "" -"`Result` is the standard type to implement error handling as we will see on " -"Day 3." -msgstr "`Result` 是用于实现错误处理的标准类型,我们将在第 3 天的课程中介绍。" - -#: src/std/option-result.md:21 -msgid "`binary_search` returns `Result`." -msgstr "`binary_search` 会返回 `Result`。" +"`Rc::downgrade` gives you a _weakly reference-counted_ object to create " +"cycles that will be dropped properly (likely in combination with `RefCell`, " +"on the next slide)." +msgstr "" +"`Rc::downgrade` 会向您提供 _弱引用计数_ 对象, 以便创建之后会被适当舍弃的周期" +"(可能会与 `RefCell` 组合)。" -#: src/std/option-result.md:22 -msgid "If found, `Result::Ok` holds the index where the element is found." -msgstr "如果找到该元素,`Result::Ok` 会保留该元素所在位置的索引。" +#: src/std/cell.md:1 +msgid "`Cell` and `RefCell`" +msgstr "" -#: src/std/option-result.md:23 +#: src/std/cell.md:3 +#, fuzzy msgid "" -"Otherwise, `Result::Err` contains the index where such an element should be " -"inserted." -msgstr "如果没有找到该元素,`Result::Err` 会包含应插入这类元素的索引。" +"[`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html) and [`RefCell`]" +"(https://doc.rust-lang.org/std/cell/struct.RefCell.html) implement what Rust " +"calls _interior mutability:_ mutation of values in an immutable context." +msgstr "" +"您可以使用 [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) 和 " +"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) 对 `u8` 来源" +"进行抽象化处理:" -#: src/std/string.md:3 +#: src/std/cell.md:8 msgid "" -"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " -"standard heap-allocated growable UTF-8 string buffer:" +"`Cell` is typically used for simple types, as it requires copying or moving " +"values. More complex interior types typically use `RefCell`, which tracks " +"shared and exclusive references at runtime and panics if they are misused." msgstr "" -"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) 是标准堆" -"分配的可扩容 UTF-8 字符串缓冲区:" -#: src/std/string.md:5 +#: src/std/cell.md:12 msgid "" "```rust,editable\n" -"fn main() {\n" -" let mut s1 = String::new();\n" -" s1.push_str(\"Hello\");\n" -" println!(\"s1: len = {}, capacity = {}\", s1.len(), s1.capacity());\n" +"use std::cell::RefCell;\n" +"use std::rc::Rc;\n" "\n" -" let mut s2 = String::with_capacity(s1.len() + 1);\n" -" s2.push_str(&s1);\n" -" s2.push('!');\n" -" println!(\"s2: len = {}, capacity = {}\", s2.len(), s2.capacity());\n" +"#[derive(Debug, Default)]\n" +"struct Node {\n" +" value: i64,\n" +" children: Vec>>,\n" +"}\n" "\n" -" let s3 = String::from(\"🇨🇭\");\n" -" println!(\"s3: len = {}, number of chars = {}\", s3.len(),\n" -" s3.chars().count());\n" +"impl Node {\n" +" fn new(value: i64) -> Rc> {\n" +" Rc::new(RefCell::new(Node { value, ..Node::default() }))\n" +" }\n" +"\n" +" fn sum(&self) -> i64 {\n" +" self.value + self.children.iter().map(|c| c.borrow().sum()).sum::" +"()\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let root = Node::new(1);\n" +" root.borrow_mut().children.push(Node::new(5));\n" +" let subtree = Node::new(10);\n" +" subtree.borrow_mut().children.push(Node::new(11));\n" +" subtree.borrow_mut().children.push(Node::new(12));\n" +" root.borrow_mut().children.push(subtree);\n" +"\n" +" println!(\"graph: {root:#?}\");\n" +" println!(\"graph sum: {}\", root.borrow().sum());\n" "}\n" "```" msgstr "" -#: src/std/string.md:22 +#: src/std/cell.md:47 msgid "" -"`String` implements [`Deref`](https://doc.rust-lang.org/std/" -"string/struct.String.html#deref-methods-str), which means that you can call " -"all `str` methods on a `String`." +"If we were using `Cell` instead of `RefCell` in this example, we would have " +"to move the `Node` out of the `Rc` to push children, then move it back in. " +"This is safe because there's always one, un-referenced value in the cell, " +"but it's not ergonomic." msgstr "" -"`String` 会实现 [`Deref`](https://doc.rust-lang.org/std/string/" -"struct.String.html#deref-methods-str),这意味着您可以 对 `String` 调用所有 " -"`str` 方法。" -#: src/std/string.md:30 +#: src/std/cell.md:48 msgid "" -"`String::new` returns a new empty string, use `String::with_capacity` when " -"you know how much data you want to push to the string." +"To do anything with a Node, you must call a `RefCell` method, usually " +"`borrow` or `borrow_mut`." msgstr "" -#: src/std/string.md:31 +#: src/std/cell.md:49 msgid "" -"`String::len` returns the size of the `String` in bytes (which can be " -"different from its length in characters)." +"Demonstrate that reference loops can be created by adding `root` to `subtree." +"children` (don't try to print it!)." msgstr "" -#: src/std/string.md:32 +#: src/std/cell.md:50 msgid "" -"`String::chars` returns an iterator over the actual characters. Note that a " -"`char` can be different from what a human will consider a \"character\" due " -"to [grapheme clusters](https://docs.rs/unicode-segmentation/latest/" -"unicode_segmentation/struct.Graphemes.html)." +"To demonstrate a runtime panic, add a `fn inc(&mut self)` that increments " +"`self.value` and calls the same method on its children. This will panic in " +"the presence of the reference loop, with `thread 'main' panicked at 'already " +"borrowed: BorrowMutError'`." msgstr "" -#: src/std/string.md:33 -msgid "" -"When people refer to strings they could either be talking about `&str` or " -"`String`." +#: src/modules.md:3 +msgid "We have seen how `impl` blocks let us namespace functions to a type." msgstr "" -#: src/std/string.md:34 -msgid "" -"When a type implements `Deref`, the compiler will let you " -"transparently call methods from `T`." +#: src/modules.md:5 +msgid "Similarly, `mod` lets us namespace types and functions:" msgstr "" -#: src/std/string.md:35 +#: src/modules.md:7 msgid "" -"`String` implements `Deref` which transparently gives it " -"access to `str`'s methods." +"```rust,editable\n" +"mod foo {\n" +" pub fn do_something() {\n" +" println!(\"In the foo module\");\n" +" }\n" +"}\n" +"\n" +"mod bar {\n" +" pub fn do_something() {\n" +" println!(\"In the bar module\");\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" foo::do_something();\n" +" bar::do_something();\n" +"}\n" +"```" msgstr "" -#: src/std/string.md:36 -msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;." +#: src/modules.md:28 +msgid "" +"Packages provide functionality and include a `Cargo.toml` file that " +"describes how to build a bundle of 1+ crates." msgstr "" -#: src/std/string.md:37 +#: src/modules.md:29 msgid "" -"`String` is implemented as a wrapper around a vector of bytes, many of the " -"operations you see supported on vectors are also supported on `String`, but " -"with some extra guarantees." +"Crates are a tree of modules, where a binary crate creates an executable and " +"a library crate compiles to a library." msgstr "" -#: src/std/string.md:38 -msgid "Compare the different ways to index a `String`:" +#: src/modules.md:30 +msgid "Modules define organization, scope, and are the focus of this section." msgstr "" -#: src/std/string.md:39 -msgid "" -"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " -"out-of-bounds." +#: src/modules/visibility.md:3 +msgid "Modules are a privacy boundary:" msgstr "" -#: src/std/string.md:40 -msgid "" -"To a substring by using `s3[0..4]`, where that slice is on character " -"boundaries or not." +#: src/modules/visibility.md:5 +msgid "Module items are private by default (hides implementation details)." msgstr "" -#: src/std/vec.md:1 -msgid "`Vec`" -msgstr "`Vec`" +#: src/modules/visibility.md:6 +msgid "Parent and sibling items are always visible." +msgstr "" -#: src/std/vec.md:3 +#: src/modules/visibility.md:7 msgid "" -"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " -"resizable heap-allocated buffer:" +"In other words, if an item is visible in module `foo`, it's visible in all " +"the descendants of `foo`." msgstr "" -"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) 是标准的可调整大小" -"堆分配缓冲区:" -#: src/std/vec.md:5 +#: src/modules/visibility.md:10 msgid "" "```rust,editable\n" -"fn main() {\n" -" let mut v1 = Vec::new();\n" -" v1.push(42);\n" -" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n" +"mod outer {\n" +" fn private() {\n" +" println!(\"outer::private\");\n" +" }\n" "\n" -" let mut v2 = Vec::with_capacity(v1.len() + 1);\n" -" v2.extend(v1.iter());\n" -" v2.push(9999);\n" -" println!(\"v2: len = {}, capacity = {}\", v2.len(), v2.capacity());\n" +" pub fn public() {\n" +" println!(\"outer::public\");\n" +" }\n" "\n" -" // Canonical macro to initialize a vector with elements.\n" -" let mut v3 = vec![0, 0, 1, 2, 3, 4];\n" +" mod inner {\n" +" fn private() {\n" +" println!(\"outer::inner::private\");\n" +" }\n" "\n" -" // Retain only the even elements.\n" -" v3.retain(|x| x % 2 == 0);\n" -" println!(\"{v3:?}\");\n" +" pub fn public() {\n" +" println!(\"outer::inner::public\");\n" +" super::private();\n" +" }\n" +" }\n" +"}\n" "\n" -" // Remove consecutive duplicates.\n" -" v3.dedup();\n" -" println!(\"{v3:?}\");\n" +"fn main() {\n" +" outer::public();\n" "}\n" "```" msgstr "" -#: src/std/vec.md:29 -msgid "" -"`Vec` implements [`Deref`](https://doc.rust-lang.org/std/vec/" -"struct.Vec.html#deref-methods-%5BT%5D), which means that you can call slice " -"methods on a `Vec`." +#: src/modules/visibility.md:39 +msgid "Use the `pub` keyword to make modules public." msgstr "" -"`Vec` 会实现 [`Deref`](https://doc.rust-lang.org/std/vec/" -"struct.Vec.html#deref-methods-%5BT%5D),这意味着您可以对 `Vec` 调用 slice 方" -"法。" -#: src/std/vec.md:37 +#: src/modules/visibility.md:41 msgid "" -"`Vec` is a type of collection, along with `String` and `HashMap`. The data " -"it contains is stored on the heap. This means the amount of data doesn't " -"need to be known at compile time. It can grow or shrink at runtime." +"Additionally, there are advanced `pub(...)` specifiers to restrict the scope " +"of public visibility." msgstr "" -#: src/std/vec.md:40 +#: src/modules/visibility.md:43 msgid "" -"Notice how `Vec` is a generic type too, but you don't have to specify `T` " -"explicitly. As always with Rust type inference, the `T` was established " -"during the first `push` call." +"See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-" +"privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)." msgstr "" -#: src/std/vec.md:42 -msgid "" -"`vec![...]` is a canonical macro to use instead of `Vec::new()` and it " -"supports adding initial elements to the vector." +#: src/modules/visibility.md:44 +msgid "Configuring `pub(crate)` visibility is a common pattern." msgstr "" -#: src/std/vec.md:44 -msgid "" -"To index the vector you use `[` `]`, but they will panic if out of bounds. " -"Alternatively, using `get` will return an `Option`. The `pop` function will " -"remove the last element." +#: src/modules/visibility.md:45 +msgid "Less commonly, you can give visibility to a specific path." msgstr "" -#: src/std/vec.md:46 +#: src/modules/visibility.md:46 msgid "" -"Show iterating over a vector and mutating the value: `for e in &mut v { *e " -"+= 50; }`" +"In any case, visibility must be granted to an ancestor module (and all of " +"its descendants)." msgstr "" -#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 -msgid "`HashMap`" -msgstr "`HashMap`" +#: src/modules/paths.md:3 +msgid "Paths are resolved as follows:" +msgstr "" -#: src/std/hashmap.md:3 -msgid "Standard hash map with protection against HashDoS attacks:" -msgstr "标准的哈希映射,内含针对 HashDoS 攻击的保护措施:" +#: src/modules/paths.md:5 +msgid "As a relative path:" +msgstr "" -#: src/std/hashmap.md:5 -msgid "" -"```rust,editable\n" -"use std::collections::HashMap;\n" -"\n" -"fn main() {\n" -" let mut page_counts = HashMap::new();\n" -" page_counts.insert(\"Adventures of Huckleberry Finn\".to_string(), " -"207);\n" -" page_counts.insert(\"Grimms' Fairy Tales\".to_string(), 751);\n" -" page_counts.insert(\"Pride and Prejudice\".to_string(), 303);\n" -"\n" -" if !page_counts.contains_key(\"Les Misérables\") {\n" -" println!(\"We know about {} books, but not Les Misérables.\",\n" -" page_counts.len());\n" -" }\n" -"\n" -" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " -"Wonderland\"] {\n" -" match page_counts.get(book) {\n" -" Some(count) => println!(\"{book}: {count} pages\"),\n" -" None => println!(\"{book} is unknown.\")\n" -" }\n" -" }\n" -"\n" -" // Use the .entry() method to insert a value if nothing is found.\n" -" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " -"Wonderland\"] {\n" -" let page_count: &mut i32 = page_counts.entry(book.to_string())." -"or_insert(0);\n" -" *page_count += 1;\n" -" }\n" -"\n" -" println!(\"{page_counts:#?}\");\n" -"}\n" -"```" +#: src/modules/paths.md:6 +msgid "`foo` or `self::foo` refers to `foo` in the current module," msgstr "" -#: src/std/hashmap.md:38 -msgid "" -"`HashMap` is not defined in the prelude and needs to be brought into scope." +#: src/modules/paths.md:7 +msgid "`super::foo` refers to `foo` in the parent module." msgstr "" -#: src/std/hashmap.md:39 -msgid "" -"Try the following lines of code. The first line will see if a book is in the " -"hashmap and if not return an alternative value. The second line will insert " -"the alternative value in the hashmap if the book is not found." +#: src/modules/paths.md:9 +msgid "As an absolute path:" msgstr "" -#: src/std/hashmap.md:41 -msgid "" -"```rust,ignore\n" -" let pc1 = page_counts\n" -" .get(\"Harry Potter and the Sorcerer's Stone \")\n" -" .unwrap_or(&336);\n" -" let pc2 = page_counts\n" -" .entry(\"The Hunger Games\".to_string())\n" -" .or_insert(374);\n" -"```" +#: src/modules/paths.md:10 +msgid "`crate::foo` refers to `foo` in the root of the current crate," msgstr "" -#: src/std/hashmap.md:49 -msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." +#: src/modules/paths.md:11 +msgid "`bar::foo` refers to `foo` in the `bar` crate." msgstr "" -#: src/std/hashmap.md:50 +#: src/modules/paths.md:13 msgid "" -"Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`](https://" -"doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#impl-" -"From%3C%5B(K,+V);+N%5D%3E-for-HashMap%3CK,+V,+RandomState%3E), which allows " -"us to easily initialize a hash map from a literal array:" +"A module can bring symbols from another module into scope with `use`. You " +"will typically see something like this at the top of each module:" msgstr "" -#: src/std/hashmap.md:52 +#: src/modules/filesystem.md:3 msgid "" -"```rust,ignore\n" -" let page_counts = HashMap::from([\n" -" (\"Harry Potter and the Sorcerer's Stone\".to_string(), 336),\n" -" (\"The Hunger Games\".to_string(), 374),\n" -" ]);\n" -"```" +"Omitting the module content will tell Rust to look for it in another file:" msgstr "" -#: src/std/hashmap.md:59 +#: src/modules/filesystem.md:9 msgid "" -"Alternatively HashMap can be built from any `Iterator` which yields key-" -"value tuples." +"This tells rust that the `garden` module content is found at `src/garden." +"rs`. Similarly, a `garden::vegetables` module can be found at `src/garden/" +"vegetables.rs`." msgstr "" -#: src/std/hashmap.md:60 -msgid "" -"We are showing `HashMap`, and avoid using `&str` as key to make " -"examples easier. Using references in collections can, of course, be done, " -"but it can lead into complications with the borrow checker." +#: src/modules/filesystem.md:12 +msgid "The `crate` root is in:" msgstr "" -#: src/std/hashmap.md:62 -msgid "" -"Try removing `to_string()` from the example above and see if it still " -"compiles. Where do you think we might run into issues?" +#: src/modules/filesystem.md:14 +msgid "`src/lib.rs` (for a library crate)" msgstr "" -#: src/std/box.md:1 -msgid "`Box`" -msgstr "`Box`" +#: src/modules/filesystem.md:15 +msgid "`src/main.rs` (for a binary crate)" +msgstr "" -#: src/std/box.md:3 +#: src/modules/filesystem.md:17 msgid "" -"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned " -"pointer to data on the heap:" +"Modules defined in files can be documented, too, using \"inner doc " +"comments\". These document the item that contains them -- in this case, a " +"module." msgstr "" -"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) 是指向堆上数据的" -"自有指针:" -#: src/std/box.md:5 +#: src/modules/filesystem.md:20 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let five = Box::new(5);\n" -" println!(\"five: {}\", *five);\n" -"}\n" +"```rust,editable,compile_fail\n" +"//! This module implements the garden, including a highly performant " +"germination\n" +"//! implementation.\n" +"\n" +"// Re-export types from this module.\n" +"pub use seeds::SeedPacket;\n" +"pub use garden::Garden;\n" +"\n" +"/// Sow the given seed packets.\n" +"pub fn sow(seeds: Vec) { todo!() }\n" +"\n" +"/// Harvest the produce in the garden that is ready.\n" +"pub fn harvest(garden: &mut Garden) { todo!() }\n" "```" msgstr "" -#: src/std/box.md:13 +#: src/modules/filesystem.md:37 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - -. .- - - - - - -.\n" -": : : :\n" -": five : : :\n" -": +-----+ : : +-----+ :\n" -": | o---|---+-----+-->| 5 | :\n" -": +-----+ : : +-----+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - -' `- - - - - - -'\n" -"```" +"Before Rust 2018, modules needed to be located at `module/mod.rs` instead of " +"`module.rs`, and this is still a working alternative for editions after 2018." msgstr "" -#: src/std/box.md:26 +#: src/modules/filesystem.md:39 msgid "" -"`Box` implements `Deref`, which means that you can [call " -"methods from `T` directly on a `Box`](https://doc.rust-lang.org/std/ops/" -"trait.Deref.html#more-on-deref-coercion)." +"The main reason to introduce `filename.rs` as alternative to `filename/mod." +"rs` was because many files named `mod.rs` can be hard to distinguish in IDEs." msgstr "" -"`Box` 会实现 `Deref`,这意味着您可以[直接在 `Box` 上通过 " -"`T` 调用相应方法](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-" -"deref-coercion)。" -#: src/std/box.md:34 -msgid "" -"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " -"not null. " +#: src/modules/filesystem.md:42 +msgid "Deeper nesting can use folders, even if the main module is a file:" msgstr "" -"在 C++ 中,`Box` 与 `std::unique_ptr` 类似,除了它一定会不为 null 以外。" -#: src/std/box.md:35 +#: src/modules/filesystem.md:52 msgid "" -"In the above example, you can even leave out the `*` in the `println!` " -"statement thanks to `Deref`. " +"The place rust will look for modules can be changed with a compiler " +"directive:" msgstr "" -"在上面的示例中,因为有 `Deref`,您甚至可以在 `println!` 语句中省略 `*`。" -#: src/std/box.md:36 -msgid "A `Box` can be useful when you:" -msgstr "在以下情况下,`Box` 可能会很实用:" +#: src/modules/filesystem.md:54 +msgid "" +"```rust,ignore\n" +"#[path = \"some/path.rs\"]\n" +"mod some_module;\n" +"```" +msgstr "" -#: src/std/box.md:37 +#: src/modules/filesystem.md:59 msgid "" -"have a type whose size that can't be known at compile time, but the Rust " -"compiler wants to know an exact size." -msgstr "在编译时间遇到无法知晓大小的类型,但 Rust 编译器需要知道确切大小。" +"This is useful, for example, if you would like to place tests for a module " +"in a file named `some_module_test.rs`, similar to the convention in Go." +msgstr "" -#: src/std/box.md:38 +#: src/exercises/day-2/afternoon.md:1 +msgid "Day 2: Afternoon Exercises" +msgstr "第二天下午习题" + +#: src/exercises/day-2/afternoon.md:3 +msgid "The exercises for this afternoon will focus on strings and iterators." +msgstr "今天下午的习题将重点关注字符串(string)和迭代器(iterator)。" + +#: src/exercises/day-2/iterators-and-ownership.md:3 msgid "" -"want to transfer ownership of a large amount of data. To avoid copying large " -"amounts of data on the stack, instead store the data on the heap in a `Box` " -"so only the pointer is moved." +"The ownership model of Rust affects many APIs. An example of this is the " +"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " +"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " +"traits." msgstr "" -"想要转让大量数据的所有权。为避免在堆栈上复制大量数据,请改为将数据存储在 " -"`Box` 中的堆上,以便仅移动指针。" -#: src/std/box-recursive.md:1 -msgid "Box with Recursive Data Structures" -msgstr "包含递归数据结构的 Box" +#: src/exercises/day-2/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 +msgid "`Iterator`" +msgstr "" -#: src/std/box-recursive.md:3 +#: src/exercises/day-2/iterators-and-ownership.md:10 msgid "" -"Recursive data types or data types with dynamic sizes need to use a `Box`:" -msgstr "递归数据类型或具有动态大小的数据类型需要使用 `Box`:" +"Traits are like interfaces: they describe behavior (methods) for a type. The " +"`Iterator` trait simply says that you can call `next` until you get `None` " +"back:" +msgstr "" -#: src/std/box-recursive.md:5 src/std/box-niche.md:3 +#: src/exercises/day-2/iterators-and-ownership.md:20 +msgid "You use this trait like this:" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:22 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"enum List {\n" -" Cons(T, Box>),\n" -" Nil,\n" -"}\n" -"\n" "fn main() {\n" -" let list: List = List::Cons(1, Box::new(List::Cons(2, Box::" -"new(List::Nil))));\n" -" println!(\"{list:?}\");\n" +" let v: Vec = vec![10, 20, 30];\n" +" let mut iter = v.iter();\n" +"\n" +" println!(\"v[0]: {:?}\", iter.next());\n" +" println!(\"v[1]: {:?}\", iter.next());\n" +" println!(\"v[2]: {:?}\", iter.next());\n" +" println!(\"No more items: {:?}\", iter.next());\n" "}\n" "```" msgstr "" -#: src/std/box-recursive.md:18 +#: src/exercises/day-2/iterators-and-ownership.md:34 +msgid "What is the type returned by the iterator? Test your answer here:" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:36 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " -"- -.\n" -": : : :\n" -": " -"list : : :\n" -": +------+----+----+ : : +------+----+----+ +------+----+----" -"+ :\n" -": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // " -"| :\n" -": +------+----+----+ : : +------+----+----+ +------+----+----" -"+ :\n" -": : : :\n" -": : : :\n" -"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " -"- -'\n" +"```rust,editable,compile_fail\n" +"fn main() {\n" +" let v: Vec = vec![10, 20, 30];\n" +" let mut iter = v.iter();\n" +"\n" +" let v0: Option<..> = iter.next();\n" +" println!(\"v0: {v0:?}\");\n" +"}\n" "```" msgstr "" -#: src/std/box-recursive.md:33 -msgid "" -"If the `Box` was not used here and we attempted to embed a `List` directly " -"into the `List`, the compiler would not compute a fixed size of the struct " -"in memory, it would look infinite." +#: src/exercises/day-2/iterators-and-ownership.md:46 +msgid "Why is this type used?" msgstr "" -"如果这里未使用 `Box`,且我们曾尝试将一个 `List` 直接嵌入 `List`, 编译器就不" -"会计算内存中结构体的固定大小,结构体看起来会像是无限大。" -#: src/std/box-recursive.md:36 +#: src/exercises/day-2/iterators-and-ownership.md:48 +msgid "`IntoIterator`" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:50 msgid "" -"`Box` solves this problem as it has the same size as a regular pointer and " -"just points at the next element of the `List` in the heap." +"The `Iterator` trait tells you how to _iterate_ once you have created an " +"iterator. The related trait `IntoIterator` tells you how to create the " +"iterator:" msgstr "" -"`Box` 大小与一般指针相同,并且只会指向堆中的下一个 `List` 元素, 因此可以解决" -"这个问题。" -#: src/std/box-recursive.md:39 +#: src/exercises/day-2/iterators-and-ownership.md:62 msgid "" -"Remove the `Box` in the List definition and show the compiler error. " -"\"Recursive with indirection\" is a hint you might want to use a Box or " -"reference of some kind, instead of storing a value directly." +"The syntax here means that every implementation of `IntoIterator` must " +"declare two types:" msgstr "" -"将 `Box` 从 List 定义中移除后,画面上会显示编译器错误。如果您看到“Recursive " -"with indirection”错误消息,这是在提示您使用 Box 或其他类型的引用,而不是直接" -"储存值。" -#: src/std/box-niche.md:16 +#: src/exercises/day-2/iterators-and-ownership.md:65 +msgid "`Item`: the type we iterate over, such as `i8`," +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:66 +msgid "`IntoIter`: the `Iterator` type returned by the `into_iter` method." +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:68 msgid "" -"A `Box` cannot be empty, so the pointer is always valid and non-`null`. This " -"allows the compiler to optimize the memory layout:" +"Note that `IntoIter` and `Item` are linked: the iterator must have the same " +"`Item` type, which means that it returns `Option`" msgstr "" -"`Box` 不得为空,因此指针始终有效且非 `null`。这样, 编译器就可以优化内存布" -"局:" -#: src/std/box-niche.md:19 +#: src/exercises/day-2/iterators-and-ownership.md:71 +msgid "Like before, what is the type returned by the iterator?" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:73 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " -"-.\n" -": : : :\n" -": " -"list : : :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " -"| :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " -"-'\n" +"```rust,editable,compile_fail\n" +"fn main() {\n" +" let v: Vec = vec![String::from(\"foo\"), String::" +"from(\"bar\")];\n" +" let mut iter = v.into_iter();\n" +"\n" +" let v0: Option<..> = iter.next();\n" +" println!(\"v0: {v0:?}\");\n" +"}\n" "```" msgstr "" -#: src/std/rc.md:1 -msgid "`Rc`" -msgstr "`Rc`" +#: src/exercises/day-2/iterators-and-ownership.md:83 +msgid "`for` Loops" +msgstr "" -#: src/std/rc.md:3 +#: src/exercises/day-2/iterators-and-ownership.md:85 msgid "" -"[`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) is a reference-" -"counted shared pointer. Use this when you need to refer to the same data " -"from multiple places:" +"Now that we know both `Iterator` and `IntoIterator`, we can build `for` " +"loops. They call `into_iter()` on an expression and iterates over the " +"resulting iterator:" msgstr "" -"[`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) 是引用计数的共享指" -"针。如果您需要从多个位置 引用相同的数据,请使用此指针:" -#: src/std/rc.md:6 +#: src/exercises/day-2/iterators-and-ownership.md:89 msgid "" "```rust,editable\n" -"use std::rc::Rc;\n" -"\n" "fn main() {\n" -" let mut a = Rc::new(10);\n" -" let mut b = Rc::clone(&a);\n" +" let v: Vec = vec![String::from(\"foo\"), String::" +"from(\"bar\")];\n" "\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\n" +" for word in &v {\n" +" println!(\"word: {word}\");\n" +" }\n" +"\n" +" for word in v {\n" +" println!(\"word: {word}\");\n" +" }\n" "}\n" "```" msgstr "" -#: src/std/rc.md:18 +#: src/exercises/day-2/iterators-and-ownership.md:103 +msgid "What is the type of `word` in each loop?" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:105 msgid "" -"If you need to mutate the data inside an `Rc`, you will need to wrap the " -"data in a type such as [`Cell` or `RefCell`](../concurrency/shared_state/arc." -"md)." +"Experiment with the code above and then consult the documentation for [`impl " +"IntoIterator for &Vec`](https://doc.rust-lang.org/std/vec/struct.Vec." +"html#impl-IntoIterator-for-%26'a+Vec%3CT,+A%3E) and [`impl IntoIterator for " +"Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-IntoIterator-" +"for-Vec%3CT,+A%3E) to check your answers." msgstr "" -"如果您需要在 `Rc` 内修改数据,则需将数据封装入 [`Cell` 或 `RefCell`](../" -"concurrency/shared_state/arc.md) 等类型中。" -#: src/std/rc.md:20 +#: src/exercises/day-2/strings-iterators.md:3 msgid "" -"See [`Arc`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) if you are " -"in a multi-threaded context." +"In this exercise, you are implementing a routing component of a web server. " +"The server is configured with a number of _path prefixes_ which are matched " +"against _request paths_. The path prefixes can contain a wildcard character " +"which matches a full segment. See the unit tests below." msgstr "" -"如果您在多线程情境中,请参阅 [`Arc`](https://doc.rust-lang.org/std/sync/" -"struct.Mutex.html)。" -#: src/std/rc.md:21 +#: src/exercises/day-2/strings-iterators.md:8 msgid "" -"You can _downgrade_ a shared pointer into a [`Weak`](https://doc.rust-lang." -"org/std/rc/struct.Weak.html) pointer to create cycles that will get dropped." +"Copy the following code to and make the tests " +"pass. Try avoiding allocating a `Vec` for your intermediate results:" msgstr "" -"您可以将共享指针_降级_为 [`Weak`](https://doc.rust-lang.org/std/rc/struct." -"Weak.html) 指针, 以便创建之后会被舍弃的循环引用。" -#: src/std/rc.md:31 +#: src/exercises/day-2/strings-iterators.md:12 msgid "" -"`Rc`'s count ensures that its contained value is valid for as long as there " -"are references." -msgstr "`Rc` 的计数可确保只要有引用,内含的值就会保持有效。" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" +"\n" +"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" +" unimplemented!()\n" +"}\n" +"\n" +"#[test]\n" +"fn test_matches_without_wildcard() {\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/" +"abc-123\"));\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/" +"books\"));\n" +"\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/" +"publishers\"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_matches_with_wildcard() {\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/books\"\n" +" ));\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/bar/books\"\n" +" ));\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/books/book1\"\n" +" ));\n" +"\n" +" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/" +"publishers\"));\n" +" assert!(!prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/booksByAuthor\"\n" +" ));\n" +"}\n" +"```" +msgstr "" -#: src/std/rc.md:32 -msgid "Like C++'s `std::shared_ptr`." -msgstr "类似 C++ 的 `std::shared_ptr`。" +#: src/welcome-day-3.md:1 +msgid "Welcome to Day 3" +msgstr "" -#: src/std/rc.md:33 +#: src/welcome-day-3.md:3 +msgid "Today, we will cover some more advanced topics of Rust:" +msgstr "" + +#: src/welcome-day-3.md:5 msgid "" -"`Rc::clone` is cheap: it creates a pointer to the same allocation and " -"increases the reference count. Does not make a deep clone and can generally " -"be ignored when looking for performance issues in code." +"Traits: deriving traits, default methods, and important standard library " +"traits." msgstr "" -"`Rc::clone` 的成本很低:这个做法会创建指向相同分配的指针,并增加引用计数,而" -"不会产生深层的克隆,排查代码性能问题时通常可以忽略。" -#: src/std/rc.md:34 +#: src/welcome-day-3.md:8 msgid "" -"`make_mut` actually clones the inner value if necessary (\"clone-on-write\") " -"and returns a mutable reference." +"Generics: generic data types, generic methods, monomorphization, and trait " +"objects." msgstr "" -"`make_mut` 实际上会在必要时克隆内部值(“clone-on-write”),并返回可变的引用。" -#: src/std/rc.md:35 -msgid "Use `Rc::strong_count` to check the reference count." -msgstr "使用 `Rc::strong_count` 可查看引用计数。" +#: src/welcome-day-3.md:11 +msgid "Error handling: panics, `Result`, and the try operator `?`." +msgstr "" + +#: src/welcome-day-3.md:13 +msgid "Testing: unit tests, documentation tests, and integration tests." +msgstr "" -#: src/std/rc.md:36 +#: src/welcome-day-3.md:15 msgid "" -"Compare the different datatypes mentioned. `Box` enables (im)mutable borrows " -"that are enforced at compile time. `RefCell` enables (im)mutable borrows " -"that are enforced at run time and will panic if it fails at runtime." +"Unsafe Rust: raw pointers, static variables, unsafe functions, and extern " +"functions." msgstr "" -"比较提及的不同数据类型。`Box` 可启用在编译时强制执行的可变/不可变借用。" -"`RefCell` 可启用在运行时强制执行的可变/不可变借用,如果在运行时失败,将产生恐" -"慌。" -#: src/std/rc.md:37 +#: src/generics.md:3 +#, fuzzy msgid "" -"`Rc::downgrade` gives you a _weakly reference-counted_ object to create " -"cycles that will be dropped properly (likely in combination with `RefCell`)." +"Rust support generics, which lets you abstract algorithms or data structures " +"(such as sorting or a binary tree) over the types used or stored." msgstr "" -"`Rc::downgrade` 会向您提供 _弱引用计数_ 对象, 以便创建之后会被适当舍弃的周期" -"(可能会与 `RefCell` 组合)。" +"Rust 支持泛型,允许您根据算法(例如排序)中使用的类型对算法进行抽象化处理。" + +#: src/generics/data-types.md:3 +msgid "You can use generics to abstract over the concrete field type:" +msgstr "您可以使用泛型对具体字段类型进行抽象化处理:" -#: src/std/rc.md:41 +#: src/generics/data-types.md:5 msgid "" "```rust,editable\n" -"use std::rc::{Rc, Weak};\n" -"use std::cell::RefCell;\n" -"\n" "#[derive(Debug)]\n" -"struct Node {\n" -" value: i64,\n" -" parent: Option>>,\n" -" children: Vec>>,\n" +"struct Point {\n" +" x: T,\n" +" y: T,\n" "}\n" "\n" "fn main() {\n" -" let mut root = Rc::new(RefCell::new(Node {\n" -" value: 42,\n" -" parent: None,\n" -" children: vec![],\n" -" }));\n" -" let child = Rc::new(RefCell::new(Node {\n" -" value: 43,\n" -" children: vec![],\n" -" parent: Some(Rc::downgrade(&root))\n" -" }));\n" -" root.borrow_mut().children.push(child);\n" -"\n" -" println!(\"graph: {root:#?}\");\n" +" let integer = Point { x: 5, y: 10 };\n" +" let float = Point { x: 1.0, y: 4.0 };\n" +" println!(\"{integer:?} and {float:?}\");\n" "}\n" "```" msgstr "" -#: src/modules.md:3 -msgid "We have seen how `impl` blocks let us namespace functions to a type." +#: src/generics/data-types.md:21 +msgid "Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`." msgstr "" -#: src/modules.md:5 -msgid "Similarly, `mod` lets us namespace types and functions:" +#: src/generics/data-types.md:23 +msgid "Fix the code to allow points that have elements of different types." msgstr "" -#: src/modules.md:7 +#: src/generics/methods.md:3 +msgid "You can declare a generic type on your `impl` block:" +msgstr "您可以在 `impl` 块中声明通用类型:" + +#: src/generics/methods.md:5 msgid "" "```rust,editable\n" -"mod foo {\n" -" pub fn do_something() {\n" -" println!(\"In the foo module\");\n" -" }\n" -"}\n" +"#[derive(Debug)]\n" +"struct Point(T, T);\n" "\n" -"mod bar {\n" -" pub fn do_something() {\n" -" println!(\"In the bar module\");\n" +"impl Point {\n" +" fn x(&self) -> &T {\n" +" &self.0 // + 10\n" " }\n" +"\n" +" // fn set_x(&mut self, x: T)\n" "}\n" "\n" "fn main() {\n" -" foo::do_something();\n" -" bar::do_something();\n" +" let p = Point(5, 10);\n" +" println!(\"p.x = {}\", p.x());\n" "}\n" "```" msgstr "" -#: src/modules.md:28 +#: src/generics/methods.md:25 msgid "" -"Packages provide functionality and include a `Cargo.toml` file that " -"describes how to build a bundle of 1+ crates." +"_Q:_ Why `T` is specified twice in `impl Point {}`? Isn't that " +"redundant?" msgstr "" +"\\*问:\\*为什么 `T` 在 `impl Point {}` 中指定了两次?这不是多余的吗?" -#: src/modules.md:29 +#: src/generics/methods.md:26 msgid "" -"Crates are a tree of modules, where a binary crate creates an executable and " -"a library crate compiles to a library." -msgstr "" +"This is because it is a generic implementation section for generic type. " +"They are independently generic." +msgstr "这是因为它是泛型类型的泛型实现部分。它们是独立的泛型内容。" -#: src/modules.md:30 -msgid "Modules define organization, scope, and are the focus of this section." -msgstr "" +#: src/generics/methods.md:27 +msgid "It means these methods are defined for any `T`." +msgstr "这意味着这些方法是针对所有 `T` 定义的。" -#: src/modules/visibility.md:3 -msgid "Modules are a privacy boundary:" -msgstr "" +#: src/generics/methods.md:28 +msgid "It is possible to write `impl Point { .. }`. " +msgstr "可以编写 `impl Point { .. }`。 " -#: src/modules/visibility.md:5 -msgid "Module items are private by default (hides implementation details)." +#: src/generics/methods.md:29 +msgid "" +"`Point` is still generic and you can use `Point`, but methods in this " +"block will only be available for `Point`." msgstr "" +"`Point` 依然是一个泛型,并且您可以使用 `Point`,但此块中的方法将仅适用" +"于 `Point`。" -#: src/modules/visibility.md:6 -msgid "Parent and sibling items are always visible." -msgstr "" +#: src/generics/monomorphization.md:3 +msgid "Generic code is turned into non-generic code based on the call sites:" +msgstr "泛型代码根据调用位置转换为非泛型代码:" -#: src/modules/visibility.md:7 +#: src/generics/monomorphization.md:12 +msgid "behaves as if you wrote" +msgstr "具体行为与您所编写的一样" + +#: src/generics/monomorphization.md:31 msgid "" -"In other words, if an item is visible in module `foo`, it's visible in all " -"the descendants of `foo`." +"This is a zero-cost abstraction: you get exactly the same result as if you " +"had hand-coded the data structures without the abstraction." msgstr "" +"这是零成本的抽象化处理:您得到的结果不会受到影响,也就是说,与在没有进行抽象" +"化处理的情况下,对数据结构进行手动编码时的结果一样。" -#: src/modules/visibility.md:10 +#: src/traits.md:3 +msgid "" +"Rust lets you abstract over types with traits. They're similar to interfaces:" +msgstr "Rust 让您可以依据特征对类型进行抽象化处理。特征与接口类似:" + +#: src/traits.md:5 msgid "" "```rust,editable\n" -"mod outer {\n" -" fn private() {\n" -" println!(\"outer::private\");\n" +"trait Pet {\n" +" fn name(&self) -> String;\n" +"}\n" +"\n" +"struct Dog {\n" +" name: String,\n" +"}\n" +"\n" +"struct Cat;\n" +"\n" +"impl Pet for Dog {\n" +" fn name(&self) -> String {\n" +" self.name.clone()\n" " }\n" +"}\n" "\n" -" pub fn public() {\n" -" println!(\"outer::public\");\n" +"impl Pet for Cat {\n" +" fn name(&self) -> String {\n" +" String::from(\"The cat\") // No name, cats won't respond to it " +"anyway.\n" " }\n" +"}\n" "\n" -" mod inner {\n" -" fn private() {\n" -" println!(\"outer::inner::private\");\n" -" }\n" +"fn greet(pet: &P) {\n" +" println!(\"Who's a cutie? {} is!\", pet.name());\n" +"}\n" "\n" -" pub fn public() {\n" -" println!(\"outer::inner::public\");\n" -" super::private();\n" -" }\n" +"fn main() {\n" +" let fido = Dog { name: \"Fido\".into() };\n" +" greet(&fido);\n" +"\n" +" let captain_floof = Cat;\n" +" greet(&captain_floof);\n" +"}\n" +"```" +msgstr "" + +#: src/traits/trait-objects.md:3 +msgid "" +"Trait objects allow for values of different types, for instance in a " +"collection:" +msgstr "特征(Trait)对象可接受不同类型的值,举例来说,在集合中会是这样:" + +#: src/traits/trait-objects.md:5 +msgid "" +"```rust,editable\n" +"trait Pet {\n" +" fn name(&self) -> String;\n" +"}\n" +"\n" +"struct Dog {\n" +" name: String,\n" +"}\n" +"\n" +"struct Cat;\n" +"\n" +"impl Pet for Dog {\n" +" fn name(&self) -> String {\n" +" self.name.clone()\n" +" }\n" +"}\n" +"\n" +"impl Pet for Cat {\n" +" fn name(&self) -> String {\n" +" String::from(\"The cat\") // No name, cats won't respond to it " +"anyway.\n" " }\n" "}\n" "\n" "fn main() {\n" -" outer::public();\n" +" let pets: Vec> = vec![\n" +" Box::new(Cat),\n" +" Box::new(Dog { name: String::from(\"Fido\") }),\n" +" ];\n" +" for pet in pets {\n" +" println!(\"Hello {}!\", pet.name());\n" +" }\n" "}\n" "```" msgstr "" -#: src/modules/visibility.md:39 -msgid "Use the `pub` keyword to make modules public." -msgstr "" +#: src/traits/trait-objects.md:40 +msgid "Memory layout after allocating `pets`:" +msgstr "以下是分配 `pets` 后的内存布局:" -#: src/modules/visibility.md:41 +#: src/traits/trait-objects.md:42 msgid "" -"Additionally, there are advanced `pub(...)` specifiers to restrict the scope " -"of public visibility." +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - " +"- -.\n" +": : : :\n" +": " +"pets : : :\n" +": +-----------+-------+ : : +-----+-----" +"+ :\n" +": | ptr | o---+---+-----+-->| o o | o o " +"| :\n" +": | len | 2 | : : +-|-|-+-|-|-" +"+ :\n" +": | capacity | 2 | : : | | | | +---------------" +"+ :\n" +": +-----------+-------+ : : | | | '-->| name: \"Fido\" " +"| :\n" +": : : | | | +---------------" +"+ :\n" +"`- - - - - - - - - - - - - -' : | | " +"| :\n" +" : | | | +----------------------" +"+ : \n" +" : | | '---->| \"::name\" " +"| :\n" +" : | | +----------------------" +"+ : \n" +" : | " +"| : \n" +" : | | +-" +"+ : \n" +" : | '-->|" +"\\| : \n" +" : | +-" +"+ : \n" +" : " +"| : \n" +" : | +----------------------" +"+ : \n" +" : '---->| \"::name\" " +"| : \n" +" : +----------------------" +"+ :\n" +" : :\n" +" '- - - - - - - - - - - - - - - - - - - - - " +"- -'\n" +"\n" +"```" msgstr "" -#: src/modules/visibility.md:43 +#: src/traits/trait-objects.md:72 msgid "" -"See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-" -"privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)." +"Types that implement a given trait may be of different sizes. This makes it " +"impossible to have things like `Vec` in the example above." msgstr "" -#: src/modules/visibility.md:44 -msgid "Configuring `pub(crate)` visibility is a common pattern." +#: src/traits/trait-objects.md:73 +msgid "" +"`dyn Pet` is a way to tell the compiler about a dynamically sized type that " +"implements `Pet`." msgstr "" -#: src/modules/visibility.md:45 -msgid "Less commonly, you can give visibility to a specific path." +#: src/traits/trait-objects.md:74 +msgid "" +"In the example, `pets` holds _fat pointers_ to objects that implement `Pet`. " +"The fat pointer consists of two components, a pointer to the actual object " +"and a pointer to the virtual method table for the `Pet` implementation of " +"that particular object." msgstr "" -#: src/modules/visibility.md:46 -msgid "" -"In any case, visibility must be granted to an ancestor module (and all of " -"its descendants)." +#: src/traits/trait-objects.md:75 +msgid "Compare these outputs in the above example:" msgstr "" -#: src/modules/paths.md:3 -msgid "Paths are resolved as follows:" -msgstr "" - -#: src/modules/paths.md:5 -msgid "As a relative path:" -msgstr "" - -#: src/modules/paths.md:6 -msgid "`foo` or `self::foo` refers to `foo` in the current module," -msgstr "" - -#: src/modules/paths.md:7 -msgid "`super::foo` refers to `foo` in the parent module." -msgstr "" - -#: src/modules/paths.md:9 -msgid "As an absolute path:" -msgstr "" - -#: src/modules/paths.md:10 -msgid "`crate::foo` refers to `foo` in the root of the current crate," -msgstr "" - -#: src/modules/paths.md:11 -msgid "`bar::foo` refers to `foo` in the `bar` crate." +#: src/traits/trait-objects.md:76 +msgid "" +"```rust,ignore\n" +" println!(\"{} {}\", std::mem::size_of::(), std::mem::size_of::" +"());\n" +" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::" +"<&Cat>());\n" +" println!(\"{}\", std::mem::size_of::<&dyn Pet>());\n" +" println!(\"{}\", std::mem::size_of::>());\n" +"```" msgstr "" -#: src/modules/paths.md:13 +#: src/traits/deriving-traits.md:3 msgid "" -"A module can bring symbols from another module into scope with `use`. You " -"will typically see something like this at the top of each module:" +"Rust derive macros work by automatically generating code that implements the " +"specified traits for a data structure." msgstr "" -#: src/modules/paths.md:16 +#: src/traits/deriving-traits.md:5 +#, fuzzy +msgid "You can let the compiler derive a number of traits as follows:" +msgstr "您可以让编译器派生多个特征:" + +#: src/traits/deriving-traits.md:7 msgid "" "```rust,editable\n" -"use std::collections::HashSet;\n" -"use std::mem::transmute;\n" +"#[derive(Debug, Clone, PartialEq, Eq, Default)]\n" +"struct Player {\n" +" name: String,\n" +" strength: u8,\n" +" hit_points: u8,\n" +"}\n" +"\n" +"fn main() {\n" +" let p1 = Player::default();\n" +" let p2 = p1.clone();\n" +" println!(\"Is {:?}\\nequal to {:?}?\\nThe answer is {}!\", &p1, &p2,\n" +" if p1 == p2 { \"yes\" } else { \"no\" });\n" +"}\n" "```" msgstr "" -#: src/modules/filesystem.md:3 -msgid "The module content can be omitted:" -msgstr "" +#: src/traits/default-methods.md:3 +msgid "Traits can implement behavior in terms of other trait methods:" +msgstr "特征可以依照其他特征方法来实现行为:" -#: src/modules/filesystem.md:5 +#: src/traits/default-methods.md:5 msgid "" -"```rust,editable,compile_fail\n" -"mod garden;\n" +"```rust,editable\n" +"trait Equals {\n" +" fn equals(&self, other: &Self) -> bool;\n" +" fn not_equals(&self, other: &Self) -> bool {\n" +" !self.equals(other)\n" +" }\n" +"}\n" +"\n" +"#[derive(Debug)]\n" +"struct Centimeter(i16);\n" +"\n" +"impl Equals for Centimeter {\n" +" fn equals(&self, other: &Centimeter) -> bool {\n" +" self.0 == other.0\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let a = Centimeter(10);\n" +" let b = Centimeter(20);\n" +" println!(\"{a:?} equals {b:?}: {}\", a.equals(&b));\n" +" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equals(&b));\n" +"}\n" "```" msgstr "" -#: src/modules/filesystem.md:9 -msgid "The `garden` module content is found at:" -msgstr "" - -#: src/modules/filesystem.md:11 -msgid "`src/garden.rs` (modern Rust 2018 style)" -msgstr "" - -#: src/modules/filesystem.md:12 -msgid "`src/garden/mod.rs` (older Rust 2015 style)" -msgstr "" - -#: src/modules/filesystem.md:14 -msgid "Similarly, a `garden::vegetables` module can be found at:" -msgstr "" - -#: src/modules/filesystem.md:16 -msgid "`src/garden/vegetables.rs` (modern Rust 2018 style)" +#: src/traits/default-methods.md:32 +msgid "" +"Traits may specify pre-implemented (default) methods and methods that users " +"are required to implement themselves. Methods with default implementations " +"can rely on required methods." msgstr "" -#: src/modules/filesystem.md:17 -msgid "`src/garden/vegetables/mod.rs` (older Rust 2015 style)" +#: src/traits/default-methods.md:35 +msgid "Move method `not_equals` to a new trait `NotEquals`." msgstr "" -#: src/modules/filesystem.md:19 -msgid "The `crate` root is in:" +#: src/traits/default-methods.md:37 +msgid "Make `Equals` a super trait for `NotEquals`." msgstr "" -#: src/modules/filesystem.md:21 -msgid "`src/lib.rs` (for a library crate)" +#: src/traits/default-methods.md:46 +msgid "Provide a blanket implementation of `NotEquals` for `Equals`." msgstr "" -#: src/modules/filesystem.md:22 -msgid "`src/main.rs` (for a binary crate)" +#: src/traits/default-methods.md:58 +msgid "" +"With the blanket implementation, you no longer need `Equals` as a super " +"trait for `NotEqual`." msgstr "" -#: src/modules/filesystem.md:24 +#: src/traits/trait-bounds.md:3 msgid "" -"Modules defined in files can be documented, too, using \"inner doc " -"comments\". These document the item that contains them -- in this case, a " -"module." +"When working with generics, you often want to require the types to implement " +"some trait, so that you can call this trait's methods." msgstr "" +"使用泛型时,您通常会想要利用类型来实现某些特性, 这样才能调用此特征的方法。" + +#: src/traits/trait-bounds.md:6 +msgid "You can do this with `T: Trait` or `impl Trait`:" +msgstr "您可以使用 `T: Trait` 或 `impl Trait` 执行此操作:" -#: src/modules/filesystem.md:27 +#: src/traits/trait-bounds.md:8 msgid "" -"```rust,editable,compile_fail\n" -"//! This module implements the garden, including a highly performant " -"germination\n" -"//! implementation.\n" +"```rust,editable\n" +"fn duplicate(a: T) -> (T, T) {\n" +" (a.clone(), a.clone())\n" +"}\n" "\n" -"// Re-export types from this module.\n" -"pub use seeds::SeedPacket;\n" -"pub use garden::Garden;\n" +"// Syntactic sugar for:\n" +"// fn add_42_millions>(x: T) -> i32 {\n" +"fn add_42_millions(x: impl Into) -> i32 {\n" +" x.into() + 42_000_000\n" +"}\n" "\n" -"/// Sow the given seed packets.\n" -"pub fn sow(seeds: Vec) { todo!() }\n" +"// struct NotClonable;\n" "\n" -"/// Harvest the produce in the garden that is ready.\n" -"pub fn harvest(garden: &mut Garden) { todo!() }\n" +"fn main() {\n" +" let foo = String::from(\"foo\");\n" +" let pair = duplicate(foo);\n" +" println!(\"{pair:?}\");\n" +"\n" +" let many = add_42_millions(42_i8);\n" +" println!(\"{many}\");\n" +" let many_more = add_42_millions(10_000_000);\n" +" println!(\"{many_more}\");\n" +"}\n" "```" msgstr "" -#: src/modules/filesystem.md:44 -msgid "" -"The change from `module/mod.rs` to `module.rs` doesn't preclude the use of " -"submodules in Rust 2018. (It was mandatory in Rust 2015.)" -msgstr "" +#: src/traits/trait-bounds.md:35 +msgid "Show a `where` clause, students will encounter it when reading code." +msgstr "显示 `where` 子句,学员在阅读代码时会看到它。" -#: src/modules/filesystem.md:47 -msgid "The following is valid:" -msgstr "" +#: src/traits/trait-bounds.md:46 +msgid "It declutters the function signature if you have many parameters." +msgstr "它会在您有多个形参的情况下整理函数签名。" -#: src/modules/filesystem.md:49 -msgid "" -"```ignore\n" -"src/\n" -"├── main.rs\n" -"├── top_module.rs\n" -"└── top_module/\n" -" └── sub_module.rs\n" -"```" -msgstr "" +#: src/traits/trait-bounds.md:47 +msgid "It has additional features making it more powerful." +msgstr "它具有额外功能,因此也更强大。" -#: src/modules/filesystem.md:57 +#: src/traits/trait-bounds.md:48 msgid "" -"The main reason for the change is to prevent many files named `mod.rs`, " -"which can be hard to distinguish in IDEs." +"If someone asks, the extra feature is that the type on the left of \":\" can " +"be arbitrary, like `Option`." msgstr "" +"如果有人提问,便阐明额外功能是指“:”左侧的类别可为任意值,例如 `Option`。" + +#: src/traits/impl-trait.md:1 +msgid "`impl Trait`" +msgstr "`impl Trait`" -#: src/modules/filesystem.md:60 +#: src/traits/impl-trait.md:3 msgid "" -"Rust will look for modules in `modulename/mod.rs` and `modulename.rs`, but " -"this can be changed with a compiler directive:" -msgstr "" +"Similar to trait bounds, an `impl Trait` syntax can be used in function " +"arguments and return values:" +msgstr "与特征边界类似,`impl Trait` 语法可以在函数实参 和返回值中使用:" -#: src/modules/filesystem.md:63 +#: src/traits/impl-trait.md:6 msgid "" -"```rust,ignore\n" -"#[path = \"some/path.rs\"]\n" -"mod some_module { }\n" +"```rust,editable\n" +"use std::fmt::Display;\n" +"\n" +"fn get_x(name: impl Display) -> impl Display {\n" +" format!(\"Hello {name}\")\n" +"}\n" +"\n" +"fn main() {\n" +" let x = get_x(\"foo\");\n" +" println!(\"{x}\");\n" +"}\n" "```" msgstr "" -#: src/modules/filesystem.md:68 -msgid "" -"This is useful, for example, if you would like to place tests for a module " -"in a file named `some_module_test.rs`, similar to the convention in Go." -msgstr "" +#: src/traits/impl-trait.md:19 +msgid "`impl Trait` allows you to work with types which you cannot name." +msgstr "`impl Trait` 让您可使用无法命名的类型。" -#: src/exercises/day-2/afternoon.md:1 -msgid "Day 2: Afternoon Exercises" -msgstr "第二天下午习题" +#: src/traits/impl-trait.md:23 +msgid "" +"The meaning of `impl Trait` is a bit different in the different positions." +msgstr "`impl Trait` 的意义因使用位置而略有不同。" -#: src/exercises/day-2/afternoon.md:3 -msgid "The exercises for this afternoon will focus on strings and iterators." -msgstr "今天下午的习题将重点关注字符串(string)和迭代器(iterator)。" +#: src/traits/impl-trait.md:25 +msgid "" +"For a parameter, `impl Trait` is like an anonymous generic parameter with a " +"trait bound." +msgstr "对形参来说,`impl Trait` 就像是具有特征边界的匿名泛型形参。" -#: src/exercises/day-2/luhn.md:3 +#: src/traits/impl-trait.md:27 msgid "" -"The [Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is used " -"to validate credit card numbers. The algorithm takes a string as input and " -"does the following to validate the credit card number:" +"For a return type, it means that the return type is some concrete type that " +"implements the trait, without naming the type. This can be useful when you " +"don't want to expose the concrete type in a public API." msgstr "" +"对返回值类型来说,它则意味着返回值类型就是实现该特征的某具体类型, 无需为该类" +"型命名。如果您不想在公共 API 中公开该具体类型,便可 使用此方法。" -#: src/exercises/day-2/luhn.md:7 -msgid "Ignore all spaces. Reject number with less than two digits." +#: src/traits/impl-trait.md:31 +msgid "" +"Inference is hard in return position. A function returning `impl Foo` picks " +"the concrete type it returns, without writing it out in the source. A " +"function returning a generic type like `collect() -> B` can return any " +"type satisfying `B`, and the caller may need to choose one, such as with " +"`let x: Vec<_> = foo.collect()` or with the turbofish, `foo.collect::" +">()`." msgstr "" +"在返回位置处进行推断有一定难度。会返回 `impl Foo` 的函数会挑选 自身返回的具体" +"类型,而不必在来源中写出此信息。会返回 泛型类型(例如 `collect() -> B`)的" +"函数则可返回符合 `B` 的任何类型,而调用方可能需要选择一个类型,例如使用 `let " +"x: Vec<_> = foo.collect()` 或使用以下 Turbofish:`foo.collect::>()`。" -#: src/exercises/day-2/luhn.md:9 +#: src/traits/impl-trait.md:37 msgid "" -"Moving from right to left, double every second digit: for the number `1234`, " -"we double `3` and `1`." +"This example is great, because it uses `impl Display` twice. It helps to " +"explain that nothing here enforces that it is _the same_ `impl Display` " +"type. If we used a single `T: Display`, it would enforce the constraint " +"that input `T` and return `T` type are the same type. It would not work for " +"this particular function, as the type we expect as input is likely not what " +"`format!` returns. If we wanted to do the same via `: Display` syntax, we'd " +"need two independent generic parameters." msgstr "" +"这是一个非常棒的示例,因为它使用了两次 `impl Display`。这有助于说明 此处没有" +"任何项目会强制使用相同的 `impl Display` 类型。如果我们使用单个 `T: Display`," +"它会强制限制输入 `T` 和返回 `T` 均为同一类型。 这并不适用于这个特定函数,因为" +"我们预期作为输入的类型可能 不会是 `format!` 返回的值。如果我们希望通过 `: " +"Display` 语法执行相同的操作,则需要两个 独立的泛型形参。" -#: src/exercises/day-2/luhn.md:12 +#: src/traits/important-traits.md:3 msgid "" -"After doubling a digit, sum the digits. So doubling `7` becomes `14` which " -"becomes `5`." -msgstr "" +"We will now look at some of the most common traits of the Rust standard " +"library:" +msgstr "现在,我们来看看 Rust 标准库的一些最常见的特征:" -#: src/exercises/day-2/luhn.md:15 -msgid "Sum all the undoubled and doubled digits." -msgstr "" +#: src/traits/important-traits.md:5 +msgid "" +"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " +"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " +"used in `for` loops," +msgstr "" +"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) 和 " +"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " +"用于 `for` 循环中," -#: src/exercises/day-2/luhn.md:17 -msgid "The credit card number is valid if the sum ends with `0`." +#: src/traits/important-traits.md:6 +msgid "" +"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) and [`Into`]" +"(https://doc.rust-lang.org/std/convert/trait.Into.html) used to convert " +"values," msgstr "" +"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) 和 [`Into`]" +"(https://doc.rust-lang.org/std/convert/trait.Into.html) 用于转换值," -#: src/exercises/day-2/luhn.md:19 +#: src/traits/important-traits.md:7 msgid "" -"Copy the following code to and implement the " -"function:" +"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and [`Write`]" +"(https://doc.rust-lang.org/std/io/trait.Write.html) used for IO," msgstr "" +"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) 和 [`Write`]" +"(https://doc.rust-lang.org/std/io/trait.Write.html) 用于实现 IO," -#: src/exercises/day-2/luhn.md:23 +#: src/traits/important-traits.md:8 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub fn luhn(cc_number: &str) -> bool {\n" -" unimplemented!()\n" -"}\n" -"\n" -"#[test]\n" -"fn test_non_digit_cc_number() {\n" -" assert!(!luhn(\"foo\"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_empty_cc_number() {\n" -" assert!(!luhn(\"\"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_single_digit_cc_number() {\n" -" assert!(!luhn(\"0\"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_two_digit_cc_number() {\n" -" assert!(luhn(\" 0 0 \"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_valid_cc_number() {\n" -" assert!(luhn(\"4263 9826 4026 9299\"));\n" -" assert!(luhn(\"4539 3195 0343 6467\"));\n" -" assert!(luhn(\"7992 7398 713\"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_invalid_cc_number() {\n" -" assert!(!luhn(\"4223 9826 4026 9299\"));\n" -" assert!(!luhn(\"4539 3195 0343 6476\"));\n" -" assert!(!luhn(\"8273 1232 7352 0569\"));\n" -"}\n" -"\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" -"```" +"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html), [`Mul`](https://" +"doc.rust-lang.org/std/ops/trait.Mul.html), ... used for operator " +"overloading, and" msgstr "" +"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html)、[`Mul`](https://" +"doc.rust-lang.org/std/ops/trait.Mul.html) 等用于实现运算符重载" -#: src/exercises/day-2/strings-iterators.md:3 +#: src/traits/important-traits.md:9 msgid "" -"In this exercise, you are implementing a routing component of a web server. " -"The server is configured with a number of _path prefixes_ which are matched " -"against _request paths_. The path prefixes can contain a wildcard character " -"which matches a full segment. See the unit tests below." +"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) used for " +"defining destructors." msgstr "" +"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) 用于定义析构函" +"数。" -#: src/exercises/day-2/strings-iterators.md:8 +#: src/traits/important-traits.md:10 msgid "" -"Copy the following code to and make the tests " -"pass. Try avoiding allocating a `Vec` for your intermediate results:" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) used " +"to construct a default instance of a type." msgstr "" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) 用于构" +"建相应类型的默认实例。" -#: src/exercises/day-2/strings-iterators.md:12 +#: src/traits/iterator.md:1 +msgid "Iterators" +msgstr "迭代器" + +#: src/traits/iterator.md:3 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" -" unimplemented!()\n" +"You can implement the [`Iterator`](https://doc.rust-lang.org/std/iter/trait." +"Iterator.html) trait on your own types:" +msgstr "" +"您可以自行实现 [`Iterator`](https://doc.rust-lang.org/std/iter/trait." +"Iterator.html) 特征:" + +#: src/traits/iterator.md:5 +msgid "" +"```rust,editable\n" +"struct Fibonacci {\n" +" curr: u32,\n" +" next: u32,\n" "}\n" "\n" -"#[test]\n" -"fn test_matches_without_wildcard() {\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/" -"abc-123\"));\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/" -"books\"));\n" +"impl Iterator for Fibonacci {\n" +" type Item = u32;\n" "\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/" -"publishers\"));\n" +" fn next(&mut self) -> Option {\n" +" let new_next = self.curr + self.next;\n" +" self.curr = self.next;\n" +" self.next = new_next;\n" +" Some(self.curr)\n" +" }\n" "}\n" "\n" -"#[test]\n" -"fn test_matches_with_wildcard() {\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/books\"\n" -" ));\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/bar/books\"\n" -" ));\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/books/book1\"\n" -" ));\n" -"\n" -" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/" -"publishers\"));\n" -" assert!(!prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/booksByAuthor\"\n" -" ));\n" +"fn main() {\n" +" let fib = Fibonacci { curr: 0, next: 1 };\n" +" for (i, n) in fib.enumerate().take(5) {\n" +" println!(\"fib({i}): {n}\");\n" +" }\n" "}\n" "```" msgstr "" -#: src/welcome-day-3.md:1 -msgid "Welcome to Day 3" -msgstr "" - -#: src/welcome-day-3.md:3 -msgid "Today, we will cover some more advanced topics of Rust:" -msgstr "" - -#: src/welcome-day-3.md:5 +#: src/traits/iterator.md:32 msgid "" -"Traits: deriving traits, default methods, and important standard library " -"traits." +"The `Iterator` trait implements many common functional programming " +"operations over collections (e.g. `map`, `filter`, `reduce`, etc). This is " +"the trait where you can find all the documentation about them. In Rust these " +"functions should produce the code as efficient as equivalent imperative " +"implementations." msgstr "" +"`Iterator` 特征会对集合实现许多常见的函数程序操作, 例如 `` map``filter ``和" +"`reduce` 等。您可以通过此特征找到有关它们的所有 文档。在 Rust 中,这些函数应" +"生成代码,且生成的代码应与等效命令式实现一样 高效。" -#: src/welcome-day-3.md:8 +#: src/traits/iterator.md:37 msgid "" -"Generics: generic data types, generic methods, monomorphization, and trait " -"objects." +"`IntoIterator` is the trait that makes for loops work. It is implemented by " +"collection types such as `Vec` and references to them such as `&Vec` " +"and `&[T]`. Ranges also implement it. This is why you can iterate over a " +"vector with `for i in some_vec { .. }` but `some_vec.next()` doesn't exist." msgstr "" +"`IntoIterator` 是迫使 for 循环运作的特征。此特征由集合类型 (例如 `Vec`)" +"和相关引用(例如 `&Vec` 和 `&[T]`)而实现。此外,范围也会实现这项特征。因" +"此, 您可以使用 `for i in some_vec { .. }` 来遍历某矢量,但 `some_vec." +"next()` 不存在。" -#: src/welcome-day-3.md:11 -msgid "Error handling: panics, `Result`, and the try operator `?`." +#: src/traits/from-iterator.md:3 +msgid "" +"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " +"lets you build a collection from an [`Iterator`](https://doc.rust-lang.org/" +"std/iter/trait.Iterator.html)." msgstr "" +"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " +"让您可通过 [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator." +"html) 构建一个集合。" -#: src/welcome-day-3.md:13 -msgid "Testing: unit tests, documentation tests, and integration tests." +#: src/traits/from-iterator.md:17 +msgid "" +"`Iterator` implements `fn collect(self) -> B where B: FromIterator, Self: Sized`" msgstr "" +"`Iterator` 会实现 `fn collect(self) -> B where B: FromIterator, Self: Sized`" -#: src/welcome-day-3.md:15 +#: src/traits/from-iterator.md:23 msgid "" -"Unsafe Rust: raw pointers, static variables, unsafe functions, and extern " -"functions." +"There are also implementations which let you do cool things like convert an " +"`Iterator>` into a `Result, E>`." msgstr "" +"还有一些实现,让您可执行一些很酷的操作,比如 将 `Iterator>` 转换成 `Result, E>`。" -#: src/generics.md:3 +#: src/traits/from-into.md:1 +msgid "`From` and `Into`" +msgstr "`From` 和 `Into`" + +#: src/traits/from-into.md:3 msgid "" -"Rust support generics, which lets you abstract an algorithm (such as " -"sorting) over the types used in the algorithm." +"Types implement [`From`](https://doc.rust-lang.org/std/convert/trait.From." +"html) and [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) to " +"facilitate type conversions:" msgstr "" -"Rust 支持泛型,允许您根据算法(例如排序)中使用的类型对算法进行抽象化处理。" - -#: src/generics/data-types.md:3 -msgid "You can use generics to abstract over the concrete field type:" -msgstr "您可以使用泛型对具体字段类型进行抽象化处理:" +"类型会实现 [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) " +"和 [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) 以加快类型" +"转换:" -#: src/generics/data-types.md:5 +#: src/traits/from-into.md:5 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point {\n" -" x: T,\n" -" y: T,\n" -"}\n" -"\n" "fn main() {\n" -" let integer = Point { x: 5, y: 10 };\n" -" let float = Point { x: 1.0, y: 4.0 };\n" -" println!(\"{integer:?} and {float:?}\");\n" +" let s = String::from(\"hello\");\n" +" let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);\n" +" let one = i16::from(true);\n" +" let bigger = i32::from(123i16);\n" +" println!(\"{s}, {addr}, {one}, {bigger}\");\n" "}\n" "```" msgstr "" -#: src/generics/data-types.md:21 -msgid "Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`." -msgstr "" - -#: src/generics/data-types.md:23 -msgid "Fix the code to allow points that have elements of different types." +#: src/traits/from-into.md:15 +msgid "" +"[`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) is " +"automatically implemented when [`From`](https://doc.rust-lang.org/std/" +"convert/trait.From.html) is implemented:" msgstr "" +"实现 [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) 后,系统" +"会自动实现 [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html):" -#: src/generics/methods.md:3 -msgid "You can declare a generic type on your `impl` block:" -msgstr "您可以在 `impl` 块中声明通用类型:" - -#: src/generics/methods.md:5 +#: src/traits/from-into.md:17 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(T, T);\n" -"\n" -"impl Point {\n" -" fn x(&self) -> &T {\n" -" &self.0 // + 10\n" -" }\n" -"\n" -" // fn set_x(&mut self, x: T)\n" -"}\n" -"\n" "fn main() {\n" -" let p = Point(5, 10);\n" -" println!(\"p.x = {}\", p.x());\n" +" let s: String = \"hello\".into();\n" +" let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();\n" +" let one: i16 = true.into();\n" +" let bigger: i32 = 123i16.into();\n" +" println!(\"{s}, {addr}, {one}, {bigger}\");\n" "}\n" "```" msgstr "" -#: src/generics/methods.md:25 +#: src/traits/from-into.md:29 msgid "" -"_Q:_ Why `T` is specified twice in `impl Point {}`? Isn't that " -"redundant?" -msgstr "" -"\\*问:\\*为什么 `T` 在 `impl Point {}` 中指定了两次?这不是多余的吗?" +"That's why it is common to only implement `From`, as your type will get " +"`Into` implementation too." +msgstr "这就是为什么通常只需实现 `From`,因为您的类型也会实现 `Into`。" -#: src/generics/methods.md:26 +#: src/traits/from-into.md:30 msgid "" -"This is because it is a generic implementation section for generic type. " -"They are independently generic." -msgstr "这是因为它是泛型类型的泛型实现部分。它们是独立的泛型内容。" - -#: src/generics/methods.md:27 -msgid "It means these methods are defined for any `T`." -msgstr "这意味着这些方法是针对所有 `T` 定义的。" +"When declaring a function argument input type like \"anything that can be " +"converted into a `String`\", the rule is opposite, you should use `Into`. " +"Your function will accept types that implement `From` and those that _only_ " +"implement `Into`." +msgstr "" +"若要声明某个函数实参输入类型(例如“任何可转换成 `String` 的类型”),规则便会" +"相反,此时应使用 `Into`。 您的函数会接受可实现 `From` 的类型,以及那些仅实现 " +"`Into` 的类型。" -#: src/generics/methods.md:28 -msgid "It is possible to write `impl Point { .. }`. " -msgstr "可以编写 `impl Point { .. }`。" +#: src/traits/read-write.md:1 +msgid "`Read` and `Write`" +msgstr "`Read` 和 `Write`" -#: src/generics/methods.md:29 +#: src/traits/read-write.md:3 msgid "" -"`Point` is still generic and you can use `Point`, but methods in this " -"block will only be available for `Point`." +"Using [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and " +"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html), you can " +"abstract over `u8` sources:" msgstr "" -"`Point` 依然是一个泛型,并且您可以使用 `Point`,但此块中的方法将仅适用" -"于 `Point`。" - -#: src/generics/monomorphization.md:3 -msgid "Generic code is turned into non-generic code based on the call sites:" -msgstr "泛型代码根据调用位置转换为非泛型代码:" +"您可以使用 [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) 和 " +"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) 对 `u8` 来源" +"进行抽象化处理:" -#: src/generics/monomorphization.md:5 +#: src/traits/read-write.md:5 msgid "" "```rust,editable\n" -"fn main() {\n" -" let integer = Some(5);\n" -" let float = Some(5.0);\n" +"use std::io::{BufRead, BufReader, Read, Result};\n" +"\n" +"fn count_lines(reader: R) -> usize {\n" +" let buf_reader = BufReader::new(reader);\n" +" buf_reader.lines().count()\n" +"}\n" +"\n" +"fn main() -> Result<()> {\n" +" let slice: &[u8] = b\"foo\\nbar\\nbaz\\n\";\n" +" println!(\"lines in slice: {}\", count_lines(slice));\n" +"\n" +" let file = std::fs::File::open(std::env::current_exe()?)?;\n" +" println!(\"lines in file: {}\", count_lines(file));\n" +" Ok(())\n" "}\n" "```" msgstr "" -#: src/generics/monomorphization.md:12 -msgid "behaves as if you wrote" -msgstr "具体行为与您所编写的一样" +#: src/traits/read-write.md:23 +msgid "" +"Similarly, [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) lets " +"you abstract over `u8` sinks:" +msgstr "" +"您同样可使用 [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) 对 " +"`u8` 接收器进行抽象化处理:" -#: src/generics/monomorphization.md:14 +#: src/traits/read-write.md:25 msgid "" "```rust,editable\n" -"enum Option_i32 {\n" -" Some(i32),\n" -" None,\n" -"}\n" +"use std::io::{Result, Write};\n" "\n" -"enum Option_f64 {\n" -" Some(f64),\n" -" None,\n" +"fn log(writer: &mut W, msg: &str) -> Result<()> {\n" +" writer.write_all(msg.as_bytes())?;\n" +" writer.write_all(\"\\n\".as_bytes())\n" "}\n" "\n" -"fn main() {\n" -" let integer = Option_i32::Some(5);\n" -" let float = Option_f64::Some(5.0);\n" +"fn main() -> Result<()> {\n" +" let mut buffer = Vec::new();\n" +" log(&mut buffer, \"Hello\")?;\n" +" log(&mut buffer, \"World\")?;\n" +" println!(\"Logged: {:?}\", buffer);\n" +" Ok(())\n" "}\n" "```" msgstr "" -#: src/generics/monomorphization.md:31 -msgid "" -"This is a zero-cost abstraction: you get exactly the same result as if you " -"had hand-coded the data structures without the abstraction." -msgstr "" -"这是零成本的抽象化处理:您得到的结果不会受到影响,也就是说,与在没有进行抽象" -"化处理的情况下,对数据结构进行手动编码时的结果一样。" +#: src/traits/drop.md:1 +msgid "The `Drop` Trait" +msgstr "`Drop` 特征" -#: src/traits.md:3 +#: src/traits/drop.md:3 msgid "" -"Rust lets you abstract over types with traits. They're similar to interfaces:" -msgstr "Rust 让您可以依据特征对类型进行抽象化处理。特征与接口类似:" +"Values which implement [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop." +"html) can specify code to run when they go out of scope:" +msgstr "" +"用于实现 [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) 的值可以" +"指定在超出范围时运行的代码:" -#: src/traits.md:5 +#: src/traits/drop.md:5 msgid "" "```rust,editable\n" -"trait Pet {\n" -" fn name(&self) -> String;\n" -"}\n" -"\n" -"struct Dog {\n" -" name: String,\n" -"}\n" -"\n" -"struct Cat;\n" -"\n" -"impl Pet for Dog {\n" -" fn name(&self) -> String {\n" -" self.name.clone()\n" -" }\n" +"struct Droppable {\n" +" name: &'static str,\n" "}\n" "\n" -"impl Pet for Cat {\n" -" fn name(&self) -> String {\n" -" String::from(\"The cat\") // No name, cats won't respond to it " -"anyway.\n" +"impl Drop for Droppable {\n" +" fn drop(&mut self) {\n" +" println!(\"Dropping {}\", self.name);\n" " }\n" "}\n" "\n" -"fn greet(pet: &P) {\n" -" println!(\"Who's a cutie? {} is!\", pet.name());\n" -"}\n" -"\n" "fn main() {\n" -" let fido = Dog { name: \"Fido\".into() };\n" -" greet(&fido);\n" -"\n" -" let captain_floof = Cat;\n" -" greet(&captain_floof);\n" +" let a = Droppable { name: \"a\" };\n" +" {\n" +" let b = Droppable { name: \"b\" };\n" +" {\n" +" let c = Droppable { name: \"c\" };\n" +" let d = Droppable { name: \"d\" };\n" +" println!(\"Exiting block B\");\n" +" }\n" +" println!(\"Exiting block A\");\n" +" }\n" +" drop(a);\n" +" println!(\"Exiting main\");\n" "}\n" "```" msgstr "" -#: src/traits/trait-objects.md:3 +#: src/traits/drop.md:34 msgid "" -"Trait objects allow for values of different types, for instance in a " -"collection:" -msgstr "特征(Trait)对象可接受不同类型的值,举例来说,在集合中会是这样:" +"`drop` is called automatically, but it can be called manually like in this " +"example." +msgstr "" -#: src/traits/trait-objects.md:5 +#: src/traits/drop.md:35 +msgid "" +"If it was called manually, it won't be called at the end of the scope for " +"the second time." +msgstr "" + +#: src/traits/drop.md:36 +msgid "" +"Calling `drop` can be useful for objects that do some work on `drop`: " +"releasing locks, closing files, etc." +msgstr "" + +#: src/traits/drop.md:38 src/traits/operators.md:26 +msgid "Discussion points:" +msgstr "讨论点:" + +#: src/traits/drop.md:40 +msgid "Why doesn't `Drop::drop` take `self`?" +msgstr "为什么 `Drop::drop` 不使用 `self`?" + +#: src/traits/drop.md:41 +msgid "" +"Short-answer: If it did, `std::mem::drop` would be called at the end of the " +"block, resulting in another call to `Drop::drop`, and a stack overflow!" +msgstr "" +"简答:如果这样的话,系统会在代码块结尾 调用 `std::mem::drop`,进而引发再一次" +"调用 `Drop::drop`,并引发堆栈 溢出!" + +#: src/traits/drop.md:44 +msgid "Try replacing `drop(a)` with `a.drop()`." +msgstr "尝试用 `a.drop()` 替换 `drop(a)`。" + +#: src/traits/default.md:1 +msgid "The `Default` Trait" +msgstr "`Default` 特征" + +#: src/traits/default.md:3 +msgid "" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " +"produces a default value for a type." +msgstr "" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) 特征会" +"为类型生成默认值。" + +#: src/traits/default.md:5 msgid "" "```rust,editable\n" -"trait Pet {\n" -" fn name(&self) -> String;\n" -"}\n" -"\n" -"struct Dog {\n" -" name: String,\n" +"#[derive(Debug, Default)]\n" +"struct Derived {\n" +" x: u32,\n" +" y: String,\n" +" z: Implemented,\n" "}\n" "\n" -"struct Cat;\n" -"\n" -"impl Pet for Dog {\n" -" fn name(&self) -> String {\n" -" self.name.clone()\n" -" }\n" -"}\n" +"#[derive(Debug)]\n" +"struct Implemented(String);\n" "\n" -"impl Pet for Cat {\n" -" fn name(&self) -> String {\n" -" String::from(\"The cat\") // No name, cats won't respond to it " -"anyway.\n" +"impl Default for Implemented {\n" +" fn default() -> Self {\n" +" Self(\"John Smith\".into())\n" " }\n" "}\n" "\n" "fn main() {\n" -" let pets: Vec> = vec![\n" -" Box::new(Cat),\n" -" Box::new(Dog { name: String::from(\"Fido\") }),\n" -" ];\n" -" for pet in pets {\n" -" println!(\"Hello {}!\", pet.name());\n" -" }\n" +" let default_struct = Derived::default();\n" +" println!(\"{default_struct:#?}\");\n" +"\n" +" let almost_default_struct = Derived {\n" +" y: \"Y is set!\".into(),\n" +" ..Derived::default()\n" +" };\n" +" println!(\"{almost_default_struct:#?}\");\n" +"\n" +" let nothing: Option = None;\n" +" println!(\"{:#?}\", nothing.unwrap_or_default());\n" "}\n" -"```" -msgstr "" - -#: src/traits/trait-objects.md:40 -msgid "Memory layout after allocating `pets`:" -msgstr "以下是分配 `pets` 后的内存布局:" - -#: src/traits/trait-objects.md:42 -msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - " -"- -.\n" -": : : :\n" -": " -"pets : : :\n" -": +-----------+-------+ : : +-----+-----" -"+ :\n" -": | ptr | o---+---+-----+-->| o o | o o " -"| :\n" -": | len | 2 | : : +-|-|-+-|-|-" -"+ :\n" -": | capacity | 2 | : : | | | | +---------------" -"+ :\n" -": +-----------+-------+ : : | | | '-->| name: \"Fido\" " -"| :\n" -": : : | | | +---------------" -"+ :\n" -"`- - - - - - - - - - - - - -' : | | " -"| :\n" -" : | | | +----------------------" -"+ : \n" -" : | | '---->| \"::name\" " -"| :\n" -" : | | +----------------------" -"+ : \n" -" : | " -"| : \n" -" : | | +-" -"+ : \n" -" : | '-->|" -"\\| : \n" -" : | +-" -"+ : \n" -" : " -"| : \n" -" : | +----------------------" -"+ : \n" -" : '---->| \"::name\" " -"| : \n" -" : +----------------------" -"+ :\n" -" : :\n" -" '- - - - - - - - - - - - - - - - - - - - - " -"- -'\n" "\n" "```" msgstr "" -#: src/traits/trait-objects.md:72 +#: src/traits/default.md:40 msgid "" -"Types that implement a given trait may be of different sizes. This makes it " -"impossible to have things like `Vec` in the example above." -msgstr "" +"It can be implemented directly or it can be derived via `#[derive(Default)]`." +msgstr "系统可以直接实现它,也可以通过 `#[derive(Default)]` 派生出它。" -#: src/traits/trait-objects.md:73 +#: src/traits/default.md:41 +#, fuzzy msgid "" -"`dyn Pet` is a way to tell the compiler about a dynamically sized type that " -"implements `Pet`." -msgstr "" +"A derived implementation will produce a value where all fields are set to " +"their default values." +msgstr "派生的实现会生成一个实例,其中字段全都设为其默认值。" -#: src/traits/trait-objects.md:74 +#: src/traits/default.md:42 +msgid "This means all types in the struct must implement `Default` too." +msgstr "这意味着,该结构体中的所有类型也都必须实现 `Default`。" + +#: src/traits/default.md:43 msgid "" -"In the example, `pets` holds _fat pointers_ to objects that implement `Pet`. " -"The fat pointer consists of two components, a pointer to the actual object " -"and a pointer to the virtual method table for the `Pet` implementation of " -"that particular object." +"Standard Rust types often implement `Default` with reasonable values (e.g. " +"`0`, `\"\"`, etc)." msgstr "" +"标准的 Rust 类型通常会以合理的值(例如 `` 0``\"\" `` 等)实现 `Default`。" -#: src/traits/trait-objects.md:75 -msgid "Compare these outputs in the above example:" -msgstr "" +#: src/traits/default.md:44 +msgid "The partial struct copy works nicely with default." +msgstr "部分结构体副本可与默认值完美搭配运作。" -#: src/traits/trait-objects.md:76 +#: src/traits/default.md:45 msgid "" -"```rust,ignore\n" -" println!(\"{} {}\", std::mem::size_of::(), std::mem::size_of::" -"());\n" -" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::" -"<&Cat>());\n" -" println!(\"{}\", std::mem::size_of::<&dyn Pet>());\n" -" println!(\"{}\", std::mem::size_of::>());\n" -"```" +"Rust standard library is aware that types can implement `Default` and " +"provides convenience methods that use it." +msgstr "Rust 标准库了解类型可能会实现 `Default`,因此提供了便利的使用方式。" + +#: src/traits/default.md:46 +msgid "" +"the `..` syntax is called [struct update syntax](https://doc.rust-lang.org/" +"book/ch05-01-defining-structs.html#creating-instances-from-other-instances-" +"with-struct-update-syntax)" msgstr "" -#: src/traits/deriving-traits.md:3 -msgid "You can let the compiler derive a number of traits:" -msgstr "您可以让编译器派生多个特征:" +#: src/traits/operators.md:1 +msgid "`Add`, `Mul`, ..." +msgstr "`` Add``Mul ``…" -#: src/traits/deriving-traits.md:5 +#: src/traits/operators.md:3 msgid "" -"```rust,editable\n" -"#[derive(Debug, Clone, PartialEq, Eq, Default)]\n" -"struct Player {\n" -" name: String,\n" -" strength: u8,\n" -" hit_points: u8,\n" -"}\n" -"\n" -"fn main() {\n" -" let p1 = Player::default();\n" -" let p2 = p1.clone();\n" -" println!(\"Is {:?}\\nequal to {:?}?\\nThe answer is {}!\", &p1, &p2,\n" -" if p1 == p2 { \"yes\" } else { \"no\" });\n" -"}\n" -"```" +"Operator overloading is implemented via traits in [`std::ops`](https://doc." +"rust-lang.org/std/ops/index.html):" msgstr "" +"运算符重载是通过 [`std::ops`](https://doc.rust-lang.org/std/ops/index.html) " +"中的特征实现的:" -#: src/traits/default-methods.md:3 -msgid "Traits can implement behavior in terms of other trait methods:" -msgstr "特征可以依照其他特征方法来实现行为:" - -#: src/traits/default-methods.md:5 +#: src/traits/operators.md:5 msgid "" "```rust,editable\n" -"trait Equals {\n" -" fn equal(&self, other: &Self) -> bool;\n" -" fn not_equal(&self, other: &Self) -> bool {\n" -" !self.equal(other)\n" -" }\n" -"}\n" +"#[derive(Debug, Copy, Clone)]\n" +"struct Point { x: i32, y: i32 }\n" "\n" -"#[derive(Debug)]\n" -"struct Centimeter(i16);\n" +"impl std::ops::Add for Point {\n" +" type Output = Self;\n" "\n" -"impl Equals for Centimeter {\n" -" fn equal(&self, other: &Centimeter) -> bool {\n" -" self.0 == other.0\n" +" fn add(self, other: Self) -> Self {\n" +" Self {x: self.x + other.x, y: self.y + other.y}\n" " }\n" "}\n" "\n" "fn main() {\n" -" let a = Centimeter(10);\n" -" let b = Centimeter(20);\n" -" println!(\"{a:?} equals {b:?}: {}\", a.equal(&b));\n" -" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equal(&b));\n" +" let p1 = Point { x: 10, y: 20 };\n" +" let p2 = Point { x: 100, y: 200 };\n" +" println!(\"{:?} + {:?} = {:?}\", p1, p2, p1 + p2);\n" "}\n" "```" msgstr "" -#: src/traits/default-methods.md:32 +#: src/traits/operators.md:28 msgid "" -"Traits may specify pre-implemented (default) methods and methods that users " -"are required to implement themselves. Methods with default implementations " -"can rely on required methods." -msgstr "" +"You could implement `Add` for `&Point`. In which situations is that useful? " +msgstr "您可以针对 `&Point` 实现 `Add`。此做法在哪些情况下可派上用场? " -#: src/traits/default-methods.md:35 -msgid "Move method `not_equal` to a new trait `NotEqual`." +#: src/traits/operators.md:29 +msgid "" +"Answer: `Add:add` consumes `self`. If type `T` for which you are overloading " +"the operator is not `Copy`, you should consider overloading the operator for " +"`&T` as well. This avoids unnecessary cloning on the call site." msgstr "" +"回答:`Add:add` 会耗用 `self`。如果您的运算符重载对象 (即类型 `T`)不是 " +"`Copy`,建议您也为 `&T` 重载运算符。这可避免调用点上存在不必要的 克隆任务。" -#: src/traits/default-methods.md:37 -msgid "Make `NotEqual` a super trait for `Equal`." +#: src/traits/operators.md:33 +msgid "" +"Why is `Output` an associated type? Could it be made a type parameter of the " +"method?" +msgstr "为什么 `Output` 是关联类型?可将它用作该方法的类型形参吗?" + +#: src/traits/operators.md:34 +msgid "" +"Short answer: Function type parameters are controlled by the caller, but " +"associated types (like `Output`) are controlled by the implementor of a " +"trait." msgstr "" +"简答:函数类型形参是由调用方控管,但 `Output` 这类关联类型则由特征实现人员 控" +"管。" -#: src/traits/default-methods.md:38 +#: src/traits/operators.md:37 msgid "" -"```rust,editable,compile_fail\n" -"trait NotEqual: Equals {\n" -" fn not_equal(&self, other: &Self) -> bool {\n" -" !self.equal(other)\n" -" }\n" -"}\n" -"```" +"You could implement `Add` for two different types, e.g. `impl Add<(i32, " +"i32)> for Point` would add a tuple to a `Point`." msgstr "" +"您可以针对两种不同类型实现 `Add`,例如, `impl Add<(i32, i32)> for Point` 会" +"向 `Point` 中添加元组。" -#: src/traits/default-methods.md:46 -msgid "Provide a blanket implementation of `NotEqual` for `Equal`." +#: src/traits/closures.md:1 +msgid "Closures" +msgstr "闭包" + +#: src/traits/closures.md:3 +msgid "" +"Closures or lambda expressions have types which cannot be named. However, " +"they implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn." +"html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), and " +"[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:" msgstr "" +"闭包或 lambda 表达式具有无法命名的类型。不过,它们会 实现特殊的 [`Fn`]" +"(https://doc.rust-lang.org/std/ops/trait.Fn.html), [`FnMut`](https://doc." +"rust-lang.org/std/ops/trait.FnMut.html) 和 [`FnOnce`](https://doc.rust-lang." +"org/std/ops/trait.FnOnce.html) 特征:" -#: src/traits/default-methods.md:47 +#: src/traits/closures.md:8 msgid "" -"```rust,editable,compile_fail\n" -"trait NotEqual {\n" -" fn not_equal(&self, other: &Self) -> bool;\n" +"```rust,editable\n" +"fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {\n" +" println!(\"Calling function on {input}\");\n" +" func(input)\n" "}\n" "\n" -"impl NotEqual for T where T: Equals {\n" -" fn not_equal(&self, other: &Self) -> bool {\n" -" !self.equal(other)\n" -" }\n" +"fn main() {\n" +" let add_3 = |x| x + 3;\n" +" println!(\"add_3: {}\", apply_with_log(add_3, 10));\n" +" println!(\"add_3: {}\", apply_with_log(add_3, 20));\n" +"\n" +" let mut v = Vec::new();\n" +" let mut accumulate = |x: i32| {\n" +" v.push(x);\n" +" v.iter().sum::()\n" +" };\n" +" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 4));\n" +" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 5));\n" +"\n" +" let multiply_sum = |x| x * v.into_iter().sum::();\n" +" println!(\"multiply_sum: {}\", apply_with_log(multiply_sum, 3));\n" "}\n" "```" msgstr "" -#: src/traits/default-methods.md:58 +#: src/traits/closures.md:34 msgid "" -"With the blanket implementation, you no longer need `NotEqual` as a super " -"trait for `Equal`." +"An `Fn` (e.g. `add_3`) neither consumes nor mutates captured values, or " +"perhaps captures nothing at all. It can be called multiple times " +"concurrently." msgstr "" +"`Fn`(例如 `add_3`)既不会耗用也不会修改捕获的值,或许 也不会捕获任何值。它可" +"被并发调用多次。" -#: src/traits/trait-bounds.md:3 +#: src/traits/closures.md:37 msgid "" -"When working with generics, you often want to require the types to implement " -"some trait, so that you can call this trait's methods." +"An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it " +"multiple times, but not concurrently." msgstr "" -"使用泛型时,您通常会想要利用类型来实现某些特性, 这样才能调用此特征的方法。" - -#: src/traits/trait-bounds.md:6 -msgid "You can do this with `T: Trait` or `impl Trait`:" -msgstr "您可以使用 `T: Trait` 或 `impl Trait` 执行此操作:" +"`FnMut`(例如 `accumulate`)可能会改变捕获的值。您可以多次调用它, 但不能并发" +"调用它。" -#: src/traits/trait-bounds.md:8 +#: src/traits/closures.md:40 msgid "" -"```rust,editable\n" -"fn duplicate(a: T) -> (T, T) {\n" -" (a.clone(), a.clone())\n" -"}\n" -"\n" -"// Syntactic sugar for:\n" -"// fn add_42_millions>(x: T) -> i32 {\n" -"fn add_42_millions(x: impl Into) -> i32 {\n" -" x.into() + 42_000_000\n" -"}\n" -"\n" -"// struct NotClonable;\n" -"\n" -"fn main() {\n" -" let foo = String::from(\"foo\");\n" -" let pair = duplicate(foo);\n" -" println!(\"{pair:?}\");\n" -"\n" -" let many = add_42_millions(42_i8);\n" -" println!(\"{many}\");\n" -" let many_more = add_42_millions(10_000_000);\n" -" println!(\"{many_more}\");\n" -"}\n" -"```" +"If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It " +"might consume captured values." msgstr "" +"如果您使用 `FnOnce`(例如 `multiply_sum`),或许只能调用它一次。它可能会耗用 " +"所捕获的值。" -#: src/traits/trait-bounds.md:35 -msgid "Show a `where` clause, students will encounter it when reading code." -msgstr "显示 `where` 子句,学员在阅读代码时会看到它。" - -#: src/traits/trait-bounds.md:37 +#: src/traits/closures.md:43 msgid "" -"```rust,ignore\n" -"fn duplicate(a: T) -> (T, T)\n" -"where\n" -" T: Clone,\n" -"{\n" -" (a.clone(), a.clone())\n" -"}\n" -"```" +"`FnMut` is a subtype of `FnOnce`. `Fn` is a subtype of `FnMut` and `FnOnce`. " +"I.e. you can use an `FnMut` wherever an `FnOnce` is called for, and you can " +"use an `Fn` wherever an `FnMut` or `FnOnce` is called for." msgstr "" -"```rust,ignore\n" -"fn duplicate(a: T) -> (T, T)\n" -"where\n" -" T: Clone,\n" -"{\n" -" (a.clone(), a.clone())\n" -"}\n" -"```" - -#: src/traits/trait-bounds.md:46 -msgid "It declutters the function signature if you have many parameters." -msgstr "它会在您有多个形参的情况下整理函数签名。" - -#: src/traits/trait-bounds.md:47 -msgid "It has additional features making it more powerful." -msgstr "它具有额外功能,因此也更强大。" +"`FnMut` 是 `FnOnce` 的子类型。`Fn` 是 `FnMut` 和 `FnOnce` 的子类型。也就是" +"说,您可以在任何 需要调用 `FnOnce` 的地方使用 `FnMut`,还可在任何需要调用 " +"`FnMut` 或 `FnOnce` 的地方 使用 `Fn`。" -#: src/traits/trait-bounds.md:48 +#: src/traits/closures.md:47 msgid "" -"If someone asks, the extra feature is that the type on the left of \":\" can " -"be arbitrary, like `Option`." +"The compiler also infers `Copy` (e.g. for `add_3`) and `Clone` (e.g. " +"`multiply_sum`), depending on what the closure captures." msgstr "" -"如果有人提问,便阐明额外功能是指“:”左侧的类别可为任意值,例如 `Option`。" - -#: src/traits/impl-trait.md:1 -msgid "`impl Trait`" -msgstr "`impl Trait`" +"编译器也会推断 `Copy`(例如针对 `add_3`)和 `Clone`(例如 `multiply_sum`), " +"具体取决于闭包捕获的数据。" -#: src/traits/impl-trait.md:3 +#: src/traits/closures.md:50 msgid "" -"Similar to trait bounds, an `impl Trait` syntax can be used in function " -"arguments and return values:" -msgstr "与特征边界类似,`impl Trait` 语法可以在函数实参 和返回值中使用:" +"By default, closures will capture by reference if they can. The `move` " +"keyword makes them capture by value." +msgstr "" +"默认情况下,闭包会依据引用来捕获数据(如果可以的话)。`move` 关键字则可让闭包" +"依据值 来捕获数据。" -#: src/traits/impl-trait.md:6 +#: src/traits/closures.md:52 msgid "" "```rust,editable\n" -"use std::fmt::Display;\n" -"\n" -"fn get_x(name: impl Display) -> impl Display {\n" -" format!(\"Hello {name}\")\n" +"fn make_greeter(prefix: String) -> impl Fn(&str) {\n" +" return move |name| println!(\"{} {}\", prefix, name)\n" "}\n" "\n" "fn main() {\n" -" let x = get_x(\"foo\");\n" -" println!(\"{x}\");\n" +" let hi = make_greeter(\"Hi\".to_string());\n" +" hi(\"there\");\n" "}\n" "```" msgstr "" -#: src/traits/impl-trait.md:19 -msgid "`impl Trait` allows you to work with types which you cannot name." -msgstr "`impl Trait` 让您可使用无法命名的类型。" - -#: src/traits/impl-trait.md:23 -msgid "" -"The meaning of `impl Trait` is a bit different in the different positions." -msgstr "`impl Trait` 的意义因使用位置而略有不同。" - -#: src/traits/impl-trait.md:25 -msgid "" -"For a parameter, `impl Trait` is like an anonymous generic parameter with a " -"trait bound." -msgstr "对形参来说,`impl Trait` 就像是具有特征边界的匿名泛型形参。" - -#: src/traits/impl-trait.md:27 -msgid "" -"For a return type, it means that the return type is some concrete type that " -"implements the trait, without naming the type. This can be useful when you " -"don't want to expose the concrete type in a public API." +#: src/exercises/day-3/morning.md:1 +msgid "Day 3: Morning Exercises" msgstr "" -"对返回值类型来说,它则意味着返回值类型就是实现该特征的某具体类型, 无需为该类" -"型命名。如果您不想在公共 API 中公开该具体类型,便可 使用此方法。" -#: src/traits/impl-trait.md:31 -msgid "" -"Inference is hard in return position. A function returning `impl Foo` picks " -"the concrete type it returns, without writing it out in the source. A " -"function returning a generic type like `collect() -> B` can return any " -"type satisfying `B`, and the caller may need to choose one, such as with " -"`let x: Vec<_> = foo.collect()` or with the turbofish, `foo.collect::" -">()`." +#: src/exercises/day-3/morning.md:3 +msgid "We will design a classical GUI library using traits and trait objects." msgstr "" -"在返回位置处进行推断有一定难度。会返回 `impl Foo` 的函数会挑选 自身返回的具体" -"类型,而不必在来源中写出此信息。会返回 泛型类型(例如 `collect() -> B`)的" -"函数则可返回符合 `B` 的任何类型,而调用方可能需要选择一个类型,例如使用 `let " -"x: Vec<_> = foo.collect()` 或使用以下 Turbofish:`foo.collect::>()`。" -#: src/traits/impl-trait.md:37 +#: src/exercises/day-3/morning.md:5 msgid "" -"This example is great, because it uses `impl Display` twice. It helps to " -"explain that nothing here enforces that it is _the same_ `impl Display` " -"type. If we used a single `T: Display`, it would enforce the constraint " -"that input `T` and return `T` type are the same type. It would not work for " -"this particular function, as the type we expect as input is likely not what " -"`format!` returns. If we wanted to do the same via `: Display` syntax, we'd " -"need two independent generic parameters." +"We will also look at enum dispatch with an exercise involving points and " +"polygons." msgstr "" -"这是一个非常棒的示例,因为它使用了两次 `impl Display`。这有助于说明 此处没有" -"任何项目会强制使用相同的 `impl Display` 类型。如果我们使用单个 `T: Display`," -"它会强制限制输入 `T` 和返回 `T` 均为同一类型。 这并不适用于这个特定函数,因为" -"我们预期作为输入的类型可能 不会是 `format!` 返回的值。如果我们希望通过 `: " -"Display` 语法执行相同的操作,则需要两个 独立的泛型形参。" - -#: src/traits/important-traits.md:3 -msgid "" -"We will now look at some of the most common traits of the Rust standard " -"library:" -msgstr "现在,我们来看看 Rust 标准库的一些最常见的特征:" -#: src/traits/important-traits.md:5 +#: src/exercises/day-3/simple-gui.md:3 msgid "" -"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " -"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " -"used in `for` loops," +"Let us design a classical GUI library using our new knowledge of traits and " +"trait objects." msgstr "" -"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) 和 " -"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " -"用于 `for` 循环中," -#: src/traits/important-traits.md:6 -msgid "" -"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) and [`Into`]" -"(https://doc.rust-lang.org/std/convert/trait.Into.html) used to convert " -"values," +#: src/exercises/day-3/simple-gui.md:6 +msgid "We will have a number of widgets in our library:" msgstr "" -"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) 和 [`Into`]" -"(https://doc.rust-lang.org/std/convert/trait.Into.html) 用于转换值," -#: src/traits/important-traits.md:7 -msgid "" -"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and [`Write`]" -"(https://doc.rust-lang.org/std/io/trait.Write.html) used for IO," +#: src/exercises/day-3/simple-gui.md:8 +msgid "`Window`: has a `title` and contains other widgets." msgstr "" -"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) 和 [`Write`]" -"(https://doc.rust-lang.org/std/io/trait.Write.html) 用于实现 IO。" -#: src/traits/important-traits.md:8 +#: src/exercises/day-3/simple-gui.md:9 msgid "" -"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html), [`Mul`](https://" -"doc.rust-lang.org/std/ops/trait.Mul.html), ... used for operator " -"overloading, and" +"`Button`: has a `label` and a callback function which is invoked when the " +"button is pressed." msgstr "" -"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html)、[`Mul`](https://" -"doc.rust-lang.org/std/ops/trait.Mul.html) 等用于实现运算符重载," -#: src/traits/important-traits.md:9 -msgid "" -"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) used for " -"defining destructors." +#: src/exercises/day-3/simple-gui.md:11 +msgid "`Label`: has a `label`." msgstr "" -"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) 用于定义析构函" -"数。" -#: src/traits/important-traits.md:10 -msgid "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) used " -"to construct a default instance of a type." +#: src/exercises/day-3/simple-gui.md:13 +msgid "The widgets will implement a `Widget` trait, see below." msgstr "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) 用于构" -"建相应类型的默认实例。" -#: src/traits/iterator.md:1 -msgid "Iterators" -msgstr "迭代器" - -#: src/traits/iterator.md:3 +#: src/exercises/day-3/simple-gui.md:15 msgid "" -"You can implement the [`Iterator`](https://doc.rust-lang.org/std/iter/trait." -"Iterator.html) trait on your own types:" +"Copy the code below to , fill in the missing " +"`draw_into` methods so that you implement the `Widget` trait:" msgstr "" -"您可以自行实现 [`Iterator`](https://doc.rust-lang.org/std/iter/trait." -"Iterator.html) 特征:" -#: src/traits/iterator.md:5 +#: src/exercises/day-3/simple-gui.md:18 msgid "" -"```rust,editable\n" -"struct Fibonacci {\n" -" curr: u32,\n" -" next: u32,\n" -"}\n" +"```rust,should_panic\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_imports, unused_variables, dead_code)]\n" "\n" -"impl Iterator for Fibonacci {\n" -" type Item = u32;\n" +"pub trait Widget {\n" +" /// Natural width of `self`.\n" +" fn width(&self) -> usize;\n" "\n" -" fn next(&mut self) -> Option {\n" -" let new_next = self.curr + self.next;\n" -" self.curr = self.next;\n" -" self.next = new_next;\n" -" Some(self.curr)\n" +" /// Draw the widget into a buffer.\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write);\n" +"\n" +" /// Draw the widget on standard output.\n" +" fn draw(&self) {\n" +" let mut buffer = String::new();\n" +" self.draw_into(&mut buffer);\n" +" println!(\"{buffer}\");\n" " }\n" "}\n" "\n" -"fn main() {\n" -" let fib = Fibonacci { curr: 0, next: 1 };\n" -" for (i, n) in fib.enumerate().take(5) {\n" -" println!(\"fib({i}): {n}\");\n" +"pub struct Label {\n" +" label: String,\n" +"}\n" +"\n" +"impl Label {\n" +" fn new(label: &str) -> Label {\n" +" Label {\n" +" label: label.to_owned(),\n" +" }\n" " }\n" "}\n" -"```" -msgstr "" - -#: src/traits/iterator.md:32 -msgid "" -"The `Iterator` trait implements many common functional programming " -"operations over collections (e.g. `map`, `filter`, `reduce`, etc). This is " -"the trait where you can find all the documentation about them. In Rust these " -"functions should produce the code as efficient as equivalent imperative " -"implementations." -msgstr "" -"`Iterator` 特征会对集合实现许多常见的函数程序操作, 例如 `` map``filter ``和" -"`reduce` 等。您可以通过此特征找到有关它们的所有 文档。在 Rust 中,这些函数应" -"生成代码,且生成的代码应与等效命令式实现一样 高效。" - -#: src/traits/iterator.md:37 -msgid "" -"`IntoIterator` is the trait that makes for loops work. It is implemented by " -"collection types such as `Vec` and references to them such as `&Vec` " -"and `&[T]`. Ranges also implement it. This is why you can iterate over a " -"vector with `for i in some_vec { .. }` but `some_vec.next()` doesn't exist." -msgstr "" -"`IntoIterator` 是迫使 for 循环运作的特征。此特征由集合类型 (例如 `Vec`)" -"和相关引用(例如 `&Vec` 和 `&[T]`)而实现。此外,范围也会实现这项特征。因" -"此, 您可以使用 `for i in some_vec { .. }` 来遍历某矢量,但 `some_vec." -"next()` 不存在。" - -#: src/traits/from-iterator.md:3 -msgid "" -"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " -"lets you build a collection from an [`Iterator`](https://doc.rust-lang.org/" -"std/iter/trait.Iterator.html)." -msgstr "" -"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " -"让您可通过 [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator." -"html) 构建一个集合。" - -#: src/traits/from-iterator.md:5 -msgid "" -"```rust,editable\n" +"\n" +"pub struct Button {\n" +" label: Label,\n" +" callback: Box,\n" +"}\n" +"\n" +"impl Button {\n" +" fn new(label: &str, callback: Box) -> Button {\n" +" Button {\n" +" label: Label::new(label),\n" +" callback,\n" +" }\n" +" }\n" +"}\n" +"\n" +"pub struct Window {\n" +" title: String,\n" +" widgets: Vec>,\n" +"}\n" +"\n" +"impl Window {\n" +" fn new(title: &str) -> Window {\n" +" Window {\n" +" title: title.to_owned(),\n" +" widgets: Vec::new(),\n" +" }\n" +" }\n" +"\n" +" fn add_widget(&mut self, widget: Box) {\n" +" self.widgets.push(widget);\n" +" }\n" +"\n" +" fn inner_width(&self) -> usize {\n" +" std::cmp::max(\n" +" self.title.chars().count(),\n" +" self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),\n" +" )\n" +" }\n" +"}\n" +"\n" +"\n" +"impl Widget for Label {\n" +" fn width(&self) -> usize {\n" +" unimplemented!()\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" +" unimplemented!()\n" +" }\n" +"}\n" +"\n" +"impl Widget for Button {\n" +" fn width(&self) -> usize {\n" +" unimplemented!()\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" +" unimplemented!()\n" +" }\n" +"}\n" +"\n" +"impl Widget for Window {\n" +" fn width(&self) -> usize {\n" +" unimplemented!()\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" +" unimplemented!()\n" +" }\n" +"}\n" +"\n" "fn main() {\n" -" let primes = vec![2, 3, 5, 7];\n" -" let prime_squares = primes\n" -" .into_iter()\n" -" .map(|prime| prime * prime)\n" -" .collect::>();\n" +" let mut window = Window::new(\"Rust GUI Demo 1.23\");\n" +" window.add_widget(Box::new(Label::new(\"This is a small text GUI demo." +"\")));\n" +" window.add_widget(Box::new(Button::new(\n" +" \"Click me!\",\n" +" Box::new(|| println!(\"You clicked the button!\")),\n" +" )));\n" +" window.draw();\n" "}\n" "```" msgstr "" -#: src/traits/from-iterator.md:17 -msgid "" -"`Iterator` implements `fn collect(self) -> B where B: FromIterator, Self: Sized`" -msgstr "" -"`Iterator` 会实现 `fn collect(self) -> B where B: FromIterator, Self: Sized`" - -#: src/traits/from-iterator.md:23 -msgid "" -"There are also implementations which let you do cool things like convert an " -"`Iterator>` into a `Result, E>`." +#: src/exercises/day-3/simple-gui.md:130 +msgid "The output of the above program can be something simple like this:" msgstr "" -"还有一些实现,让您可执行一些很酷的操作,比如 将 `Iterator>` 转换成 `Result, E>`。" - -#: src/traits/from-into.md:1 -msgid "`From` and `Into`" -msgstr "`From` 和 `Into`" -#: src/traits/from-into.md:3 +#: src/exercises/day-3/simple-gui.md:142 msgid "" -"Types implement [`From`](https://doc.rust-lang.org/std/convert/trait.From." -"html) and [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) to " -"facilitate type conversions:" +"If you want to draw aligned text, you can use the [fill/alignment](https://" +"doc.rust-lang.org/std/fmt/index.html#fillalignment) formatting operators. In " +"particular, notice how you can pad with different characters (here a `'/'`) " +"and how you can control alignment:" msgstr "" -"类型会实现 [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) " -"和 [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) 以加快类型" -"转换:" -#: src/traits/from-into.md:5 +#: src/exercises/day-3/simple-gui.md:147 msgid "" "```rust,editable\n" "fn main() {\n" -" let s = String::from(\"hello\");\n" -" let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);\n" -" let one = i16::from(true);\n" -" let bigger = i32::from(123i16);\n" -" println!(\"{s}, {addr}, {one}, {bigger}\");\n" +" let width = 10;\n" +" println!(\"left aligned: |{:/width$}|\", \"foo\");\n" "}\n" "```" msgstr "" -#: src/traits/from-into.md:15 -msgid "" -"[`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) is " -"automatically implemented when [`From`](https://doc.rust-lang.org/std/" -"convert/trait.From.html) is implemented:" -msgstr "" -"实现 [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) 后,系统" -"会自动实现 [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html):" - -#: src/traits/from-into.md:17 +#: src/exercises/day-3/simple-gui.md:156 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let s: String = \"hello\".into();\n" -" let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();\n" -" let one: i16 = true.into();\n" -" let bigger: i32 = 123i16.into();\n" -" println!(\"{s}, {addr}, {one}, {bigger}\");\n" -"}\n" -"```" +"Using such alignment tricks, you can for example produce output like this:" msgstr "" -#: src/traits/from-into.md:29 -msgid "" -"That's why it is common to only implement `From`, as your type will get " -"`Into` implementation too." -msgstr "这就是为什么通常只需实现 `From`,因为您的类型也会实现 `Into`。" - -#: src/traits/from-into.md:30 -msgid "" -"When declaring a function argument input type like \"anything that can be " -"converted into a `String`\", the rule is opposite, you should use `Into`. " -"Your function will accept types that implement `From` and those that _only_ " -"implement `Into`." +#: src/exercises/day-3/points-polygons.md:1 +msgid "Polygon Struct" msgstr "" -"若要声明某个函数实参输入类型(例如“任何可转换成 `String` 的类型”),规则便会" -"相反,此时应使用 `Into`。 您的函数会接受可实现 `From` 的类型,以及那些仅实现 " -"`Into` 的类型。" - -#: src/traits/read-write.md:1 -msgid "`Read` and `Write`" -msgstr "`Read` 和 `Write`" -#: src/traits/read-write.md:3 +#: src/exercises/day-3/points-polygons.md:3 msgid "" -"Using [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and " -"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html), you can " -"abstract over `u8` sources:" +"We will create a `Polygon` struct which contain some points. Copy the code " +"below to and fill in the missing methods to " +"make the tests pass:" msgstr "" -"您可以使用 [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) 和 " -"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) 对 `u8` 来源" -"进行抽象化处理:" -#: src/traits/read-write.md:5 +#: src/exercises/day-3/points-polygons.md:7 msgid "" -"```rust,editable\n" -"use std::io::{BufRead, BufReader, Read, Result};\n" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" "\n" -"fn count_lines(reader: R) -> usize {\n" -" let buf_reader = BufReader::new(reader);\n" -" buf_reader.lines().count()\n" +"pub struct Point {\n" +" // add fields\n" "}\n" "\n" -"fn main() -> Result<()> {\n" -" let slice: &[u8] = b\"foo\\nbar\\nbaz\\n\";\n" -" println!(\"lines in slice: {}\", count_lines(slice));\n" +"impl Point {\n" +" // add methods\n" +"}\n" "\n" -" let file = std::fs::File::open(std::env::current_exe()?)?;\n" -" println!(\"lines in file: {}\", count_lines(file));\n" -" Ok(())\n" +"pub struct Polygon {\n" +" // add fields\n" "}\n" -"```" -msgstr "" - -#: src/traits/read-write.md:23 -msgid "" -"Similarly, [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) lets " -"you abstract over `u8` sinks:" -msgstr "" -"您同样可使用 [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) 对 " -"`u8` 接收器进行抽象化处理:" - -#: src/traits/read-write.md:25 -msgid "" -"```rust,editable\n" -"use std::io::{Result, Write};\n" "\n" -"fn log(writer: &mut W, msg: &str) -> Result<()> {\n" -" writer.write_all(msg.as_bytes())?;\n" -" writer.write_all(\"\\n\".as_bytes())\n" +"impl Polygon {\n" +" // add methods\n" "}\n" "\n" -"fn main() -> Result<()> {\n" -" let mut buffer = Vec::new();\n" -" log(&mut buffer, \"Hello\")?;\n" -" log(&mut buffer, \"World\")?;\n" -" println!(\"Logged: {:?}\", buffer);\n" -" Ok(())\n" +"pub struct Circle {\n" +" // add fields\n" "}\n" -"```" -msgstr "" - -#: src/traits/drop.md:1 -msgid "The `Drop` Trait" -msgstr "`Drop` 特征" - -#: src/traits/drop.md:3 -msgid "" -"Values which implement [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop." -"html) can specify code to run when they go out of scope:" -msgstr "" -"用于实现 [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) 的值可以" -"指定在超出范围时运行的代码:" - -#: src/traits/drop.md:5 -msgid "" -"```rust,editable\n" -"struct Droppable {\n" -" name: &'static str,\n" +"\n" +"impl Circle {\n" +" // add methods\n" "}\n" "\n" -"impl Drop for Droppable {\n" -" fn drop(&mut self) {\n" -" println!(\"Dropping {}\", self.name);\n" -" }\n" +"pub enum Shape {\n" +" Polygon(Polygon),\n" +" Circle(Circle),\n" "}\n" "\n" -"fn main() {\n" -" let a = Droppable { name: \"a\" };\n" -" {\n" -" let b = Droppable { name: \"b\" };\n" -" {\n" -" let c = Droppable { name: \"c\" };\n" -" let d = Droppable { name: \"d\" };\n" -" println!(\"Exiting block B\");\n" -" }\n" -" println!(\"Exiting block A\");\n" +"#[cfg(test)]\n" +"mod tests {\n" +" use super::*;\n" +"\n" +" fn round_two_digits(x: f64) -> f64 {\n" +" (x * 100.0).round() / 100.0\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_magnitude() {\n" +" let p1 = Point::new(12, 13);\n" +" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_dist() {\n" +" let p1 = Point::new(10, 10);\n" +" let p2 = Point::new(14, 13);\n" +" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_add() {\n" +" let p1 = Point::new(16, 16);\n" +" let p2 = p1 + Point::new(-4, 3);\n" +" assert_eq!(p2, Point::new(12, 19));\n" +" }\n" +"\n" +" #[test]\n" +" fn test_polygon_left_most_point() {\n" +" let p1 = Point::new(12, 13);\n" +" let p2 = Point::new(16, 16);\n" +"\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(p1);\n" +" poly.add_point(p2);\n" +" assert_eq!(poly.left_most_point(), Some(p1));\n" +" }\n" +"\n" +" #[test]\n" +" fn test_polygon_iter() {\n" +" let p1 = Point::new(12, 13);\n" +" let p2 = Point::new(16, 16);\n" +"\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(p1);\n" +" poly.add_point(p2);\n" +"\n" +" let points = poly.iter().cloned().collect::>();\n" +" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_shape_perimeters() {\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(Point::new(12, 13));\n" +" poly.add_point(Point::new(17, 11));\n" +" poly.add_point(Point::new(16, 16));\n" +" let shapes = vec![\n" +" Shape::from(poly),\n" +" Shape::from(Circle::new(Point::new(10, 20), 5)),\n" +" ];\n" +" let perimeters = shapes\n" +" .iter()\n" +" .map(Shape::perimeter)\n" +" .map(round_two_digits)\n" +" .collect::>();\n" +" assert_eq!(perimeters, vec![15.48, 31.42]);\n" " }\n" -" drop(a);\n" -" println!(\"Exiting main\");\n" "}\n" +"\n" +"#[allow(dead_code)]\n" +"fn main() {}\n" "```" msgstr "" -#: src/traits/drop.md:34 src/traits/operators.md:26 -msgid "Discussion points:" -msgstr "讨论点:" - -#: src/traits/drop.md:36 -msgid "Why doesn't `Drop::drop` take `self`?" -msgstr "为什么 `Drop::drop` 不使用 `self`?" - -#: src/traits/drop.md:37 +#: src/exercises/day-3/points-polygons.md:117 msgid "" -"Short-answer: If it did, `std::mem::drop` would be called at the end of the " -"block, resulting in another call to `Drop::drop`, and a stack overflow!" +"Since the method signatures are missing from the problem statements, the key " +"part of the exercise is to specify those correctly. You don't have to modify " +"the tests." msgstr "" -"简答:如果这样的话,系统会在代码块结尾 调用 `std::mem::drop`,进而引发再一次" -"调用 `Drop::drop`,并引发堆栈 溢出!" - -#: src/traits/drop.md:40 -msgid "Try replacing `drop(a)` with `a.drop()`." -msgstr "尝试用 `a.drop()` 替换 `drop(a)`。" -#: src/traits/default.md:1 -msgid "The `Default` Trait" -msgstr "`Default` 特征" +#: src/exercises/day-3/points-polygons.md:120 +msgid "Other interesting parts of the exercise:" +msgstr "" -#: src/traits/default.md:3 +#: src/exercises/day-3/points-polygons.md:122 msgid "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " -"provides a default implementation of a trait." +"Derive a `Copy` trait for some structs, as in tests the methods sometimes " +"don't borrow their arguments." msgstr "" -#: src/traits/default.md:5 +#: src/exercises/day-3/points-polygons.md:123 msgid "" -"```rust,editable\n" -"#[derive(Debug, Default)]\n" -"struct Derived {\n" -" x: u32,\n" -" y: String,\n" -" z: Implemented,\n" -"}\n" -"\n" -"#[derive(Debug)]\n" -"struct Implemented(String);\n" -"\n" -"impl Default for Implemented {\n" -" fn default() -> Self {\n" -" Self(\"John Smith\".into())\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let default_struct: Derived = Default::default();\n" -" println!(\"{default_struct:#?}\");\n" -"\n" -" let almost_default_struct = Derived {\n" -" y: \"Y is set!\".into(),\n" -" ..Default::default()\n" -" };\n" -" println!(\"{almost_default_struct:#?}\");\n" -"\n" -" let nothing: Option = None;\n" -" println!(\"{:#?}\", nothing.unwrap_or_default());\n" -"}\n" -"\n" -"```" +"Discover that `Add` trait must be implemented for two objects to be addable " +"via \"+\". Note that we do not discuss generics until Day 3." msgstr "" -#: src/traits/default.md:40 -msgid "" -"It can be implemented directly or it can be derived via `#[derive(Default)]`." -msgstr "系统可以直接实现它,也可以通过 `#[derive(Default)]` 派生出它。" +#: src/error-handling.md:3 +msgid "Error handling in Rust is done using explicit control flow:" +msgstr "Rust 中的错误处理是使用显式控制流来进行的:" -#: src/traits/default.md:41 -msgid "" -"Derived implementation will produce an instance where all fields are set to " -"their default values." -msgstr "派生的实现会生成一个实例,其中字段全都设为其默认值。" +#: src/error-handling.md:5 +msgid "Functions that can have errors list this in their return type," +msgstr "包含错误的函数会在返回类型中列出相关信息," -#: src/traits/default.md:42 -msgid "This means all types in the struct must implement `Default` too." -msgstr "这意味着,该结构体中的所有类型也都必须实现 `Default`。" +#: src/error-handling.md:6 +msgid "There are no exceptions." +msgstr "此规则没有例外。" -#: src/traits/default.md:43 +#: src/error-handling/panics.md:3 +msgid "Rust will trigger a panic if a fatal error happens at runtime:" +msgstr "如果运行时发生严重错误,Rust 会触发 panic:" + +#: src/error-handling/panics.md:5 msgid "" -"Standard Rust types often implement `Default` with reasonable values (e.g. " -"`0`, `\"\"`, etc)." +"```rust,editable,should_panic\n" +"fn main() {\n" +" let v = vec![10, 20, 30];\n" +" println!(\"v[100]: {}\", v[100]);\n" +"}\n" +"```" msgstr "" -"标准的 Rust 类型通常会以合理的值(例如 `` 0``\"\" `` 等)实现 `Default`。" -#: src/traits/default.md:44 -msgid "The partial struct copy works nicely with default." -msgstr "部分结构体副本可与默认值完美搭配运作。" +#: src/error-handling/panics.md:12 +msgid "Panics are for unrecoverable and unexpected errors." +msgstr "Panic 用于指示不可恢复的意外错误。" -#: src/traits/default.md:45 +#: src/error-handling/panics.md:13 +msgid "Panics are symptoms of bugs in the program." +msgstr "Panic反映了程序中的 bug 问题。" + +#: src/error-handling/panics.md:14 msgid "" -"Rust standard library is aware that types can implement `Default` and " -"provides convenience methods that use it." -msgstr "Rust 标准库了解类型可能会实现 `Default`,因此提供了便利的使用方式。" +"Use non-panicking APIs (such as `Vec::get`) if crashing is not acceptable." +msgstr "如果崩溃不可接受,请使用不会触发 panic 的 API(例如 `Vec::get`)。" -#: src/traits/operators.md:1 -msgid "`Add`, `Mul`, ..." -msgstr "`` Add``Mul ``…" +#: src/error-handling/panic-unwind.md:1 +msgid "Catching the Stack Unwinding" +msgstr "捕获堆栈展开" -#: src/traits/operators.md:3 +#: src/error-handling/panic-unwind.md:3 msgid "" -"Operator overloading is implemented via traits in [`std::ops`](https://doc." -"rust-lang.org/std/ops/index.html):" -msgstr "" -"运算符重载是通过 [`std::ops`](https://doc.rust-lang.org/std/ops/index.html) " -"中的特征实现的:" +"By default, a panic will cause the stack to unwind. The unwinding can be " +"caught:" +msgstr "默认情况下,panic 会导致堆栈展开。您可以捕获展开信息:" -#: src/traits/operators.md:5 +#: src/error-handling/panic-unwind.md:5 msgid "" "```rust,editable\n" -"#[derive(Debug, Copy, Clone)]\n" -"struct Point { x: i32, y: i32 }\n" -"\n" -"impl std::ops::Add for Point {\n" -" type Output = Self;\n" -"\n" -" fn add(self, other: Self) -> Self {\n" -" Self {x: self.x + other.x, y: self.y + other.y}\n" -" }\n" -"}\n" +"use std::panic;\n" "\n" "fn main() {\n" -" let p1 = Point { x: 10, y: 20 };\n" -" let p2 = Point { x: 100, y: 200 };\n" -" println!(\"{:?} + {:?} = {:?}\", p1, p2, p1 + p2);\n" +" let result = panic::catch_unwind(|| {\n" +" println!(\"hello!\");\n" +" });\n" +" assert!(result.is_ok());\n" +" \n" +" let result = panic::catch_unwind(|| {\n" +" panic!(\"oh no!\");\n" +" });\n" +" assert!(result.is_err());\n" "}\n" "```" msgstr "" -#: src/traits/operators.md:28 -msgid "" -"You could implement `Add` for `&Point`. In which situations is that useful? " -msgstr "您可以针对 `&Point` 实现 `Add`。此做法在哪些情况下可派上用场?" - -#: src/traits/operators.md:29 +#: src/error-handling/panic-unwind.md:21 msgid "" -"Answer: `Add:add` consumes `self`. If type `T` for which you are overloading " -"the operator is not `Copy`, you should consider overloading the operator for " -"`&T` as well. This avoids unnecessary cloning on the call site." -msgstr "" -"回答:`Add:add` 会耗用 `self`。如果您的运算符重载对象 (即类型 `T`)不是 " -"`Copy`,建议您也为 `&T` 重载运算符。这可避免调用点上存在不必要的 克隆任务。" - -#: src/traits/operators.md:33 -msgid "Why is `Output` an associated type? Could it be made a type parameter?" +"This can be useful in servers which should keep running even if a single " +"request crashes." msgstr "" +"如果服务器需要持续运行(即使是在请求发生崩溃的情况下), 此方法十分有用。" -#: src/traits/operators.md:34 -msgid "" -"Short answer: Type parameters are controlled by the caller, but associated " -"types (like `Output`) are controlled by the implementor of a trait." -msgstr "" +#: src/error-handling/panic-unwind.md:23 +msgid "This does not work if `panic = 'abort'` is set in your `Cargo.toml`." +msgstr "如果您在 `Cargo.toml` 中设置了 `panic = 'abort'`,此方法不会生效。" -#: src/traits/closures.md:1 -msgid "Closures" -msgstr "闭包" +#: src/error-handling/result.md:1 +msgid "Structured Error Handling with `Result`" +msgstr "使用 `Result` 进行结构化错误处理" -#: src/traits/closures.md:3 +#: src/error-handling/result.md:3 msgid "" -"Closures or lambda expressions have types which cannot be named. However, " -"they implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn." -"html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), and " -"[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:" +"We have already seen the `Result` enum. This is used pervasively when errors " +"are expected as part of normal operation:" msgstr "" -"闭包或 lambda 表达式具有无法命名的类型。不过,它们会 实现特殊的 [`Fn`]" -"(https://doc.rust-lang.org/std/ops/trait.Fn.html), [`FnMut`](https://doc." -"rust-lang.org/std/ops/trait.FnMut.html) 和 [`FnOnce`](https://doc.rust-lang." -"org/std/ops/trait.FnOnce.html) 特征:" +"在前面,我们看到了 `Result` 枚举。在遇到正常操作产生的预期错误时, 我们常会用" +"到此方法:" -#: src/traits/closures.md:8 +#: src/error-handling/result.md:6 msgid "" "```rust,editable\n" -"fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {\n" -" println!(\"Calling function on {input}\");\n" -" func(input)\n" -"}\n" +"use std::fs;\n" +"use std::io::Read;\n" "\n" "fn main() {\n" -" let add_3 = |x| x + 3;\n" -" let mul_5 = |x| x * 5;\n" -"\n" -" println!(\"add_3: {}\", apply_with_log(add_3, 10));\n" -" println!(\"mul_5: {}\", apply_with_log(mul_5, 20));\n" +" let file = fs::File::open(\"diary.txt\");\n" +" match file {\n" +" Ok(mut file) => {\n" +" let mut contents = String::new();\n" +" file.read_to_string(&mut contents);\n" +" println!(\"Dear diary: {contents}\");\n" +" },\n" +" Err(err) => {\n" +" println!(\"The diary could not be opened: {err}\");\n" +" }\n" +" }\n" "}\n" "```" msgstr "" -#: src/traits/closures.md:25 +#: src/error-handling/result.md:27 msgid "" -"If you have an `FnOnce`, you may only call it once. It might consume " -"captured values." +"As with `Option`, the successful value sits inside of `Result`, forcing the " +"developer to explicitly extract it. This encourages error checking. In the " +"case where an error should never happen, `unwrap()` or `expect()` can be " +"called, and this is a signal of the developer intent too." msgstr "" +"与 `Option` 方法相同,成功值位于 `Result` 方法内部, 开发者必须显示提取成功" +"值。因此,建议进行错误检查。在绝不应出现错误的情况下, 可以调用 `unwrap()` " +"或 `expect()` 方法,这也是一种开发者意向信号。" -#: src/traits/closures.md:27 +#: src/error-handling/result.md:30 msgid "" -"An `FnMut` might mutate captured values, so you can call it multiple times " -"but not concurrently." +"`Result` documentation is a recommended read. Not during the course, but it " +"is worth mentioning. It contains a lot of convenience methods and functions " +"that help functional-style programming. " msgstr "" +"我们建议阅读 `Result` 文档。虽然课程中不会涉及该文档,但是有必要提到它。 该文" +"档中包含许多便捷的方法和函数,对于函数式编程很有帮助。 " -#: src/traits/closures.md:29 -msgid "" -"An `Fn` neither consumes nor mutates captured values, or perhaps captures " -"nothing at all, so it can be called multiple times concurrently." -msgstr "" +#: src/error-handling/try-operator.md:1 +msgid "Propagating Errors with `?`" +msgstr "使用 `?` 传播错误" -#: src/traits/closures.md:43 +#: src/error-handling/try-operator.md:3 msgid "" -"`FnMut` is a subtype of `FnOnce`. `Fn` is a subtype of `FnMut` and `FnOnce`. " -"I.e. you can use an `FnMut` wherever an `FnOnce` is called for, and you can " -"use an `Fn` wherever an `FnMut` or `FnOnce` is called for." -msgstr "" -"`FnMut` 是 `FnOnce` 的子类型。`Fn` 是 `FnMut` 和 `FnOnce` 的子类型。也就是" -"说,您可以在任何 需要调用 `FnOnce` 的地方使用 `FnMut`,还可在任何需要调用 " -"`FnMut` 或 `FnOnce` 的地方 使用 `Fn`。" +"The try-operator `?` is used to return errors to the caller. It lets you " +"turn the common" +msgstr "将 try 操作符 `?` 用于将错误返回给调用方。它能把常用命令" -#: src/traits/closures.md:36 -msgid "`move` closures only implement `FnOnce`." -msgstr "" +#: src/error-handling/try-operator.md:13 +msgid "into the much simpler" +msgstr "转换成更简单的命令" -#: src/traits/default.md:3 +#: src/error-handling/try-operator.md:19 +#, fuzzy +msgid "We can use this to simplify our error handling code:" +msgstr "我们可以用它来简化错误处理代码:" + +#: src/error-handling/try-operator.md:21 msgid "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " -"produces a default value for a type." +"```rust,editable\n" +"use std::{fs, io};\n" +"use std::io::Read;\n" +"\n" +"fn read_username(path: &str) -> Result {\n" +" let username_file_result = fs::File::open(path);\n" +" let mut username_file = match username_file_result {\n" +" Ok(file) => file,\n" +" Err(err) => return Err(err),\n" +" };\n" +"\n" +" let mut username = String::new();\n" +" match username_file.read_to_string(&mut username) {\n" +" Ok(_) => Ok(username),\n" +" Err(err) => Err(err),\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" //fs::write(\"config.dat\", \"alice\").unwrap();\n" +" let username = read_username(\"config.dat\");\n" +" println!(\"username or error: {username:?}\");\n" +"}\n" +"```" msgstr "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) 特征会" -"为类型生成默认值。" -#: src/traits/operators.md:33 -msgid "" -"Why is `Output` an associated type? Could it be made a type parameter of the " -"method?" -msgstr "为什么 `Output` 是关联类型?可将它用作该方法的类型形参吗?" +#: src/error-handling/try-operator.md:50 +#: src/error-handling/converting-error-types-example.md:52 +msgid "The `username` variable can be either `Ok(string)` or `Err(error)`." +msgstr "`username` 变量可以是 `Ok(string)` 或 `Err(error)`。" -#: src/traits/operators.md:34 +#: src/error-handling/try-operator.md:51 +#: src/error-handling/converting-error-types-example.md:53 msgid "" -"Short answer: Function type parameters are controlled by the caller, but " -"associated types (like `Output`) are controlled by the implementor of a " -"trait." +"Use the `fs::write` call to test out the different scenarios: no file, empty " +"file, file with username." msgstr "" -"简答:函数类型形参是由调用方控管,但 `Output` 这类关联类型则由特征实现人员 控" -"管。" +"可以使用 `fs::write` 调用来测试不同的场景:没有文件、空文件、包含用户名的文" +"件。" -#: src/traits/operators.md:37 +#: src/error-handling/try-operator.md:52 msgid "" -"You could implement `Add` for two different types, e.g. `impl Add<(i32, " -"i32)> for Point` would add a tuple to a `Point`." +"The return type of the function has to be compatible with the nested " +"functions it calls. For instance, a function returning a `Result` " +"can only apply the `?` operator on a function returning a `Result`. It cannot apply the `?` operator on a function returning an " +"`Option` or `Result` unless `OtherErr` implements " +"`From`. Reciprocally, a function returning an `Option` can only " +"apply the `?` operator on a function returning an `Option`." msgstr "" -"您可以针对两种不同类型实现 `Add`,例如, `impl Add<(i32, i32)> for Point` 会" -"向 `Point` 中添加元组。" -#: src/traits/closures.md:34 +#: src/error-handling/try-operator.md:57 msgid "" -"An `Fn` (e.g. `add_3`) neither consumes nor mutates captured values, or " -"perhaps captures nothing at all. It can be called multiple times " -"concurrently." +"You can convert incompatible types into one another with the different " +"`Option` and `Result` methods such as `Option::ok_or`, `Result::ok`, " +"`Result::err`." msgstr "" -"`Fn`(例如 `add_3`)既不会耗用也不会修改捕获的值,或许 也不会捕获任何值。它可" -"被并发调用多次。" -#: src/traits/closures.md:37 +#: src/error-handling/converting-error-types.md:3 msgid "" -"An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it " -"multiple times, but not concurrently." -msgstr "" -"`FnMut`(例如 `accumulate`)可能会改变捕获的值。您可以多次调用它, 但不能并发" -"调用它。" +"The effective expansion of `?` is a little more complicated than previously " +"indicated:" +msgstr "`?` 的有效展开比前面介绍的内容略微复杂一些:" -#: src/traits/closures.md:40 -msgid "" -"If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It " -"might consume captured values." -msgstr "" -"如果您使用 `FnOnce`(例如 `multiply_sum`),或许只能调用它一次。它可能会耗用 " -"所捕获的值。" +#: src/error-handling/converting-error-types.md:9 +msgid "works the same as" +msgstr "效果等同于" -#: src/traits/closures.md:47 +#: src/error-handling/converting-error-types.md:18 msgid "" -"The compiler also infers `Copy` (e.g. for `add_3`) and `Clone` (e.g. " -"`multiply_sum`), depending on what the closure captures." +"The `From::from` call here means we attempt to convert the error type to the " +"type returned by the function:" msgstr "" -"编译器也会推断 `Copy`(例如针对 `add_3`)和 `Clone`(例如 `multiply_sum`), " -"具体取决于闭包捕获的数据。" +"此处的 `From::from` 调用表示,我们尝试将错误类型转换为 函数返回的类型:" -#: src/traits/closures.md:50 +#: src/error-handling/converting-error-types-example.md:3 msgid "" -"By default, closures will capture by reference if they can. The `move` " -"keyword makes them capture by value." +"```rust,editable\n" +"use std::error::Error;\n" +"use std::fmt::{self, Display, Formatter};\n" +"use std::fs::{self, File};\n" +"use std::io::{self, Read};\n" +"\n" +"#[derive(Debug)]\n" +"enum ReadUsernameError {\n" +" IoError(io::Error),\n" +" EmptyUsername(String),\n" +"}\n" +"\n" +"impl Error for ReadUsernameError {}\n" +"\n" +"impl Display for ReadUsernameError {\n" +" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n" +" match self {\n" +" Self::IoError(e) => write!(f, \"IO error: {e}\"),\n" +" Self::EmptyUsername(filename) => write!(f, \"Found no username " +"in {filename}\"),\n" +" }\n" +" }\n" +"}\n" +"\n" +"impl From for ReadUsernameError {\n" +" fn from(err: io::Error) -> ReadUsernameError {\n" +" ReadUsernameError::IoError(err)\n" +" }\n" +"}\n" +"\n" +"fn read_username(path: &str) -> Result {\n" +" let mut username = String::with_capacity(100);\n" +" File::open(path)?.read_to_string(&mut username)?;\n" +" if username.is_empty() {\n" +" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n" +" }\n" +" Ok(username)\n" +"}\n" +"\n" +"fn main() {\n" +" //fs::write(\"config.dat\", \"\").unwrap();\n" +" let username = read_username(\"config.dat\");\n" +" println!(\"username or error: {username:?}\");\n" +"}\n" +"```" msgstr "" -"默认情况下,闭包会依据引用来捕获数据(如果可以的话)。`move` 关键字则可让闭包" -"依据值 来捕获数据。" -#: src/exercises/day-3/morning.md:1 -msgid "Day 3: Morning Exercises" +#: src/error-handling/converting-error-types-example.md:55 +msgid "" +"It is good practice for all error types that don't need to be `no_std` to " +"implement `std::error::Error`, which requires `Debug` and `Display`. The " +"`Error` crate for `core` is only available in [nightly](https://github.com/" +"rust-lang/rust/issues/103765), so not fully `no_std` compatible yet." msgstr "" -#: src/exercises/day-3/morning.md:3 -msgid "We will design a classical GUI library traits and trait objects." +#: src/error-handling/converting-error-types-example.md:57 +#, fuzzy +msgid "" +"It's generally helpful for them to implement `Clone` and `Eq` too where " +"possible, to make life easier for tests and consumers of your library. In " +"this case we can't easily do so, because `io::Error` doesn't implement them." msgstr "" +"对所有错误类型实现 `std::error::Error` 是一种很好的做法,而这需要结合使用 " +"`Debug` 和 `Display` 方法。 通常,在可能的情况下实现 `Clone` 和 `Eq` 也十分有" +"益, 可以让库的测试和使用变得更加简单。在本例中,我们无法轻松做到这一点, 因" +"为 `io::Error` 不能实现这些方法。" -#: src/exercises/day-3/simple-gui.md:3 +#: src/error-handling/deriving-error-enums.md:3 msgid "" -"Let us design a classical GUI library using our new knowledge of traits and " -"trait objects." -msgstr "" - -#: src/exercises/day-3/simple-gui.md:6 -msgid "We will have a number of widgets in our library:" -msgstr "" - -#: src/exercises/day-3/simple-gui.md:8 -msgid "`Window`: has a `title` and contains other widgets." +"The [thiserror](https://docs.rs/thiserror/) crate is a popular way to create " +"an error enum like we did on the previous page:" msgstr "" +"[thiserror](https://docs.rs/thiserror/) crate 是创建错误枚举的常用方法, 就像" +"前一页中提供的示例一样:" -#: src/exercises/day-3/simple-gui.md:9 +#: src/error-handling/deriving-error-enums.md:6 msgid "" -"`Button`: has a `label` and a callback function which is invoked when the " -"button is pressed." +"```rust,editable,compile_fail\n" +"use std::{fs, io};\n" +"use std::io::Read;\n" +"use thiserror::Error;\n" +"\n" +"#[derive(Debug, Error)]\n" +"enum ReadUsernameError {\n" +" #[error(\"Could not read: {0}\")]\n" +" IoError(#[from] io::Error),\n" +" #[error(\"Found no username in {0}\")]\n" +" EmptyUsername(String),\n" +"}\n" +"\n" +"fn read_username(path: &str) -> Result {\n" +" let mut username = String::new();\n" +" fs::File::open(path)?.read_to_string(&mut username)?;\n" +" if username.is_empty() {\n" +" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n" +" }\n" +" Ok(username)\n" +"}\n" +"\n" +"fn main() {\n" +" //fs::write(\"config.dat\", \"\").unwrap();\n" +" match read_username(\"config.dat\") {\n" +" Ok(username) => println!(\"Username: {username}\"),\n" +" Err(err) => println!(\"Error: {err}\"),\n" +" }\n" +"}\n" +"```" msgstr "" -#: src/exercises/day-3/simple-gui.md:11 -msgid "`Label`: has a `label`." +#: src/error-handling/deriving-error-enums.md:39 +msgid "" +"`thiserror`'s derive macro automatically implements `std::error::Error`, and " +"optionally `Display` (if the `#[error(...)]` attributes are provided) and " +"`From` (if the `#[from]` attribute is added). It also works for structs." msgstr "" +"`thiserror` 的派生宏会自动实现 `std::error::Error`,并且可以选择性地实现 " +"`Display` (如果提供了 `#[error(...)]` 属性)和 `From`(如果添加了 `#[from]` " +"属性)。 此规则也适用于结构体。" -#: src/exercises/day-3/simple-gui.md:13 -msgid "The widgets will implement a `Widget` trait, see below." -msgstr "" +#: src/error-handling/deriving-error-enums.md:43 +msgid "It doesn't affect your public API, which makes it good for libraries." +msgstr "但是,此规则不会影响公共 API,对于库而言,这非常理想。" -#: src/exercises/day-3/simple-gui.md:15 +#: src/error-handling/dynamic-errors.md:3 msgid "" -"Copy the code below to , fill in the missing " -"`draw_into` methods so that you implement the `Widget` trait:" +"Sometimes we want to allow any type of error to be returned without writing " +"our own enum covering all the different possibilities. `std::error::Error` " +"makes this easy." msgstr "" +"有时,我们需要允许返回任意类型的错误,但又不想自己手动编写枚举来涵盖所有不同" +"的可能性。 `std::error::Error` 可以让我们轻松做到这一点。" -#: src/exercises/day-3/simple-gui.md:18 +#: src/error-handling/dynamic-errors.md:6 msgid "" -"```rust,should_panic\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_imports, unused_variables, dead_code)]\n" -"\n" -"pub trait Widget {\n" -" /// Natural width of `self`.\n" -" fn width(&self) -> usize;\n" -"\n" -" /// Draw the widget into a buffer.\n" -" fn draw_into(&self, buffer: &mut dyn std::fmt::Write);\n" -"\n" -" /// Draw the widget on standard output.\n" -" fn draw(&self) {\n" -" let mut buffer = String::new();\n" -" self.draw_into(&mut buffer);\n" -" println!(\"{buffer}\");\n" -" }\n" -"}\n" -"\n" -"pub struct Label {\n" -" label: String,\n" -"}\n" -"\n" -"impl Label {\n" -" fn new(label: &str) -> Label {\n" -" Label {\n" -" label: label.to_owned(),\n" -" }\n" -" }\n" -"}\n" -"\n" -"pub struct Button {\n" -" label: Label,\n" -" callback: Box,\n" -"}\n" -"\n" -"impl Button {\n" -" fn new(label: &str, callback: Box) -> Button {\n" -" Button {\n" -" label: Label::new(label),\n" -" callback,\n" -" }\n" -" }\n" -"}\n" -"\n" -"pub struct Window {\n" -" title: String,\n" -" widgets: Vec>,\n" -"}\n" -"\n" -"impl Window {\n" -" fn new(title: &str) -> Window {\n" -" Window {\n" -" title: title.to_owned(),\n" -" widgets: Vec::new(),\n" -" }\n" -" }\n" -"\n" -" fn add_widget(&mut self, widget: Box) {\n" -" self.widgets.push(widget);\n" -" }\n" -"\n" -" fn inner_width(&self) -> usize {\n" -" std::cmp::max(\n" -" self.title.chars().count(),\n" -" self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),\n" -" )\n" -" }\n" -"}\n" -"\n" -"\n" -"impl Widget for Label {\n" -" fn width(&self) -> usize {\n" -" unimplemented!()\n" -" }\n" -"\n" -" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" unimplemented!()\n" -" }\n" -"}\n" -"\n" -"impl Widget for Button {\n" -" fn width(&self) -> usize {\n" -" unimplemented!()\n" -" }\n" -"\n" -" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" unimplemented!()\n" -" }\n" -"}\n" +"```rust,editable,compile_fail\n" +"use std::fs;\n" +"use std::io::Read;\n" +"use thiserror::Error;\n" +"use std::error::Error;\n" "\n" -"impl Widget for Window {\n" -" fn width(&self) -> usize {\n" -" unimplemented!()\n" -" }\n" +"#[derive(Clone, Debug, Eq, Error, PartialEq)]\n" +"#[error(\"Found no username in {0}\")]\n" +"struct EmptyUsernameError(String);\n" "\n" -" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" unimplemented!()\n" +"fn read_username(path: &str) -> Result> {\n" +" let mut username = String::new();\n" +" fs::File::open(path)?.read_to_string(&mut username)?;\n" +" if username.is_empty() {\n" +" return Err(EmptyUsernameError(String::from(path)).into());\n" " }\n" +" Ok(username)\n" "}\n" "\n" "fn main() {\n" -" let mut window = Window::new(\"Rust GUI Demo 1.23\");\n" -" window.add_widget(Box::new(Label::new(\"This is a small text GUI demo." -"\")));\n" -" window.add_widget(Box::new(Button::new(\n" -" \"Click me!\",\n" -" Box::new(|| println!(\"You clicked the button!\")),\n" -" )));\n" -" window.draw();\n" +" //fs::write(\"config.dat\", \"\").unwrap();\n" +" match read_username(\"config.dat\") {\n" +" Ok(username) => println!(\"Username: {username}\"),\n" +" Err(err) => println!(\"Error: {err}\"),\n" +" }\n" "}\n" "```" msgstr "" -#: src/exercises/day-3/simple-gui.md:130 -msgid "The output of the above program can be something simple like this:" -msgstr "" - -#: src/exercises/day-3/simple-gui.md:132 +#: src/error-handling/dynamic-errors.md:36 msgid "" -"```text\n" -"========\n" -"Rust GUI Demo 1.23\n" -"========\n" -"\n" -"This is a small text GUI demo.\n" -"\n" -"| Click me! |\n" -"```" +"This saves on code, but gives up the ability to cleanly handle different " +"error cases differently in the program. As such it's generally not a good " +"idea to use `Box` in the public API of a library, but it can be a " +"good option in a program where you just want to display the error message " +"somewhere." msgstr "" +"虽然这可以省却编写代码的麻烦,但也会导致我们无法在程序中以不同的方式正常处理" +"不同的 错误情况。因此,在库的公共 API 中使用 `Box` 通常不是一个好" +"主意。 但是对于您只需要在某处显示错误消息的程序来说,这不失为一个 很好的选" +"择。" -#: src/exercises/day-3/simple-gui.md:142 +#: src/error-handling/error-contexts.md:3 msgid "" -"If you want to draw aligned text, you can use the [fill/alignment](https://" -"doc.rust-lang.org/std/fmt/index.html#fillalignment) formatting operators. In " -"particular, notice how you can pad with different characters (here a `'/'`) " -"and how you can control alignment:" +"The widely used [anyhow](https://docs.rs/anyhow/) crate can help you add " +"contextual information to your errors and allows you to have fewer custom " +"error types:" msgstr "" +"广泛使用的 [anyhow](https://docs.rs/anyhow/) crate 可以帮助我们为错误添加 背" +"景信息,并减少自定义错误类型的数量:" -#: src/exercises/day-3/simple-gui.md:147 +#: src/error-handling/error-contexts.md:7 msgid "" -"```rust,editable\n" +"```rust,editable,compile_fail\n" +"use std::{fs, io};\n" +"use std::io::Read;\n" +"use anyhow::{Context, Result, bail};\n" +"\n" +"fn read_username(path: &str) -> Result {\n" +" let mut username = String::with_capacity(100);\n" +" fs::File::open(path)\n" +" .with_context(|| format!(\"Failed to open {path}\"))?\n" +" .read_to_string(&mut username)\n" +" .context(\"Failed to read\")?;\n" +" if username.is_empty() {\n" +" bail!(\"Found no username in {path}\");\n" +" }\n" +" Ok(username)\n" +"}\n" +"\n" "fn main() {\n" -" let width = 10;\n" -" println!(\"left aligned: |{:/width$}|\", \"foo\");\n" +" //fs::write(\"config.dat\", \"\").unwrap();\n" +" match read_username(\"config.dat\") {\n" +" Ok(username) => println!(\"Username: {username}\"),\n" +" Err(err) => println!(\"Error: {err:?}\"),\n" +" }\n" "}\n" "```" msgstr "" -#: src/exercises/day-3/simple-gui.md:156 -msgid "" -"Using such alignment tricks, you can for example produce output like this:" -msgstr "" +#: src/error-handling/error-contexts.md:35 +#, fuzzy +msgid "`anyhow::Result` is a type alias for `Result`." +msgstr "“anyhow::Result" -#: src/exercises/day-3/simple-gui.md:158 +#: src/error-handling/error-contexts.md:36 +#, fuzzy msgid "" -"```text\n" -"+--------------------------------+\n" -"| Rust GUI Demo 1.23 |\n" -"+================================+\n" -"| This is a small text GUI demo. |\n" -"| +-----------+ |\n" -"| | Click me! | |\n" -"| +-----------+ |\n" -"+--------------------------------+\n" -"```" -msgstr "" - -#: src/error-handling.md:3 -msgid "Error handling in Rust is done using explicit control flow:" -msgstr "Rust 中的错误处理是使用显式控制流来进行的:" - -#: src/error-handling.md:5 -msgid "Functions that can have errors list this in their return type," -msgstr "包含错误的函数会在返回类型中列出相关信息。" - -#: src/error-handling.md:6 -msgid "There are no exceptions." -msgstr "此规则没有例外。" +"`anyhow::Error` is essentially a wrapper around `Box`. As such " +"it's again generally not a good choice for the public API of a library, but " +"is widely used in applications." +msgstr "”是“Result\\”的类型别名。" -#: src/error-handling/panics.md:3 -msgid "Rust will trigger a panic if a fatal error happens at runtime:" -msgstr "如果运行时发生严重错误,Rust 会触发 panic:" +#: src/error-handling/error-contexts.md:38 +#, fuzzy +msgid "" +"Actual error type inside of it can be extracted for examination if necessary." +msgstr "“anyhow::Error”本质上是“Box" -#: src/error-handling/panics.md:5 +#: src/error-handling/error-contexts.md:39 +#, fuzzy msgid "" -"```rust,editable,should_panic\n" -"fn main() {\n" -" let v = vec![10, 20, 30];\n" -" println!(\"v[100]: {}\", v[100]);\n" -"}\n" -"```" +"Functionality provided by `anyhow::Result` may be familiar to Go " +"developers, as it provides similar usage patterns and ergonomics to `(T, " +"error)` from Go." msgstr "" +"”的封装容器。因此,就像前面提到的那样,在库的公共 API 中 使用它通常不是一个好" +"主意。但是它广泛用于应用中。\n" +"\n" +"如果需要,可以提取其内部的实际错误类型进行检查。\n" +"\n" +"Go 开发者可能会十分熟悉 `anyhow::Result` 提供的功能, 因为它的使用模式和工" +"效学设计与 Go 的 `(T, error)` 方法十分相似。" -#: src/error-handling/panics.md:12 -msgid "Panics are for unrecoverable and unexpected errors." -msgstr "Panic 用于指示不可恢复的意外错误。" - -#: src/error-handling/panics.md:13 -msgid "Panics are symptoms of bugs in the program." -msgstr "Panic反映了程序中的 bug 问题。" +#: src/testing.md:3 +msgid "Rust and Cargo come with a simple unit test framework:" +msgstr "Rust 和 Cargo 随附了一个简单的单元测试框架:" -#: src/error-handling/panics.md:14 -msgid "" -"Use non-panicking APIs (such as `Vec::get`) if crashing is not acceptable." -msgstr "如果崩溃不可接受,请使用不会触发 panic 的 API(例如 `Vec::get`)。" +#: src/testing.md:5 +msgid "Unit tests are supported throughout your code." +msgstr "单元测试在您的整个代码中都受支持。" -#: src/error-handling/panic-unwind.md:1 -msgid "Catching the Stack Unwinding" -msgstr "捕获堆栈展开" +#: src/testing.md:7 +msgid "Integration tests are supported via the `tests/` directory." +msgstr "您可以通过 `tests/` 目录来支持集成测试。" -#: src/error-handling/panic-unwind.md:3 -msgid "" -"By default, a panic will cause the stack to unwind. The unwinding can be " -"caught:" -msgstr "默认情况下,panic 会导致堆栈展开。您可以捕获展开信息:" +#: src/testing/unit-tests.md:3 +msgid "Mark unit tests with `#[test]`:" +msgstr "使用 `#[test]` 标记单元测试:" -#: src/error-handling/panic-unwind.md:5 +#: src/testing/unit-tests.md:5 msgid "" -"```rust,editable\n" -"use std::panic;\n" +"```rust,editable,ignore\n" +"fn first_word(text: &str) -> &str {\n" +" match text.find(' ') {\n" +" Some(idx) => &text[..idx],\n" +" None => &text,\n" +" }\n" +"}\n" +"\n" +"#[test]\n" +"fn test_empty() {\n" +" assert_eq!(first_word(\"\"), \"\");\n" +"}\n" "\n" -"let result = panic::catch_unwind(|| {\n" -" println!(\"hello!\");\n" -"});\n" -"assert!(result.is_ok());\n" +"#[test]\n" +"fn test_single_word() {\n" +" assert_eq!(first_word(\"Hello\"), \"Hello\");\n" +"}\n" "\n" -"let result = panic::catch_unwind(|| {\n" -" panic!(\"oh no!\");\n" -"});\n" -"assert!(result.is_err());\n" +"#[test]\n" +"fn test_multiple_words() {\n" +" assert_eq!(first_word(\"Hello World\"), \"Hello\");\n" +"}\n" "```" msgstr "" -#: src/error-handling/panic-unwind.md:21 -msgid "" -"This can be useful in servers which should keep running even if a single " -"request crashes." -msgstr "" -"如果服务器需要持续运行(即使是在请求发生崩溃的情况下), 此方法十分有用。" - -#: src/error-handling/panic-unwind.md:23 -msgid "This does not work if `panic = 'abort'` is set in your `Cargo.toml`." -msgstr "如果您在 `Cargo.toml` 中设置了 `panic = 'abort'`,此方法不会生效。" - -#: src/error-handling/result.md:1 -msgid "Structured Error Handling with `Result`" -msgstr "使用 `Result` 进行结构化错误处理" +#: src/testing/unit-tests.md:29 +msgid "Use `cargo test` to find and run the unit tests." +msgstr "使用 `cargo test` 查找并运行单元测试。" -#: src/error-handling/result.md:3 +#: src/testing/test-modules.md:3 msgid "" -"We have already seen the `Result` enum. This is used pervasively when errors " -"are expected as part of normal operation:" +"Unit tests are often put in a nested module (run tests on the [Playground]" +"(https://play.rust-lang.org/)):" msgstr "" -"在前面,我们看到了 `Result` 枚举。在遇到正常操作产生的预期错误时, 我们常会用" -"到此方法:" +"单元测试通常会放在嵌套模块中(在 [Playground](https://play.rust-lang.org/) 上" +"运行测试):" -#: src/error-handling/result.md:6 +#: src/testing/test-modules.md:6 msgid "" "```rust,editable\n" -"use std::fs::File;\n" -"use std::io::Read;\n" +"fn helper(a: &str, b: &str) -> String {\n" +" format!(\"{a} {b}\")\n" +"}\n" "\n" -"fn main() {\n" -" let file = File::open(\"diary.txt\");\n" -" match file {\n" -" Ok(mut file) => {\n" -" let mut contents = String::new();\n" -" file.read_to_string(&mut contents);\n" -" println!(\"Dear diary: {contents}\");\n" -" },\n" -" Err(err) => {\n" -" println!(\"The diary could not be opened: {err}\");\n" -" }\n" +"pub fn main() {\n" +" println!(\"{}\", helper(\"Hello\", \"World\"));\n" +"}\n" +"\n" +"#[cfg(test)]\n" +"mod tests {\n" +" use super::*;\n" +"\n" +" #[test]\n" +" fn test_helper() {\n" +" assert_eq!(helper(\"foo\", \"bar\"), \"foo bar\");\n" " }\n" "}\n" "```" msgstr "" -#: src/error-handling/result.md:27 -msgid "" -"As with `Option`, the successful value sits inside of `Result`, forcing the " -"developer to explicitly extract it. This encourages error checking. In the " -"case where an error should never happen, `unwrap()` or `expect()` can be " -"called, and this is a signal of the developer intent too." -msgstr "" -"与 `Option` 方法相同,成功值位于 `Result` 方法内部, 开发者必须显示提取成功" -"值。因此,建议进行错误检查。在绝不应出现错误的情况下, 可以调用 `unwrap()` " -"或 `expect()` 方法,这也是一种开发者意向信号。" - -#: src/error-handling/result.md:30 -msgid "" -"`Result` documentation is a recommended read. Not during the course, but it " -"is worth mentioning. It contains a lot of convenience methods and functions " -"that help functional-style programming. " -msgstr "" -"我们建议阅读 `Result` 文档。虽然课程中不会涉及该文档,但是有必要提到它。 该文" -"档中包含许多便捷的方法和函数,对于函数式编程很有帮助。" +#: src/testing/test-modules.md:26 +msgid "This lets you unit test private helpers." +msgstr "这样一来,您可以对专用帮助程序进行单元测试。" -#: src/error-handling/try-operator.md:1 -msgid "Propagating Errors with `?`" -msgstr "使用 `?` 传播错误" +#: src/testing/test-modules.md:27 +msgid "The `#[cfg(test)]` attribute is only active when you run `cargo test`." +msgstr "仅当您运行 `cargo test` 时,`#[cfg(test)]` 属性才有效。" -#: src/error-handling/try-operator.md:3 -msgid "" -"The try-operator `?` is used to return errors to the caller. It lets you " -"turn the common" -msgstr "try 操作符 `?` 用于将错误返回给调用方。它能把常用命令" +#: src/testing/doc-tests.md:3 +msgid "Rust has built-in support for documentation tests:" +msgstr "Rust 本身就支持文档测试:" -#: src/error-handling/try-operator.md:6 +#: src/testing/doc-tests.md:5 msgid "" -"```rust,ignore\n" -"match some_expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(err),\n" +"```rust\n" +"/// Shortens a string to the given length.\n" +"///\n" +"/// ```\n" +"/// use playground::shorten_string;\n" +"/// assert_eq!(shorten_string(\"Hello World\", 5), \"Hello\");\n" +"/// assert_eq!(shorten_string(\"Hello World\", 20), \"Hello World\");\n" +"/// ```\n" +"pub fn shorten_string(s: &str, length: usize) -> &str {\n" +" &s[..std::cmp::min(length, s.len())]\n" "}\n" "```" msgstr "" -"```rust,ignore\n" -"match some_expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(err),\n" -"}\n" -"```" -#: src/error-handling/try-operator.md:13 -msgid "into the much simpler" -msgstr "转换成更简单的命令" +#: src/testing/doc-tests.md:18 +msgid "Code blocks in `///` comments are automatically seen as Rust code." +msgstr "`///` 注释中的代码块会自动被视为 Rust 代码。" + +#: src/testing/doc-tests.md:19 +msgid "The code will be compiled and executed as part of `cargo test`." +msgstr "代码会作为 `cargo test` 的一部分进行编译和执行。" -#: src/error-handling/try-operator.md:15 +#: src/testing/doc-tests.md:20 msgid "" -"```rust,ignore\n" -"some_expression?\n" -"```" +"Test the above code on the [Rust Playground](https://play.rust-lang.org/?" +"version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0)." msgstr "" -"```rust,ignore\n" -"some_expression?\n" -"```" +"在 [Rust Playground](https://play.rust-lang.org/?" +"version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0) " +"上测试上述代码。" -#: src/error-handling/try-operator.md:19 -msgid "We can use this to simplify our error handing code:" -msgstr "我们可以用它来简化错误处理代码:" +#: src/testing/integration-tests.md:3 +msgid "If you want to test your library as a client, use an integration test." +msgstr "如果您想要以客户的身份测试您的库,请使用集成测试。" -#: src/error-handling/try-operator.md:21 +#: src/testing/integration-tests.md:5 +msgid "Create a `.rs` file under `tests/`:" +msgstr "在 `tests/` 下方创建一个 `.rs` 文件:" + +#: src/testing/integration-tests.md:16 +msgid "These tests only have access to the public API of your crate." +msgstr "这些测试只能使用您的 crate 的公共 API。" + +#: src/testing/useful-crates.md:1 +msgid "Useful crates for writing tests" +msgstr "用于编写测试的实用 crate" + +#: src/testing/useful-crates.md:3 +msgid "Rust comes with only basic support for writing tests." +msgstr "Rust 仅为编写测试提供基本支持。" + +#: src/testing/useful-crates.md:5 +msgid "Here are some additional crates which we recommend for writing tests:" +msgstr "下面列出了我们建议在编写测试时使用的一些其他 crate:" + +#: src/testing/useful-crates.md:7 +msgid "" +"[googletest](https://docs.rs/googletest): Comprehensive test assertion " +"library in the tradition of GoogleTest for C++." +msgstr "" +"[googletest](https://docs.rs/googletest):遵从 GoogleTest for C++ 传统的综合" +"测试断言库。" + +#: src/testing/useful-crates.md:8 +msgid "[proptest](https://docs.rs/proptest): Property-based testing for Rust." +msgstr "[proptest](https://docs.rs/proptest):基于属性的测试,适用于 Rust。" + +#: src/testing/useful-crates.md:9 +msgid "" +"[rstest](https://docs.rs/rstest): Support for fixtures and parameterised " +"tests." +msgstr "[rstest](https://docs.rs/rstest):支持固件和参数化测试。" + +#: src/unsafe.md:3 +msgid "The Rust language has two parts:" +msgstr "Rust 语言包含两个部分:" + +#: src/unsafe.md:5 +msgid "**Safe Rust:** memory safe, no undefined behavior possible." +msgstr "\\*\\*安全 Rust:\\*\\*内存安全,没有潜在的未定义行为。" + +#: src/unsafe.md:6 +msgid "" +"**Unsafe Rust:** can trigger undefined behavior if preconditions are " +"violated." +msgstr "\\*\\*不安全 Rust:\\*\\*如果违反了前提条件,可能会触发未定义的行为。" + +#: src/unsafe.md:8 +msgid "" +"We will be seeing mostly safe Rust in this course, but it's important to " +"know what Unsafe Rust is." +msgstr "" +"本课程中出现的大多为“安全 Rust”,但是了解“不安全 Rust”的定义 非常重要。" + +#: src/unsafe.md:11 +msgid "" +"Unsafe code is usually small and isolated, and its correctness should be " +"carefully documented. It is usually wrapped in a safe abstraction layer." +msgstr "" +"不安全的代码通常内容很少而且与其他代码隔离, 其正确性也应得到仔细记录。这类代" +"码通常封装在安全的抽象层中。" + +#: src/unsafe.md:14 +msgid "Unsafe Rust gives you access to five new capabilities:" +msgstr "不安全 Rust 提供了五种新功能:" + +#: src/unsafe.md:16 +msgid "Dereference raw pointers." +msgstr "解引用原始指针。" + +#: src/unsafe.md:17 +msgid "Access or modify mutable static variables." +msgstr "访问或修改可变的静态变量。" + +#: src/unsafe.md:18 +msgid "Access `union` fields." +msgstr "访问 `union` 字段。" + +#: src/unsafe.md:19 +msgid "Call `unsafe` functions, including `extern` functions." +msgstr "调用 `unsafe` 函数,包括 `extern` 函数。" + +#: src/unsafe.md:20 +msgid "Implement `unsafe` traits." +msgstr "实现 `unsafe` trait。" + +#: src/unsafe.md:22 +msgid "" +"We will briefly cover unsafe capabilities next. For full details, please see " +"[Chapter 19.1 in the Rust Book](https://doc.rust-lang.org/book/ch19-01-" +"unsafe-rust.html) and the [Rustonomicon](https://doc.rust-lang.org/nomicon/)." +msgstr "" +"下面,我们将简要介绍这些不安全功能。如需了解完整详情,请参阅 [《Rust 手册》" +"第 19.1 章](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html) 和 " +"[Rustonomicon](https://doc.rust-lang.org/nomicon/)。" + +#: src/unsafe.md:28 +msgid "" +"Unsafe Rust does not mean the code is incorrect. It means that developers " +"have turned off the compiler safety features and have to write correct code " +"by themselves. It means the compiler no longer enforces Rust's memory-safety " +"rules." +msgstr "" +"不安全 Rust 并不意味着代码不正确,而是这意味着开发者已停用 编译器的安全功能," +"必须自行编写正确的 代码。也就是说,编译器不再强制执行 Rust 的内存安全规则。" + +#: src/unsafe/raw-pointers.md:3 +msgid "Creating pointers is safe, but dereferencing them requires `unsafe`:" +msgstr "创建指针是安全的操作,但解引用指针需要使用 `unsafe` 方法:" + +#: src/unsafe/raw-pointers.md:5 msgid "" "```rust,editable\n" -"use std::fs;\n" -"use std::io::{self, Read};\n" +"fn main() {\n" +" let mut num = 5;\n" "\n" -"fn read_username(path: &str) -> Result {\n" -" let username_file_result = fs::File::open(path);\n" -" let mut username_file = match username_file_result {\n" -" Ok(file) => file,\n" -" Err(err) => return Err(err),\n" -" };\n" +" let r1 = &mut num as *mut i32;\n" +" let r2 = r1 as *const i32;\n" "\n" -" let mut username = String::new();\n" -" match username_file.read_to_string(&mut username) {\n" -" Ok(_) => Ok(username),\n" -" Err(err) => Err(err),\n" +" // Safe because r1 and r2 were obtained from references and so are\n" +" // guaranteed to be non-null and properly aligned, the objects " +"underlying\n" +" // the references from which they were obtained are live throughout the\n" +" // whole unsafe block, and they are not accessed either through the\n" +" // references or concurrently through any other pointers.\n" +" unsafe {\n" +" println!(\"r1 is: {}\", *r1);\n" +" *r1 = 10;\n" +" println!(\"r2 is: {}\", *r2);\n" " }\n" "}\n" -"\n" -"fn main() {\n" -" //fs::write(\"config.dat\", \"alice\").unwrap();\n" -" let username = read_username(\"config.dat\");\n" -" println!(\"username or error: {username:?}\");\n" -"}\n" "```" msgstr "" -#: src/error-handling/try-operator.md:50 -#: src/error-handling/converting-error-types-example.md:52 -msgid "The `username` variable can be either `Ok(string)` or `Err(error)`." -msgstr "`username` 变量可以是 `Ok(string)` 或 `Err(error)`。" +#: src/unsafe/raw-pointers.md:27 +msgid "" +"It is good practice (and required by the Android Rust style guide) to write " +"a comment for each `unsafe` block explaining how the code inside it " +"satisfies the safety requirements of the unsafe operations it is doing." +msgstr "" +"我们建议(而且 Android Rust 样式指南要求)为每个 `unsafe` 代码块编写一条注" +"释, 说明该代码块中的代码如何满足其所执行的不安全操作的 安全要求。" -#: src/error-handling/try-operator.md:51 -#: src/error-handling/converting-error-types-example.md:53 +#: src/unsafe/raw-pointers.md:31 msgid "" -"Use the `fs::write` call to test out the different scenarios: no file, empty " -"file, file with username." +"In the case of pointer dereferences, this means that the pointers must be " +"[_valid_](https://doc.rust-lang.org/std/ptr/index.html#safety), i.e.:" msgstr "" -"可以使用 `fs::write` 调用来测试不同的场景:没有文件、空文件、包含用户名的文" -"件。" +"对于指针解除引用,这意味着指针必须为 [_valid_](https://doc.rust-lang.org/std/" +"ptr/index.html#safety),即:" -#: src/error-handling/converting-error-types.md:3 +#: src/unsafe/raw-pointers.md:34 +msgid "The pointer must be non-null." +msgstr "指针必须为非 null。" + +#: src/unsafe/raw-pointers.md:35 msgid "" -"The effective expansion of `?` is a little more complicated than previously " -"indicated:" -msgstr "`?` 的有效展开比前面介绍的内容略微复杂一些:" +"The pointer must be _dereferenceable_ (within the bounds of a single " +"allocated object)." +msgstr "指针必须是 _dereferenceable_(在单个已分配对象的边界内)。" + +#: src/unsafe/raw-pointers.md:36 +msgid "The object must not have been deallocated." +msgstr "对象不得已取消分配。" + +#: src/unsafe/raw-pointers.md:37 +msgid "There must not be concurrent accesses to the same location." +msgstr "不得并发访问相同位置。" -#: src/error-handling/converting-error-types.md:5 +#: src/unsafe/raw-pointers.md:38 msgid "" -"```rust,ignore\n" -"expression?\n" -"```" +"If the pointer was obtained by casting a reference, the underlying object " +"must be live and no reference may be used to access the memory." msgstr "" -"```rust,ignore\n" -"expression?\n" -"```" +"如果通过转换引用类型来获取指针,则底层对象必须处于活跃状态, 而且不得使用任何" +"引用来访问内存。" -#: src/error-handling/converting-error-types.md:9 -msgid "works the same as" -msgstr "效果等同于" +#: src/unsafe/raw-pointers.md:41 +msgid "In most cases the pointer must also be properly aligned." +msgstr "在大多数情况下,指针还必须正确对齐。" + +#: src/unsafe/mutable-static-variables.md:3 +msgid "It is safe to read an immutable static variable:" +msgstr "读取不可变的静态变量是安全的操作:" -#: src/error-handling/converting-error-types.md:11 +#: src/unsafe/mutable-static-variables.md:5 msgid "" -"```rust,ignore\n" -"match expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(From::from(err)),\n" +"```rust,editable\n" +"static HELLO_WORLD: &str = \"Hello, world!\";\n" +"\n" +"fn main() {\n" +" println!(\"HELLO_WORLD: {HELLO_WORLD}\");\n" "}\n" "```" msgstr "" -"```rust,ignore\n" -"match expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(From::from(err)),\n" -"}\n" -"```" -#: src/error-handling/converting-error-types.md:18 +#: src/unsafe/mutable-static-variables.md:13 msgid "" -"The `From::from` call here means we attempt to convert the error type to the " -"type returned by the function:" -msgstr "" -"此处的 `From::from` 调用表示,我们尝试将错误类型转换为 函数返回的类型:" +"However, since data races can occur, it is unsafe to read and write mutable " +"static variables:" +msgstr "但是,读取和写入可变的静态变量是不安全的,因为这可能会 造成数据争用:" -#: src/error-handling/converting-error-types-example.md:3 +#: src/unsafe/mutable-static-variables.md:16 msgid "" "```rust,editable\n" -"use std::error::Error;\n" -"use std::fmt::{self, Display, Formatter};\n" -"use std::fs::{self, File};\n" -"use std::io::{self, Read};\n" -"\n" -"#[derive(Debug)]\n" -"enum ReadUsernameError {\n" -" IoError(io::Error),\n" -" EmptyUsername(String),\n" -"}\n" -"\n" -"impl Error for ReadUsernameError {}\n" -"\n" -"impl Display for ReadUsernameError {\n" -" fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n" -" match self {\n" -" Self::IoError(e) => write!(f, \"IO error: {e}\"),\n" -" Self::EmptyUsername(filename) => write!(f, \"Found no username " -"in {filename}\"),\n" -" }\n" -" }\n" -"}\n" -"\n" -"impl From for ReadUsernameError {\n" -" fn from(err: io::Error) -> ReadUsernameError {\n" -" ReadUsernameError::IoError(err)\n" -" }\n" -"}\n" +"static mut COUNTER: u32 = 0;\n" "\n" -"fn read_username(path: &str) -> Result {\n" -" let mut username = String::with_capacity(100);\n" -" File::open(path)?.read_to_string(&mut username)?;\n" -" if username.is_empty() {\n" -" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n" -" }\n" -" Ok(username)\n" +"fn add_to_counter(inc: u32) {\n" +" unsafe { COUNTER += inc; } // Potential data race!\n" "}\n" "\n" "fn main() {\n" -" //fs::write(\"config.dat\", \"\").unwrap();\n" -" let username = read_username(\"config.dat\");\n" -" println!(\"username or error: {username:?}\");\n" +" add_to_counter(42);\n" +"\n" +" unsafe { println!(\"COUNTER: {COUNTER}\"); } // Potential data race!\n" "}\n" "```" msgstr "" -#: src/error-handling/converting-error-types-example.md:55 +#: src/unsafe/mutable-static-variables.md:32 msgid "" -"It is good practice for all error types to implement `std::error::Error`, " -"which requires `Debug` and `Display`. It's generally helpful for them to " -"implement `Clone` and `Eq` too where possible, to make life easier for tests " -"and consumers of your library. In this case we can't easily do so, because " -"`io::Error` doesn't implement them." +"Using a mutable static is generally a bad idea, but there are some cases " +"where it might make sense in low-level `no_std` code, such as implementing a " +"heap allocator or working with some C APIs." msgstr "" -"对所有错误类型实现 `std::error::Error` 是一种很好的做法,而这需要结合使用 " -"`Debug` 和 `Display` 方法。 通常,在可能的情况下实现 `Clone` 和 `Eq` 也十分有" -"益, 可以让库的测试和使用变得更加简单。在本例中,我们无法轻松做到这一点, 因" -"为 `io::Error` 不能实现这些方法。" +"通常,我们不建议使用可变的静态变量,但在某些情况下,在低层级 `no_std` 代码中" +"可能需要这样做, 例如实现堆分配器或使用某些 C API。" -#: src/error-handling/deriving-error-enums.md:3 -msgid "" -"The [thiserror](https://docs.rs/thiserror/) crate is a popular way to create " -"an error enum like we did on the previous page:" -msgstr "" -"[thiserror](https://docs.rs/thiserror/) crate 是创建错误枚举的常用方法, 就像" -"前一页中提供的示例一样:" +#: src/unsafe/unions.md:3 +msgid "Unions are like enums, but you need to track the active field yourself:" +msgstr "联合体与枚举类似,但您需要自行跟踪活跃字段:" -#: src/error-handling/deriving-error-enums.md:6 +#: src/unsafe/unions.md:5 msgid "" -"```rust,editable,compile_fail\n" -"use std::{fs, io};\n" -"use std::io::Read;\n" -"use thiserror::Error;\n" -"\n" -"#[derive(Debug, Error)]\n" -"enum ReadUsernameError {\n" -" #[error(\"Could not read: {0}\")]\n" -" IoError(#[from] io::Error),\n" -" #[error(\"Found no username in {0}\")]\n" -" EmptyUsername(String),\n" -"}\n" -"\n" -"fn read_username(path: &str) -> Result {\n" -" let mut username = String::with_capacity(100);\n" -" fs::File::open(path)?.read_to_string(&mut username)?;\n" -" if username.is_empty() {\n" -" return Err(ReadUsernameError::EmptyUsername(String::from(path)));\n" -" }\n" -" Ok(username)\n" +"```rust,editable\n" +"#[repr(C)]\n" +"union MyUnion {\n" +" i: u8,\n" +" b: bool,\n" "}\n" "\n" "fn main() {\n" -" //fs::write(\"config.dat\", \"\").unwrap();\n" -" match read_username(\"config.dat\") {\n" -" Ok(username) => println!(\"Username: {username}\"),\n" -" Err(err) => println!(\"Error: {err}\"),\n" -" }\n" +" let u = MyUnion { i: 42 };\n" +" println!(\"int: {}\", unsafe { u.i });\n" +" println!(\"bool: {}\", unsafe { u.b }); // Undefined behavior!\n" "}\n" "```" msgstr "" -#: src/error-handling/deriving-error-enums.md:39 +#: src/unsafe/unions.md:21 msgid "" -"`thiserror`'s derive macro automatically implements `std::error::Error`, and " -"optionally `Display` (if the `#[error(...)]` attributes are provided) and " -"`From` (if the `#[from]` attribute is added). It also works for structs." +"Unions are very rarely needed in Rust as you can usually use an enum. They " +"are occasionally needed for interacting with C library APIs." msgstr "" -"`thiserror` 的派生宏会自动实现 `std::error::Error`,并且可以选择性地实现 " -"`Display` (如果提供了 `#[error(...)]` 属性)和 `From`(如果添加了 `#[from]` " -"属性)。 此规则也适用于结构体。" +"在 Rust 中很少需要用到联合体,因为您通常可以使用枚举。联合体只是偶尔用于 与 " +"C 库 API 进行交互。" -#: src/error-handling/deriving-error-enums.md:43 -msgid "It doesn't affect your public API, which makes it good for libraries." -msgstr "但是,此规则不会影响公共 API,对于库而言,这非常理想。" +#: src/unsafe/unions.md:24 +msgid "" +"If you just want to reinterpret bytes as a different type, you probably want " +"[`std::mem::transmute`](https://doc.rust-lang.org/stable/std/mem/fn." +"transmute.html) or a safe wrapper such as the [`zerocopy`](https://crates.io/" +"crates/zerocopy) crate." +msgstr "" +"如果您只是想将字节重新解释为其他类型,则可能需要使用 [`std::mem::transmute`]" +"(https://doc.rust-lang.org/stable/std/mem/fn.transmute.html) 或 安全的封装容" +"器,例如 [`zerocopy`](https://crates.io/crates/zerocopy) crate。" -#: src/error-handling/dynamic-errors.md:3 +#: src/unsafe/calling-unsafe-functions.md:3 msgid "" -"Sometimes we want to allow any type of error to be returned without writing " -"our own enum covering all the different possibilities. `std::error::Error` " -"makes this easy." +"A function or method can be marked `unsafe` if it has extra preconditions " +"you must uphold to avoid undefined behaviour:" msgstr "" -"有时,我们需要允许返回任意类型的错误,但又不想自己手动编写枚举来涵盖所有不同" -"的可能性。 `std::error::Error` 可以让我们轻松做到这一点。" +"如果函数或方法具有额外的前提条件,您必须遵守这些前提条件来避免未定义的行为, " +"则可以将该函数或方法标记为 `unsafe`:" -#: src/error-handling/dynamic-errors.md:6 +#: src/unsafe/calling-unsafe-functions.md:6 msgid "" -"```rust,editable,compile_fail\n" -"use std::fs::{self, File};\n" -"use std::io::Read;\n" -"use thiserror::Error;\n" -"use std::error::Error;\n" -"\n" -"#[derive(Clone, Debug, Eq, Error, PartialEq)]\n" -"#[error(\"Found no username in {0}\")]\n" -"struct EmptyUsernameError(String);\n" +"```rust,editable\n" +"fn main() {\n" +" let emojis = \"🗻∈🌏\";\n" "\n" -"fn read_username(path: &str) -> Result> {\n" -" let mut username = String::with_capacity(100);\n" -" File::open(path)?.read_to_string(&mut username)?;\n" -" if username.is_empty() {\n" -" return Err(EmptyUsernameError(String::from(path)).into());\n" +" // Safe because the indices are in the correct order, within the bounds " +"of\n" +" // the string slice, and lie on UTF-8 sequence boundaries.\n" +" unsafe {\n" +" println!(\"emoji: {}\", emojis.get_unchecked(0..4));\n" +" println!(\"emoji: {}\", emojis.get_unchecked(4..7));\n" +" println!(\"emoji: {}\", emojis.get_unchecked(7..11));\n" " }\n" -" Ok(username)\n" +"\n" +" println!(\"char count: {}\", count_chars(unsafe { emojis." +"get_unchecked(0..7) }));\n" +"\n" +" // Not upholding the UTF-8 encoding requirement breaks memory safety!\n" +" // println!(\"emoji: {}\", unsafe { emojis.get_unchecked(0..3) });\n" +" // println!(\"char count: {}\", count_chars(unsafe { emojis." +"get_unchecked(0..3) }));\n" +"}\n" +"\n" +"fn count_chars(s: &str) -> usize {\n" +" s.chars().map(|_| 1).sum()\n" +"}\n" +"```" +msgstr "" + +#: src/unsafe/writing-unsafe-functions.md:3 +msgid "" +"You can mark your own functions as `unsafe` if they require particular " +"conditions to avoid undefined behaviour." +msgstr "" +"如果您自己编写的函数需要满足特定条件以避免未定义的行为, 您可以将这些函数标记" +"为 `unsafe`。" + +#: src/unsafe/writing-unsafe-functions.md:6 +msgid "" +"```rust,editable\n" +"/// Swaps the values pointed to by the given pointers.\n" +"///\n" +"/// # Safety\n" +"///\n" +"/// The pointers must be valid and properly aligned.\n" +"unsafe fn swap(a: *mut u8, b: *mut u8) {\n" +" let temp = *a;\n" +" *a = *b;\n" +" *b = temp;\n" "}\n" "\n" "fn main() {\n" -" //fs::write(\"config.dat\", \"\").unwrap();\n" -" match read_username(\"config.dat\") {\n" -" Ok(username) => println!(\"Username: {username}\"),\n" -" Err(err) => println!(\"Error: {err}\"),\n" +" let mut a = 42;\n" +" let mut b = 66;\n" +"\n" +" // Safe because ...\n" +" unsafe {\n" +" swap(&mut a, &mut b);\n" " }\n" +"\n" +" println!(\"a = {}, b = {}\", a, b);\n" "}\n" "```" msgstr "" -#: src/error-handling/dynamic-errors.md:36 +#: src/unsafe/writing-unsafe-functions.md:33 msgid "" -"This saves on code, but gives up the ability to cleanly handle different " -"error cases differently in the program. As such it's generally not a good " -"idea to use `Box` in the public API of a library, but it can be a " -"good option in a program where you just want to display the error message " -"somewhere." +"We wouldn't actually use pointers for this because it can be done safely " +"with references." +msgstr "实际上,我们不会这样使用指针,因为使用引用可以安全地达到相同的目的。" + +#: src/unsafe/writing-unsafe-functions.md:35 +msgid "" +"Note that unsafe code is allowed within an unsafe function without an " +"`unsafe` block. We can prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. " +"Try adding it and see what happens." msgstr "" -"虽然这可以省却编写代码的麻烦,但也会导致我们无法在程序中以不同的方式正常处理" -"不同的 错误情况。因此,在库的公共 API 中使用 `Box` 通常不是一个好" -"主意。 但是对于您只需要在某处显示错误消息的程序来说,这不失为一个 很好的选" -"择。" +"请注意,在不安全函数中,可以在没有 `unsafe` 代码块的情况下使用不安全代码。我" +"们可以 使用 `#[deny(unsafe_op_in_unsafe_fn)]` 来禁止此行为。请尝试添加该命" +"令,看看会出现什么情况。" -#: src/error-handling/error-contexts.md:3 +#: src/unsafe/extern-functions.md:1 +msgid "Calling External Code" +msgstr "调用外部代码" + +#: src/unsafe/extern-functions.md:3 msgid "" -"The widely used [anyhow](https://docs.rs/anyhow/) crate can help you add " -"contextual information to your errors and allows you to have fewer custom " -"error types:" +"Functions from other languages might violate the guarantees of Rust. Calling " +"them is thus unsafe:" msgstr "" -"广泛使用的 [anyhow](https://docs.rs/anyhow/) crate 可以帮助我们为错误添加 背" -"景信息,并减少自定义错误类型的 数量。" +"基于其他语言的函数可能会违反 Rust 的保证。因此, 调用这类函数是不安全的:" -#: src/error-handling/error-contexts.md:7 +#: src/unsafe/extern-functions.md:6 msgid "" -"```rust,editable,compile_fail\n" -"use std::{fs, io};\n" -"use std::io::Read;\n" -"use anyhow::{Context, Result, bail};\n" -"\n" -"fn read_username(path: &str) -> Result {\n" -" let mut username = String::with_capacity(100);\n" -" fs::File::open(path)\n" -" .with_context(|| format!(\"Failed to open {path}\"))?\n" -" .read_to_string(&mut username)\n" -" .context(\"Failed to read\")?;\n" -" if username.is_empty() {\n" -" bail!(\"Found no username in {path}\");\n" -" }\n" -" Ok(username)\n" +"```rust,editable\n" +"extern \"C\" {\n" +" fn abs(input: i32) -> i32;\n" "}\n" "\n" "fn main() {\n" -" //fs::write(\"config.dat\", \"\").unwrap();\n" -" match read_username(\"config.dat\") {\n" -" Ok(username) => println!(\"Username: {username}\"),\n" -" Err(err) => println!(\"Error: {err:?}\"),\n" +" unsafe {\n" +" // Undefined behavior if abs misbehaves.\n" +" println!(\"Absolute value of -3 according to C: {}\", abs(-3));\n" " }\n" "}\n" "```" msgstr "" -#: src/error-handling/error-contexts.md:35 -#, fuzzy -msgid "`anyhow::Result` is a type alias for `Result`." -msgstr "“anyhow::Result" - -#: src/error-handling/error-contexts.md:36 -#, fuzzy +#: src/unsafe/extern-functions.md:21 msgid "" -"`anyhow::Error` is essentially a wrapper around `Box`. As such " -"it's again generally not a good choice for the public API of a library, but " -"is widely used in applications." -msgstr "”是“Result\\”的类型别名。" +"This is usually only a problem for extern functions which do things with " +"pointers which might violate Rust's memory model, but in general any C " +"function might have undefined behaviour under any arbitrary circumstances." +msgstr "" +"这个问题通常仅存在于使用指针执行违反 Rust 内存模型的操作的外部函数中。 但一般" +"而言,任何 C 函数都有可能在任意情况下出现未定义行为。" -#: src/error-handling/error-contexts.md:38 -#, fuzzy +#: src/unsafe/extern-functions.md:25 msgid "" -"Actual error type inside of it can be extracted for examination if necessary." -msgstr "“anyhow::Error”本质上是“Box" +"The `\"C\"` in this example is the ABI; [other ABIs are available too]" +"(https://doc.rust-lang.org/reference/items/external-blocks.html)." +msgstr "" -#: src/error-handling/error-contexts.md:39 -#, fuzzy +#: src/unsafe/unsafe-traits.md:3 msgid "" -"Functionality provided by `anyhow::Result` may be familiar to Go " -"developers, as it provides similar usage patterns and ergonomics to `(T, " -"error)` from Go." +"Like with functions, you can mark a trait as `unsafe` if the implementation " +"must guarantee particular conditions to avoid undefined behaviour." msgstr "" -"”的封装容器。因此,就像前面提到的那样,在库的公共 API 中 使用它通常不是一个好" -"主意。但是它广泛用于应用中。\n" -"\n" -"如果需要,可以提取其内部的实际错误类型进行检查。\n" -"\n" -"Go 开发者可能会十分熟悉 `anyhow::Result` 提供的功能, 因为它的使用模式和工" -"效学设计与 Go 的 `(T, error)` 方法十分相似。" - -#: src/testing.md:3 -msgid "Rust and Cargo come with a simple unit test framework:" -msgstr "Rust 和 Cargo 随附了一个简单的单元测试框架:" - -#: src/testing.md:5 -msgid "Unit tests are supported throughout your code." -msgstr "单元测试在您的整个代码中都受支持。" - -#: src/testing.md:7 -msgid "Integration tests are supported via the `tests/` directory." -msgstr "您可以通过 `tests/` 目录来支持集成测试。" +"与函数一样,如果您在实现某个 trait 时必须保证特定条件来避免未定义的行为, 您" +"也可以将该 trait 标记为 `unsafe`。" -#: src/testing/unit-tests.md:3 -msgid "Mark unit tests with `#[test]`:" -msgstr "使用 `#[test]` 标记单元测试:" +#: src/unsafe/unsafe-traits.md:6 +msgid "" +"For example, the `zerocopy` crate has an unsafe trait that looks [something " +"like this](https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html):" +msgstr "" +"例如,`zerocopy` crate 包含一个不安全的 trait, [大致内容是这样的](https://" +"docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html):" -#: src/testing/unit-tests.md:5 +#: src/unsafe/unsafe-traits.md:9 msgid "" -"```rust,editable,ignore\n" -"fn first_word(text: &str) -> &str {\n" -" match text.find(' ') {\n" -" Some(idx) => &text[..idx],\n" -" None => &text,\n" -" }\n" -"}\n" -"\n" -"#[test]\n" -"fn test_empty() {\n" -" assert_eq!(first_word(\"\"), \"\");\n" -"}\n" +"```rust,editable\n" +"use std::mem::size_of_val;\n" +"use std::slice;\n" "\n" -"#[test]\n" -"fn test_single_word() {\n" -" assert_eq!(first_word(\"Hello\"), \"Hello\");\n" +"/// ...\n" +"/// # Safety\n" +"/// The type must have a defined representation and no padding.\n" +"pub unsafe trait AsBytes {\n" +" fn as_bytes(&self) -> &[u8] {\n" +" unsafe {\n" +" slice::from_raw_parts(self as *const Self as *const u8, " +"size_of_val(self))\n" +" }\n" +" }\n" "}\n" "\n" -"#[test]\n" -"fn test_multiple_words() {\n" -" assert_eq!(first_word(\"Hello World\"), \"Hello\");\n" -"}\n" +"// Safe because u32 has a defined representation and no padding.\n" +"unsafe impl AsBytes for u32 {}\n" "```" msgstr "" -#: src/testing/unit-tests.md:29 -msgid "Use `cargo test` to find and run the unit tests." -msgstr "使用 `cargo test` 查找并运行单元测试。" - -#: src/testing/test-modules.md:3 +#: src/unsafe/unsafe-traits.md:30 msgid "" -"Unit tests are often put in a nested module (run tests on the [Playground]" -"(https://play.rust-lang.org/)):" +"There should be a `# Safety` section on the Rustdoc for the trait explaining " +"the requirements for the trait to be safely implemented." msgstr "" -"单元测试通常会放在嵌套模块中(在 [Playground](https://play.rust-lang.org/) 上" -"运行测试):" +"在 Rustdoc 中有关 trait 的章节下,有一个标题为 `# 安全` 的部分介绍了 安全实" +"现 trait 的要求。" -#: src/testing/test-modules.md:6 +#: src/unsafe/unsafe-traits.md:33 msgid "" -"```rust,editable\n" -"fn helper(a: &str, b: &str) -> String {\n" -" format!(\"{a} {b}\")\n" -"}\n" -"\n" -"pub fn main() {\n" -" println!(\"{}\", helper(\"Hello\", \"World\"));\n" -"}\n" -"\n" -"#[cfg(test)]\n" -"mod tests {\n" -" use super::*;\n" -"\n" -" #[test]\n" -" fn test_helper() {\n" -" assert_eq!(helper(\"foo\", \"bar\"), \"foo bar\");\n" -" }\n" -"}\n" -"```" -msgstr "" +"The actual safety section for `AsBytes` is rather longer and more " +"complicated." +msgstr "实际上,与 `AsBytes` 相关的安全说明远比这里展示的更详尽、更复杂。" -#: src/testing/test-modules.md:26 -msgid "This lets you unit test private helpers." -msgstr "这样一来,您可以对专用帮助程序进行单元测试。" +#: src/unsafe/unsafe-traits.md:35 +msgid "The built-in `Send` and `Sync` traits are unsafe." +msgstr "内置的 `Send` 和 `Sync` trait 都是不安全的。" -#: src/testing/test-modules.md:27 -msgid "The `#[cfg(test)]` attribute is only active when you run `cargo test`." -msgstr "仅当您运行 `cargo test` 时,`#[cfg(test)]` 属性才有效。" +#: src/exercises/day-3/afternoon.md:1 +msgid "Day 3: Afternoon Exercises" +msgstr "" -#: src/testing/doc-tests.md:3 -msgid "Rust has built-in support for documentation tests:" -msgstr "Rust 本身就支持文档测试:" +#: src/exercises/day-3/afternoon.md:3 +msgid "Let us build a safe wrapper for reading directory content!" +msgstr "" -#: src/testing/doc-tests.md:5 +#: src/exercises/day-3/afternoon.md:5 msgid "" -"```rust\n" -"/// Shortens a string to the given length.\n" -"///\n" -"/// ```\n" -"/// use playground::shorten_string;\n" -"/// assert_eq!(shorten_string(\"Hello World\", 5), \"Hello\");\n" -"/// assert_eq!(shorten_string(\"Hello World\", 20), \"Hello World\");\n" -"/// ```\n" -"pub fn shorten_string(s: &str, length: usize) -> &str {\n" -" &s[..std::cmp::min(length, s.len())]\n" -"}\n" -"```" +"For this exercise, we suggest using a local dev environment instead of the " +"Playground. This will allow you to run your binary on your own machine." msgstr "" -#: src/testing/doc-tests.md:18 -msgid "Code blocks in `///` comments are automatically seen as Rust code." -msgstr "`///` 注释中的代码块会自动被视为 Rust 代码。" - -#: src/testing/doc-tests.md:19 -msgid "The code will be compiled and executed as part of `cargo test`." -msgstr "代码会作为 `cargo test` 的一部分进行编译和执行。" - -#: src/testing/doc-tests.md:20 +#: src/exercises/day-3/afternoon.md:8 msgid "" -"Test the above code on the [Rust Playground](https://play.rust-lang.org/?" -"version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0)." +"To get started, follow the [running locally](../../cargo/running-locally.md) " +"instructions." msgstr "" -"在 [Rust Playground](https://play.rust-lang.org/?" -"version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0) " -"上测试上述代码。" - -#: src/testing/integration-tests.md:3 -msgid "If you want to test your library as a client, use an integration test." -msgstr "如果您想要以客户的身份测试您的库,请使用集成测试。" -#: src/testing/integration-tests.md:5 -msgid "Create a `.rs` file under `tests/`:" -msgstr "在 `tests/` 下方创建一个 `.rs` 文件:" +#: src/exercises/day-3/afternoon.md:14 +msgid "" +"After looking at the exercise, you can look at the [solution](solutions-" +"afternoon.md) provided." +msgstr "" -#: src/testing/integration-tests.md:7 +#: src/exercises/day-3/safe-ffi-wrapper.md:3 msgid "" -"```rust,ignore\n" -"use my_library::init;\n" -"\n" -"#[test]\n" -"fn test_init() {\n" -" assert!(init().is_ok());\n" -"}\n" -"```" +"Rust has great support for calling functions through a _foreign function " +"interface_ (FFI). We will use this to build a safe wrapper for the `libc` " +"functions you would use from C to read the filenames of a directory." msgstr "" -#: src/testing/integration-tests.md:16 -msgid "These tests only have access to the public API of your crate." -msgstr "这些测试只能使用您的 crate 的公共 API。" +#: src/exercises/day-3/safe-ffi-wrapper.md:7 +msgid "You will want to consult the manual pages:" +msgstr "" -#: src/testing/useful-crates.md:1 -msgid "Useful crates for writing tests" -msgstr "用于编写测试的实用 crate" +#: src/exercises/day-3/safe-ffi-wrapper.md:9 +msgid "[`opendir(3)`](https://man7.org/linux/man-pages/man3/opendir.3.html)" +msgstr "" -#: src/testing/useful-crates.md:3 -msgid "Rust comes with only basic support for writing tests." -msgstr "Rust 仅为编写测试提供基本支持。" +#: src/exercises/day-3/safe-ffi-wrapper.md:10 +msgid "[`readdir(3)`](https://man7.org/linux/man-pages/man3/readdir.3.html)" +msgstr "" -#: src/testing/useful-crates.md:5 -msgid "Here are some additional crates which we recommend for writing tests:" -msgstr "下面列出了我们建议在编写测试时使用的一些其他 crate:" +#: src/exercises/day-3/safe-ffi-wrapper.md:11 +msgid "[`closedir(3)`](https://man7.org/linux/man-pages/man3/closedir.3.html)" +msgstr "" -#: src/testing/useful-crates.md:7 +#: src/exercises/day-3/safe-ffi-wrapper.md:13 msgid "" -"[googletest](https://docs.rs/googletest): Comprehensive test assertion " -"library in the tradition of GoogleTest for C++." +"You will also want to browse the [`std::ffi`](https://doc.rust-lang.org/std/" +"ffi/) module. There you find a number of string types which you need for the " +"exercise:" msgstr "" -"[googletest](https://docs.rs/googletest):遵从 GoogleTest for C++ 传统的综合" -"测试断言库。" -#: src/testing/useful-crates.md:8 -msgid "[proptest](https://docs.rs/proptest): Property-based testing for Rust." -msgstr "[proptest](https://docs.rs/proptest):基于属性的测试,适用于 Rust。" +#: src/exercises/day-3/safe-ffi-wrapper.md:16 +msgid "Encoding" +msgstr "" -#: src/testing/useful-crates.md:9 +#: src/exercises/day-3/safe-ffi-wrapper.md:16 +msgid "Use" +msgstr "" + +#: src/exercises/day-3/safe-ffi-wrapper.md:18 msgid "" -"[rstest](https://docs.rs/rstest): Support for fixtures and parameterised " -"tests." -msgstr "[rstest](https://docs.rs/rstest):支持固件和参数化测试。" +"[`str`](https://doc.rust-lang.org/std/primitive.str.html) and [`String`]" +"(https://doc.rust-lang.org/std/string/struct.String.html)" +msgstr "" -#: src/unsafe.md:3 -msgid "The Rust language has two parts:" -msgstr "Rust 语言包含两个部分:" +#: src/exercises/day-3/safe-ffi-wrapper.md:18 +msgid "UTF-8" +msgstr "" -#: src/unsafe.md:5 -msgid "**Safe Rust:** memory safe, no undefined behavior possible." -msgstr "\\*\\*安全 Rust:\\*\\*内存安全,没有潜在的未定义行为。" +#: src/exercises/day-3/safe-ffi-wrapper.md:18 +msgid "Text processing in Rust" +msgstr "" -#: src/unsafe.md:6 +#: src/exercises/day-3/safe-ffi-wrapper.md:19 msgid "" -"**Unsafe Rust:** can trigger undefined behavior if preconditions are " -"violated." -msgstr "\\*\\*不安全 Rust:\\*\\*如果违反了前提条件,可能会触发未定义的行为。" +"[`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html) and [`CString`]" +"(https://doc.rust-lang.org/std/ffi/struct.CString.html)" +msgstr "" -#: src/unsafe.md:8 -msgid "" -"We will be seeing mostly safe Rust in this course, but it's important to " -"know what Unsafe Rust is." +#: src/exercises/day-3/safe-ffi-wrapper.md:19 +msgid "NUL-terminated" msgstr "" -"本课程中出现的大多为“安全 Rust”,但是了解“不安全 Rust”的定义 非常重要。" -#: src/unsafe.md:11 -msgid "" -"Unsafe code is usually small and isolated, and its correctness should be " -"carefully documented. It is usually wrapped in a safe abstraction layer." -msgstr "" -"不安全的代码通常内容很少而且与其他代码隔离, 其正确性也应得到仔细记录。这类代" -"码通常封装在安全的抽象层中。" - -#: src/unsafe.md:14 -msgid "Unsafe Rust gives you access to five new capabilities:" -msgstr "不安全 Rust 提供了五种新功能:" - -#: src/unsafe.md:16 -msgid "Dereference raw pointers." -msgstr "解引用原始指针。" - -#: src/unsafe.md:17 -msgid "Access or modify mutable static variables." -msgstr "访问或修改可变的静态变量。" - -#: src/unsafe.md:18 -msgid "Access `union` fields." -msgstr "访问 `union` 字段。" - -#: src/unsafe.md:19 -msgid "Call `unsafe` functions, including `extern` functions." -msgstr "调用 `unsafe` 函数,包括 `extern` 函数。" - -#: src/unsafe.md:20 -msgid "Implement `unsafe` traits." -msgstr "实现 `unsafe` trait。" - -#: src/unsafe.md:22 -msgid "" -"We will briefly cover unsafe capabilities next. For full details, please see " -"[Chapter 19.1 in the Rust Book](https://doc.rust-lang.org/book/ch19-01-" -"unsafe-rust.html) and the [Rustonomicon](https://doc.rust-lang.org/nomicon/)." +#: src/exercises/day-3/safe-ffi-wrapper.md:19 +msgid "Communicating with C functions" msgstr "" -"下面,我们将简要介绍这些不安全功能。如需了解完整详情,请参阅 [《Rust 手册》" -"第 19.1 章](https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html) 和 " -"[Rustonomicon](https://doc.rust-lang.org/nomicon/)。" -#: src/unsafe.md:28 +#: src/exercises/day-3/safe-ffi-wrapper.md:20 msgid "" -"Unsafe Rust does not mean the code is incorrect. It means that developers " -"have turned off the compiler safety features and have to write correct code " -"by themselves. It means the compiler no longer enforces Rust's memory-safety " -"rules." +"[`OsStr`](https://doc.rust-lang.org/std/ffi/struct.OsStr.html) and " +"[`OsString`](https://doc.rust-lang.org/std/ffi/struct.OsString.html)" msgstr "" -"不安全 Rust 并不意味着代码不正确,而是这意味着开发者已停用 编译器的安全功能," -"必须自行编写正确的 代码。也就是说,编译器不再强制执行 Rust 的内存安全规则。" - -#: src/unsafe/raw-pointers.md:3 -msgid "Creating pointers is safe, but dereferencing them requires `unsafe`:" -msgstr "创建指针是安全的操作,但解引用指针需要使用 `unsafe` 方法:" -#: src/unsafe/raw-pointers.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut num = 5;\n" -"\n" -" let r1 = &mut num as *mut i32;\n" -" let r2 = r1 as *const i32;\n" -"\n" -" // Safe because r1 and r2 were obtained from references and so are\n" -" // guaranteed to be non-null and properly aligned, the objects " -"underlying\n" -" // the references from which they were obtained are live throughout the\n" -" // whole unsafe block, and they are not accessed either through the\n" -" // references or concurrently through any other pointers.\n" -" unsafe {\n" -" println!(\"r1 is: {}\", *r1);\n" -" *r1 = 10;\n" -" println!(\"r2 is: {}\", *r2);\n" -" }\n" -"}\n" -"```" +#: src/exercises/day-3/safe-ffi-wrapper.md:20 +msgid "OS-specific" msgstr "" -#: src/unsafe/raw-pointers.md:27 -msgid "" -"It is good practice (and required by the Android Rust style guide) to write " -"a comment for each `unsafe` block explaining how the code inside it " -"satisfies the safety requirements of the unsafe operations it is doing." +#: src/exercises/day-3/safe-ffi-wrapper.md:20 +msgid "Communicating with the OS" msgstr "" -"我们建议(而且 Android Rust 样式指南要求)为每个 `unsafe` 代码块编写一条注" -"释, 说明该代码块中的代码如何满足其所执行的不安全操作的 安全要求。" -#: src/unsafe/raw-pointers.md:31 -msgid "" -"In the case of pointer dereferences, this means that the pointers must be " -"[_valid_](https://doc.rust-lang.org/std/ptr/index.html#safety), i.e.:" +#: src/exercises/day-3/safe-ffi-wrapper.md:22 +msgid "You will convert between all these types:" msgstr "" -"对于指针解除引用,这意味着指针必须为 [_valid_](https://doc.rust-lang.org/std/" -"ptr/index.html#safety),即:" -#: src/unsafe/raw-pointers.md:34 -msgid "The pointer must be non-null." -msgstr "指针必须为非 null。" - -#: src/unsafe/raw-pointers.md:35 -msgid "" -"The pointer must be _dereferenceable_ (within the bounds of a single " -"allocated object)." -msgstr "指针必须是 _dereferenceable_(在单个已分配对象的边界内)。" - -#: src/unsafe/raw-pointers.md:36 -msgid "The object must not have been deallocated." -msgstr "对象不得已取消分配。" - -#: src/unsafe/raw-pointers.md:37 -msgid "There must not be concurrent accesses to the same location." -msgstr "不得并发访问相同位置。" - -#: src/unsafe/raw-pointers.md:38 +#: src/exercises/day-3/safe-ffi-wrapper.md:24 msgid "" -"If the pointer was obtained by casting a reference, the underlying object " -"must be live and no reference may be used to access the memory." +"`&str` to `CString`: you need to allocate space for a trailing `\\0` " +"character," msgstr "" -"如果通过转换引用类型来获取指针,则底层对象必须处于活跃状态, 而且不得使用任何" -"引用来访问内存。" - -#: src/unsafe/raw-pointers.md:41 -msgid "In most cases the pointer must also be properly aligned." -msgstr "在大多数情况下,指针还必须正确对齐。" - -#: src/unsafe/mutable-static-variables.md:3 -msgid "It is safe to read an immutable static variable:" -msgstr "读取不可变的静态变量是安全的操作:" -#: src/unsafe/mutable-static-variables.md:5 -msgid "" -"```rust,editable\n" -"static HELLO_WORLD: &str = \"Hello, world!\";\n" -"\n" -"fn main() {\n" -" println!(\"HELLO_WORLD: {HELLO_WORLD}\");\n" -"}\n" -"```" +#: src/exercises/day-3/safe-ffi-wrapper.md:25 +msgid "`CString` to `*const i8`: you need a pointer to call C functions," msgstr "" -#: src/unsafe/mutable-static-variables.md:13 -msgid "" -"However, since data races can occur, it is unsafe to read and write mutable " -"static variables:" -msgstr "但是,读取和写入可变的静态变量是不安全的,因为这可能会 造成数据争用:" - -#: src/unsafe/mutable-static-variables.md:16 +#: src/exercises/day-3/safe-ffi-wrapper.md:26 msgid "" -"```rust,editable\n" -"static mut COUNTER: u32 = 0;\n" -"\n" -"fn add_to_counter(inc: u32) {\n" -" unsafe { COUNTER += inc; } // Potential data race!\n" -"}\n" -"\n" -"fn main() {\n" -" add_to_counter(42);\n" -"\n" -" unsafe { println!(\"COUNTER: {COUNTER}\"); } // Potential data race!\n" -"}\n" -"```" +"`*const i8` to `&CStr`: you need something which can find the trailing `\\0` " +"character," msgstr "" -#: src/unsafe/mutable-static-variables.md:32 +#: src/exercises/day-3/safe-ffi-wrapper.md:27 msgid "" -"Using a mutable static is generally a bad idea, but there are some cases " -"where it might make sense in low-level `no_std` code, such as implementing a " -"heap allocator or working with some C APIs." +"`&CStr` to `&[u8]`: a slice of bytes is the universal interface for \"some " +"unknow data\"," msgstr "" -"通常,我们不建议使用可变的静态变量,但在某些情况下,在低层级 `no_std` 代码中" -"可能需要这样做, 例如实现堆分配器或使用某些 C API。" - -#: src/unsafe/unions.md:3 -msgid "Unions are like enums, but you need to track the active field yourself:" -msgstr "联合体与枚举类似,但您需要自行跟踪活跃字段:" -#: src/unsafe/unions.md:5 +#: src/exercises/day-3/safe-ffi-wrapper.md:28 msgid "" -"```rust,editable\n" -"#[repr(C)]\n" -"union MyUnion {\n" -" i: u8,\n" -" b: bool,\n" -"}\n" -"\n" -"fn main() {\n" -" let u = MyUnion { i: 42 };\n" -" println!(\"int: {}\", unsafe { u.i });\n" -" println!(\"bool: {}\", unsafe { u.b }); // Undefined behavior!\n" -"}\n" -"```" +"`&[u8]` to `&OsStr`: `&OsStr` is a step towards `OsString`, use [`OsStrExt`]" +"(https://doc.rust-lang.org/std/os/unix/ffi/trait.OsStrExt.html) to create it," msgstr "" -#: src/unsafe/unions.md:21 +#: src/exercises/day-3/safe-ffi-wrapper.md:31 msgid "" -"Unions are very rarely needed in Rust as you can usually use an enum. They " -"are occasionally needed for interacting with C library APIs." +"`&OsStr` to `OsString`: you need to clone the data in `&OsStr` to be able to " +"return it and call `readdir` again." msgstr "" -"在 Rust 中很少需要用到联合体,因为您通常可以使用枚举。联合体只是偶尔用于 与 " -"C 库 API 进行交互。" -#: src/unsafe/unions.md:24 +#: src/exercises/day-3/safe-ffi-wrapper.md:34 msgid "" -"If you just want to reinterpret bytes as a different type, you probably want " -"[`std::mem::transmute`](https://doc.rust-lang.org/stable/std/mem/fn." -"transmute.html) or a safe wrapper such as the [`zerocopy`](https://crates.io/" -"crates/zerocopy) crate." +"The [Nomicon](https://doc.rust-lang.org/nomicon/ffi.html) also has a very " +"useful chapter about FFI." msgstr "" -"如果您只是想将字节重新解释为其他类型,则可能需要使用 [`std::mem::transmute`]" -"(https://doc.rust-lang.org/stable/std/mem/fn.transmute.html) 或 安全的封装容" -"器,例如 [`zerocopy`](https://crates.io/crates/zerocopy) crate。" -#: src/unsafe/calling-unsafe-functions.md:3 +#: src/exercises/day-3/safe-ffi-wrapper.md:45 msgid "" -"A function or method can be marked `unsafe` if it has extra preconditions " -"you must uphold to avoid undefined behaviour:" +"Copy the code below to and fill in the missing " +"functions and methods:" msgstr "" -"如果函数或方法具有额外的前提条件,您必须遵守这些前提条件来避免未定义的行为, " -"则可以将该函数或方法标记为 `unsafe`:" -#: src/unsafe/calling-unsafe-functions.md:6 +#: src/exercises/day-3/safe-ffi-wrapper.md:48 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let emojis = \"🗻∈🌏\";\n" +"```rust,should_panic\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_imports, unused_variables, dead_code)]\n" "\n" -" // Safe because the indices are in the correct order, within the bounds " -"of\n" -" // the string slice, and lie on UTF-8 sequence boundaries.\n" -" unsafe {\n" -" println!(\"emoji: {}\", emojis.get_unchecked(0..4));\n" -" println!(\"emoji: {}\", emojis.get_unchecked(4..7));\n" -" println!(\"emoji: {}\", emojis.get_unchecked(7..11));\n" +"mod ffi {\n" +" use std::os::raw::{c_char, c_int};\n" +" #[cfg(not(target_os = \"macos\"))]\n" +" use std::os::raw::{c_long, c_ulong, c_ushort, c_uchar};\n" +"\n" +" // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.\n" +" #[repr(C)]\n" +" pub struct DIR {\n" +" _data: [u8; 0],\n" +" _marker: core::marker::PhantomData<(*mut u8, core::marker::" +"PhantomPinned)>,\n" " }\n" "\n" -" println!(\"char count: {}\", count_chars(unsafe { emojis." -"get_unchecked(0..7) }));\n" +" // Layout according to the Linux man page for readdir(3), where ino_t " +"and\n" +" // off_t are resolved according to the definitions in\n" +" // /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h}.\n" +" #[cfg(not(target_os = \"macos\"))]\n" +" #[repr(C)]\n" +" pub struct dirent {\n" +" pub d_ino: c_ulong,\n" +" pub d_off: c_long,\n" +" pub d_reclen: c_ushort,\n" +" pub d_type: c_uchar,\n" +" pub d_name: [c_char; 256],\n" +" }\n" "\n" -" // Not upholding the UTF-8 encoding requirement breaks memory safety!\n" -" // println!(\"emoji: {}\", unsafe { emojis.get_unchecked(0..3) });\n" -" // println!(\"char count: {}\", count_chars(unsafe { emojis." -"get_unchecked(0..3) }));\n" +" // Layout according to the macOS man page for dir(5).\n" +" #[cfg(all(target_os = \"macos\"))]\n" +" #[repr(C)]\n" +" pub struct dirent {\n" +" pub d_fileno: u64,\n" +" pub d_seekoff: u64,\n" +" pub d_reclen: u16,\n" +" pub d_namlen: u16,\n" +" pub d_type: u8,\n" +" pub d_name: [c_char; 1024],\n" +" }\n" +"\n" +" extern \"C\" {\n" +" pub fn opendir(s: *const c_char) -> *mut DIR;\n" +"\n" +" #[cfg(not(all(target_os = \"macos\", target_arch = \"x86_64\")))]\n" +" pub fn readdir(s: *mut DIR) -> *const dirent;\n" +"\n" +" // See https://github.com/rust-lang/libc/issues/414 and the section " +"on\n" +" // _DARWIN_FEATURE_64_BIT_INODE in the macOS man page for stat(2).\n" +" //\n" +" // \"Platforms that existed before these updates were available\" " +"refers\n" +" // to macOS (as opposed to iOS / wearOS / etc.) on Intel and " +"PowerPC.\n" +" #[cfg(all(target_os = \"macos\", target_arch = \"x86_64\"))]\n" +" #[link_name = \"readdir$INODE64\"]\n" +" pub fn readdir(s: *mut DIR) -> *const dirent;\n" +"\n" +" pub fn closedir(s: *mut DIR) -> c_int;\n" +" }\n" "}\n" "\n" -"fn count_chars(s: &str) -> usize {\n" -" s.chars().map(|_| 1).sum()\n" +"use std::ffi::{CStr, CString, OsStr, OsString};\n" +"use std::os::unix::ffi::OsStrExt;\n" +"\n" +"#[derive(Debug)]\n" +"struct DirectoryIterator {\n" +" path: CString,\n" +" dir: *mut ffi::DIR,\n" "}\n" -"```" -msgstr "" - -#: src/unsafe/writing-unsafe-functions.md:3 -msgid "" -"You can mark your own functions as `unsafe` if they require particular " -"conditions to avoid undefined behaviour." -msgstr "" -"如果您自己编写的函数需要满足特定条件以避免未定义的行为, 您可以将这些函数标记" -"为 `unsafe`。" - -#: src/unsafe/writing-unsafe-functions.md:6 -msgid "" -"```rust,editable\n" -"/// Swaps the values pointed to by the given pointers.\n" -"///\n" -"/// # Safety\n" -"///\n" -"/// The pointers must be valid and properly aligned.\n" -"unsafe fn swap(a: *mut u8, b: *mut u8) {\n" -" let temp = *a;\n" -" *a = *b;\n" -" *b = temp;\n" +"\n" +"impl DirectoryIterator {\n" +" fn new(path: &str) -> Result {\n" +" // Call opendir and return a Ok value if that worked,\n" +" // otherwise return Err with a message.\n" +" unimplemented!()\n" +" }\n" "}\n" "\n" -"fn main() {\n" -" let mut a = 42;\n" -" let mut b = 66;\n" +"impl Iterator for DirectoryIterator {\n" +" type Item = OsString;\n" +" fn next(&mut self) -> Option {\n" +" // Keep calling readdir until we get a NULL pointer back.\n" +" unimplemented!()\n" +" }\n" +"}\n" "\n" -" // Safe because ...\n" -" unsafe {\n" -" swap(&mut a, &mut b);\n" +"impl Drop for DirectoryIterator {\n" +" fn drop(&mut self) {\n" +" // Call closedir as needed.\n" +" unimplemented!()\n" " }\n" +"}\n" "\n" -" println!(\"a = {}, b = {}\", a, b);\n" +"fn main() -> Result<(), String> {\n" +" let iter = DirectoryIterator::new(\".\")?;\n" +" println!(\"files: {:#?}\", iter.collect::>());\n" +" Ok(())\n" "}\n" "```" msgstr "" -#: src/unsafe/writing-unsafe-functions.md:33 -msgid "" -"We wouldn't actually use pointers for this because it can be done safely " -"with references." -msgstr "实际上,我们不会这样使用指针,因为使用引用可以安全地达到相同的目的。" - -#: src/unsafe/writing-unsafe-functions.md:35 -msgid "" -"Note that unsafe code is allowed within an unsafe function without an " -"`unsafe` block. We can prohibit this with `#[deny(unsafe_op_in_unsafe_fn)]`. " -"Try adding it and see what happens." -msgstr "" -"请注意,在不安全函数中,可以在没有 `unsafe` 代码块的情况下使用不安全代码。我" -"们可以 使用 `#[deny(unsafe_op_in_unsafe_fn)]` 来禁止此行为。请尝试添加该命" -"令,看看会出现什么情况。" - -#: src/unsafe/extern-functions.md:1 -msgid "Calling External Code" -msgstr "调用外部代码" +#: src/android.md:1 +msgid "Welcome to Rust in Android" +msgstr "欢迎来到Android 中的Rust" -#: src/unsafe/extern-functions.md:3 +#: src/android.md:3 msgid "" -"Functions from other languages might violate the guarantees of Rust. Calling " -"them is thus unsafe:" +"Rust is supported for native platform development on Android. This means " +"that you can write new operating system services in Rust, as well as " +"extending existing services." msgstr "" -"基于其他语言的函数可能会违反 Rust 的保证。因此, 调用这类函数是不安全的:" +"Rust 支持Android 的原生平台开发。这意味着您可以在Rust 中编写新的操作系统服" +"务,以及扩展现有服务。" -#: src/unsafe/extern-functions.md:6 +#: src/android.md:7 msgid "" -"```rust,editable\n" -"extern \"C\" {\n" -" fn abs(input: i32) -> i32;\n" -"}\n" -"\n" -"fn main() {\n" -" unsafe {\n" -" // Undefined behavior if abs misbehaves.\n" -" println!(\"Absolute value of -3 according to C: {}\", abs(-3));\n" -" }\n" -"}\n" -"```" +"We will attempt to call Rust from one of your own projects today. So try to " +"find a little corner of your code base where we can move some lines of code " +"to Rust. The fewer dependencies and \"exotic\" types the better. Something " +"that parses some raw bytes would be ideal." msgstr "" +"今天我们会尝试在你自己的项目中调用Rust。 所以试着在你的代码中找一小段来改成" +"Rust。 代码中越少依赖(dependencies),越少“独特”的类型,越好。比如 一段解析原" +"始字符的代码就很理想。" -#: src/unsafe/extern-functions.md:21 +#: src/android/setup.md:3 msgid "" -"This is usually only a problem for extern functions which do things with " -"pointers which might violate Rust's memory model, but in general any C " -"function might have undefined behaviour under any arbitrary circumstances." +"We will be using an Android Virtual Device to test our code. Make sure you " +"have access to one or create a new one with:" msgstr "" -"这个问题通常仅存在于使用指针执行违反 Rust 内存模型的操作的外部函数中。 但一般" -"而言,任何 C 函数都有可能在任意情况下出现未定义行为。" +"我们将会使用Android 虚拟设备(Android Virtual Device)来测试我们的代码。 确保" +"你有权限访问一个,或者用以下命令创建一个新的:" -#: src/unsafe/extern-functions.md:25 +#: src/android/setup.md:12 msgid "" -"The `\"C\"` in this example is the ABI; [other ABIs are available too]" -"(https://doc.rust-lang.org/reference/items/external-blocks.html)." +"Please see the [Android Developer Codelab](https://source.android.com/docs/" +"setup/start) for details." msgstr "" +"更多细节请参考 [Android Developer Codelab](https://source.android.com/docs/" +"setup/start)." -#: src/unsafe/unsafe-traits.md:3 -msgid "" -"Like with functions, you can mark a trait as `unsafe` if the implementation " -"must guarantee particular conditions to avoid undefined behaviour." -msgstr "" -"与函数一样,如果您在实现某个 trait 时必须保证特定条件来避免未定义的行为, 您" -"也可以将该 trait 标记为 `unsafe`。" +#: src/android/build-rules.md:3 +msgid "The Android build system (Soong) supports Rust via a number of modules:" +msgstr "Android 构建系统(Soong)通过一系列模块来支持Rust:" -#: src/unsafe/unsafe-traits.md:6 -msgid "" -"For example, the `zerocopy` crate has an unsafe trait that looks [something " -"like this](https://docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html):" +#: src/android/build-rules.md:5 +#, fuzzy +msgid "Module Type" msgstr "" -"例如,`zerocopy` crate 包含一个不安全的 trait, [大致内容是这样的](https://" -"docs.rs/zerocopy/latest/zerocopy/trait.AsBytes.html):" +"\\| 模块类型 | 描" +"述 " +"| \\|—————————|——————————————————————————————————————————————————| \\| " +"`rust_binary` | Rust 二进制文" +"件。 " +"| \\| `rust_library` | 生成Rust 库,并且提供 `rlib` 和 `dylib` 变" +"体。 | \\| `rust_ffi` | 生成可由 cc 模块使用的 " +"Rust C 库,并提供静态和共享变体。 | \\| `rust_proc_macro` | 生成 proc-" +"macro Rust 库。 这些宏与编译器插件类似。 | \\| " +"`rust_test` | 生成使用标准 Rust 自动化测试框架的 Rust 测试二进制文" +"件。 | \\| `rust_fuzz` | 生成使用 " +"libfuzzer 的 Rust 模糊测试二进制文" +"件。 | \\| `rust_protobuf` | 生成源" +"代码,并生成为特定 protobuf 提供接口的 Rust 库。| \\| `rust_bindgen` | 生" +"成源代码,并生成包含与 C 库的 Rust 绑定的 Rust 库。|" -#: src/unsafe/unsafe-traits.md:9 -msgid "" -"```rust,editable\n" -"use std::mem::size_of_val;\n" -"use std::slice;\n" -"\n" -"/// ...\n" -"/// # Safety\n" -"/// The type must have a defined representation and no padding.\n" -"pub unsafe trait AsBytes {\n" -" fn as_bytes(&self) -> &[u8] {\n" -" unsafe {\n" -" slice::from_raw_parts(self as *const Self as *const u8, " -"size_of_val(self))\n" -" }\n" -" }\n" -"}\n" -"\n" -"// Safe because u32 has a defined representation and no padding.\n" -"unsafe impl AsBytes for u32 {}\n" -"```" -msgstr "" +#: src/android/build-rules.md:5 +msgid "Description" +msgstr "描述" -#: src/unsafe/unsafe-traits.md:30 -msgid "" -"There should be a `# Safety` section on the Rustdoc for the trait explaining " -"the requirements for the trait to be safely implemented." -msgstr "" -"在 Rustdoc 中有关 trait 的章节下,有一个标题为 `# 安全` 的部分介绍了 安全实" -"现 trait 的要求。" +#: src/android/build-rules.md:7 +msgid "`rust_binary`" +msgstr "`rust_binary`" -#: src/unsafe/unsafe-traits.md:33 -msgid "" -"The actual safety section for `AsBytes` is rather longer and more " -"complicated." -msgstr "实际上,与 `AsBytes` 相关的安全说明远比这里展示的更详尽、更复杂。" +#: src/android/build-rules.md:7 +msgid "Produces a Rust binary." +msgstr "生成一个Rust二进制文件。" -#: src/unsafe/unsafe-traits.md:35 -msgid "The built-in `Send` and `Sync` traits are unsafe." -msgstr "内置的 `Send` 和 `Sync` trait 都是不安全的。" +#: src/android/build-rules.md:8 +msgid "`rust_library`" +msgstr "`rust_library`" -#: src/exercises/day-3/afternoon.md:1 -msgid "Day 3: Afternoon Exercises" -msgstr "" +#: src/android/build-rules.md:8 +msgid "Produces a Rust library, and provides both `rlib` and `dylib` variants." +msgstr "生成一个 Rust 库,并提供 `rlib` 和 `dylib` 两种变体。" -#: src/exercises/day-3/afternoon.md:3 -msgid "Let us build a safe wrapper for reading directory content!" -msgstr "" +#: src/android/build-rules.md:9 +msgid "`rust_ffi`" +msgstr "`rust_ffi`" -#: src/exercises/day-3/afternoon.md:7 +#: src/android/build-rules.md:9 msgid "" -"After looking at the exercise, you can look at the [solution](solutions-" -"afternoon.md) provided." -msgstr "" +"Produces a Rust C library usable by `cc` modules, and provides both static " +"and shared variants." +msgstr "生成一个可由 `cc` 模块使用的 Rust C 库,并提供静态和共享两种变体。" -#: src/exercises/day-3/safe-ffi-wrapper.md:3 -msgid "" -"Rust has great support for calling functions through a _foreign function " -"interface_ (FFI). We will use this to build a safe wrapper for the `libc` " -"functions you would use from C to read the filenames of a directory." -msgstr "" - -#: src/exercises/day-3/safe-ffi-wrapper.md:7 -msgid "You will want to consult the manual pages:" -msgstr "" +#: src/android/build-rules.md:10 +msgid "`rust_proc_macro`" +msgstr "`rust_proc_macro`" -#: src/exercises/day-3/safe-ffi-wrapper.md:9 -msgid "[`opendir(3)`](https://man7.org/linux/man-pages/man3/opendir.3.html)" +#: src/android/build-rules.md:10 +msgid "" +"Produces a `proc-macro` Rust library. These are analogous to compiler " +"plugins." msgstr "" -#: src/exercises/day-3/safe-ffi-wrapper.md:10 -msgid "[`readdir(3)`](https://man7.org/linux/man-pages/man3/readdir.3.html)" -msgstr "" +#: src/android/build-rules.md:11 +msgid "`rust_test`" +msgstr "`rust_test`" -#: src/exercises/day-3/safe-ffi-wrapper.md:11 -msgid "[`closedir(3)`](https://man7.org/linux/man-pages/man3/closedir.3.html)" -msgstr "" +#: src/android/build-rules.md:11 +msgid "Produces a Rust test binary that uses the standard Rust test harness." +msgstr "生成使用标准 Rust 测试框架的 Rust 测试二进制文件。" -#: src/exercises/day-3/safe-ffi-wrapper.md:13 -msgid "" -"You will also want to browse the [`std::ffi`](https://doc.rust-lang.org/std/" -"ffi/) module. There you find a number of string types which you need for the " -"exercise:" -msgstr "" +#: src/android/build-rules.md:12 +msgid "`rust_fuzz`" +msgstr "`rust_fuzz`" -#: src/exercises/day-3/safe-ffi-wrapper.md:16 -msgid "Encoding" +#: src/android/build-rules.md:12 +msgid "Produces a Rust fuzz binary leveraging `libfuzzer`." msgstr "" -#: src/exercises/day-3/safe-ffi-wrapper.md:16 -msgid "Use" -msgstr "" +#: src/android/build-rules.md:13 +msgid "`rust_protobuf`" +msgstr "`rust_protobuf`" -#: src/exercises/day-3/safe-ffi-wrapper.md:18 +#: src/android/build-rules.md:13 msgid "" -"[`str`](https://doc.rust-lang.org/std/primitive.str.html) and [`String`]" -"(https://doc.rust-lang.org/std/string/struct.String.html)" -msgstr "" - -#: src/exercises/day-3/safe-ffi-wrapper.md:18 -msgid "UTF-8" -msgstr "" +"Generates source and produces a Rust library that provides an interface for " +"a particular protobuf." +msgstr "生成源代码并生成为特定 protobuf 提供接口的 Rust 库。" -#: src/exercises/day-3/safe-ffi-wrapper.md:18 -msgid "Text processing in Rust" +#: src/android/build-rules.md:14 +msgid "`rust_bindgen`" msgstr "" -#: src/exercises/day-3/safe-ffi-wrapper.md:19 +#: src/android/build-rules.md:14 msgid "" -"[`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html) and [`CString`]" -"(https://doc.rust-lang.org/std/ffi/struct.CString.html)" -msgstr "" +"Generates source and produces a Rust library containing Rust bindings to C " +"libraries." +msgstr "生成源代码并生成包含 Rust 绑定到 C 库的 Rust 库。" -#: src/exercises/day-3/safe-ffi-wrapper.md:19 -msgid "NUL-terminated" -msgstr "" +#: src/android/build-rules.md:16 +msgid "We will look at `rust_binary` and `rust_library` next." +msgstr "下面我们来看看 `rust_binary` 和 `rust_library`。" -#: src/exercises/day-3/safe-ffi-wrapper.md:19 -msgid "Communicating with C functions" -msgstr "" +#: src/android/build-rules/binary.md:1 +msgid "Rust Binaries" +msgstr "Rust 二进制文件" -#: src/exercises/day-3/safe-ffi-wrapper.md:20 +#: src/android/build-rules/binary.md:3 msgid "" -"[`OsStr`](https://doc.rust-lang.org/std/ffi/struct.OsStr.html) and " -"[`OsString`](https://doc.rust-lang.org/std/ffi/struct.OsString.html)" -msgstr "" +"Let us start with a simple application. At the root of an AOSP checkout, " +"create the following files:" +msgstr "让我们从一个简单的应用程序开始。在 AOSP 签出的根目录下,创建以下文件:" -#: src/exercises/day-3/safe-ffi-wrapper.md:20 -msgid "OS-specific" -msgstr "" +#: src/android/build-rules/binary.md:6 src/android/build-rules/library.md:13 +msgid "_hello_rust/Android.bp_:" +msgstr "_hello_rust/Android.bp_:" -#: src/exercises/day-3/safe-ffi-wrapper.md:20 -msgid "Communicating with the OS" +#: src/android/build-rules/binary.md:8 +msgid "" +"```javascript\n" +"rust_binary {\n" +" name: \"hello_rust\",\n" +" crate_name: \"hello_rust\",\n" +" srcs: [\"src/main.rs\"],\n" +"}\n" +"```" msgstr "" +"```javascript\n" +"rust_binary {\n" +" name: \"hello_rust\",\n" +" crate_name: \"hello_rust\",\n" +" srcs: [\"src/main.rs\"],\n" +"}\n" +"```" -#: src/exercises/day-3/safe-ffi-wrapper.md:22 -msgid "You will convert between all these types:" -msgstr "" +#: src/android/build-rules/binary.md:16 src/android/build-rules/library.md:34 +msgid "_hello_rust/src/main.rs_:" +msgstr "_hello_rust/src/main.rs_:" -#: src/exercises/day-3/safe-ffi-wrapper.md:24 +#: src/android/build-rules/binary.md:18 msgid "" -"`&str` to `CString`: you need to allocate space for a trailing `\\0` " -"character," +"```rust\n" +"//! Rust demo.\n" +"\n" +"/// Prints a greeting to standard output.\n" +"fn main() {\n" +" println!(\"Hello from Rust!\");\n" +"}\n" +"```" msgstr "" +"```rust\n" +"//! Rust demo.\n" +"\n" +"/// Prints a greeting to standard output.\n" +"fn main() {\n" +" println!(\"Hello from Rust!\");\n" +"}\n" +"```" -#: src/exercises/day-3/safe-ffi-wrapper.md:25 -msgid "`CString` to `*const i8`: you need a pointer to call C functions," -msgstr "" +#: src/android/build-rules/binary.md:27 +msgid "You can now build, push, and run the binary:" +msgstr "你现在可以构建、推送和运行二进制文件:" -#: src/exercises/day-3/safe-ffi-wrapper.md:26 +#: src/android/build-rules/binary.md:29 +#, fuzzy msgid "" -"`*const i8` to `&CStr`: you need something which can find the trailing `\\0` " -"character," +"```shell\n" +"m hello_rust\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\"\n" +"adb shell /data/local/tmp/hello_rust\n" +"```" msgstr "" +"```shell\n" +"$ m hello_rust_logs\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp\n" +"$ adb shell /data/local/tmp/hello_rust_logs\n" +"```" -#: src/exercises/day-3/safe-ffi-wrapper.md:27 -msgid "" -"`&CStr` to `&[u8]`: a slice of bytes is the universal interface for \"some " -"unknow data\"," -msgstr "" +#: src/android/build-rules/library.md:1 +msgid "Rust Libraries" +msgstr "Rust 库" -#: src/exercises/day-3/safe-ffi-wrapper.md:28 -msgid "" -"`&[u8]` to `&OsStr`: `&OsStr` is a step towards `OsString`, use [`OsStrExt`]" -"(https://doc.rust-lang.org/std/os/unix/ffi/trait.OsStrExt.html) to create it," -msgstr "" +#: src/android/build-rules/library.md:3 +msgid "You use `rust_library` to create a new Rust library for Android." +msgstr "您可以使用 `rust_library` 为 Android 创建一个新的 Rust 库。" -#: src/exercises/day-3/safe-ffi-wrapper.md:31 -msgid "" -"`&OsStr` to `OsString`: you need to clone the data in `&OsStr` to be able to " -"return it and call `readdir` again." -msgstr "" +#: src/android/build-rules/library.md:5 +msgid "Here we declare a dependency on two libraries:" +msgstr "在这里,我们声明了对两个库的依赖:" -#: src/exercises/day-3/safe-ffi-wrapper.md:34 +#: src/android/build-rules/library.md:7 +msgid "`libgreeting`, which we define below," +msgstr "`libgreeting`, 我们在下面进行了定义," + +#: src/android/build-rules/library.md:8 msgid "" -"The [Nomicon](https://doc.rust-lang.org/nomicon/ffi.html) also has a very " -"useful chapter about FFI." +"`libtextwrap`, which is a crate already vendored in [`external/rust/crates/`]" +"(https://cs.android.com/android/platform/superproject/+/master:external/rust/" +"crates/)." msgstr "" +"`libtextwrap`, 一个已经在 [`external/rust/crates/`](https://cs.android.com/" +"android/platform/superproject/+/master:external/rust/crates/) 中提供的 " +"crate。" -#: src/exercises/day-3/safe-ffi-wrapper.md:45 +#: src/android/build-rules/library.md:15 msgid "" -"Copy the code below to and fill in the missing " -"functions and methods:" +"```javascript\n" +"rust_binary {\n" +" name: \"hello_rust_with_dep\",\n" +" crate_name: \"hello_rust_with_dep\",\n" +" srcs: [\"src/main.rs\"],\n" +" rustlibs: [\n" +" \"libgreetings\",\n" +" \"libtextwrap\",\n" +" ],\n" +" prefer_rlib: true,\n" +"}\n" +"\n" +"rust_library {\n" +" name: \"libgreetings\",\n" +" crate_name: \"greetings\",\n" +" srcs: [\"src/lib.rs\"],\n" +"}\n" +"```" msgstr "" +"```javascript\n" +"rust_binary {\n" +" name: \"hello_rust_with_dep\",\n" +" crate_name: \"hello_rust_with_dep\",\n" +" srcs: [\"src/main.rs\"],\n" +" rustlibs: [\n" +" \"libgreetings\",\n" +" \"libtextwrap\",\n" +" ],\n" +" prefer_rlib: true,\n" +"}\n" +"\n" +"rust_library {\n" +" name: \"libgreetings\",\n" +" crate_name: \"greetings\",\n" +" srcs: [\"src/lib.rs\"],\n" +"}\n" +"```" -#: src/exercises/day-3/safe-ffi-wrapper.md:48 +#: src/android/build-rules/library.md:36 msgid "" -"```rust,should_panic\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_imports, unused_variables, dead_code)]\n" +"```rust,ignore\n" +"//! Rust demo.\n" "\n" -"mod ffi {\n" -" use std::os::raw::{c_char, c_int};\n" -" #[cfg(not(target_os = \"macos\"))]\n" -" use std::os::raw::{c_long, c_ulong, c_ushort};\n" +"use greetings::greeting;\n" +"use textwrap::fill;\n" "\n" -" // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.\n" -" #[repr(C)]\n" -" pub struct DIR {\n" -" _data: [u8; 0],\n" -" _marker: core::marker::PhantomData<(*mut u8, core::marker::" -"PhantomPinned)>,\n" -" }\n" -"\n" -" // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-" -"gnu.\n" -" #[cfg(not(target_os = \"macos\"))]\n" -" #[repr(C)]\n" -" pub struct dirent {\n" -" pub d_ino: c_long,\n" -" pub d_off: c_ulong,\n" -" pub d_reclen: c_ushort,\n" -" pub d_type: c_char,\n" -" pub d_name: [c_char; 256],\n" -" }\n" -"\n" -" // Layout as per man entry for dirent\n" -" #[cfg(target_os = \"macos\")]\n" -" #[repr(C)]\n" -" pub struct dirent {\n" -" pub d_ino: u64,\n" -" pub d_seekoff: u64,\n" -" pub d_reclen: u16,\n" -" pub d_namlen: u16,\n" -" pub d_type: u8,\n" -" pub d_name: [c_char; 1024],\n" -" }\n" -"\n" -" extern \"C\" {\n" -" pub fn opendir(s: *const c_char) -> *mut DIR;\n" -" pub fn readdir(s: *mut DIR) -> *const dirent;\n" -" pub fn closedir(s: *mut DIR) -> c_int;\n" -" }\n" -"}\n" -"\n" -"use std::ffi::{CStr, CString, OsStr, OsString};\n" -"use std::os::unix::ffi::OsStrExt;\n" -"\n" -"#[derive(Debug)]\n" -"struct DirectoryIterator {\n" -" path: CString,\n" -" dir: *mut ffi::DIR,\n" -"}\n" -"\n" -"impl DirectoryIterator {\n" -" fn new(path: &str) -> Result {\n" -" // Call opendir and return a Ok value if that worked,\n" -" // otherwise return Err with a message.\n" -" unimplemented!()\n" -" }\n" -"}\n" -"\n" -"impl Iterator for DirectoryIterator {\n" -" type Item = OsString;\n" -" fn next(&mut self) -> Option {\n" -" // Keep calling readdir until we get a NULL pointer back.\n" -" unimplemented!()\n" -" }\n" +"/// Prints a greeting to standard output.\n" +"fn main() {\n" +" println!(\"{}\", fill(&greeting(\"Bob\"), 24));\n" "}\n" +"```" +msgstr "" +"```rust,ignore\n" +"//! Rust demo.\n" "\n" -"impl Drop for DirectoryIterator {\n" -" fn drop(&mut self) {\n" -" // Call closedir as needed.\n" -" unimplemented!()\n" -" }\n" -"}\n" +"use greetings::greeting;\n" +"use textwrap::fill;\n" "\n" -"fn main() -> Result<(), String> {\n" -" let iter = DirectoryIterator::new(\".\")?;\n" -" println!(\"files: {:#?}\", iter.collect::>());\n" -" Ok(())\n" +"/// Prints a greeting to standard output.\n" +"fn main() {\n" +" println!(\"{}\", fill(&greeting(\"Bob\"), 24));\n" "}\n" "```" -msgstr "" - -#: src/android.md:1 -msgid "Welcome to Rust in Android" -msgstr "欢迎来到Android 中的Rust" -#: src/android.md:3 -msgid "" -"Rust is supported for native platform development on Android. This means " -"that you can write new operating system services in Rust, as well as " -"extending existing services." -msgstr "" -"Rust 支持Android 的原生平台开发。这意味着您可以在Rust 中编写新的操作系统服" -"务,以及扩展现有服务。" +#: src/android/build-rules/library.md:48 +msgid "_hello_rust/src/lib.rs_:" +msgstr "_hello_rust/src/lib.rs_:" -#: src/android.md:7 +#: src/android/build-rules/library.md:50 msgid "" -"We will attempt to call Rust from one of your own projects today. So try to " -"find a little corner of your code base where we can move some lines of code " -"to Rust. The fewer dependencies and \"exotic\" types the better. Something " -"that parses some raw bytes would be ideal." +"```rust,ignore\n" +"//! Greeting library.\n" +"\n" +"/// Greet `name`.\n" +"pub fn greeting(name: &str) -> String {\n" +" format!(\"Hello {name}, it is very nice to meet you!\")\n" +"}\n" +"```" msgstr "" -"今天我们会尝试在你自己的项目中调用Rust。 所以试着在你的代码中找一小段来改成" -"Rust。 代码中越少依赖(dependencies),越少“独特”的类型,越好。比如 一段解析原" -"始字符的代码就很理想。" +"```rust,ignore\n" +"//! Greeting library.\n" +"\n" +"/// Greet `name`.\n" +"pub fn greeting(name: &str) -> String {\n" +" format!(\"Hello {name}, it is very nice to meet you!\")\n" +"}\n" +"```" -#: src/android/setup.md:3 -msgid "" -"We will be using an Android Virtual Device to test our code. Make sure you " -"have access to one or create a new one with:" -msgstr "" -"我们将会使用Android 虚拟设备(Android Virtual Device)来测试我们的代码。 确保" -"你有权限访问一个,或者用以下命令创建一个新的:" +#: src/android/build-rules/library.md:59 +msgid "You build, push, and run the binary like before:" +msgstr "您可以像之前一样构建、推送和运行二进制文件:" -#: src/android/setup.md:6 +#: src/android/build-rules/library.md:61 +#, fuzzy msgid "" "```shell\n" -"$ source build/envsetup.sh\n" -"$ lunch aosp_cf_x86_64_phone-userdebug\n" -"$ acloud create\n" +"m hello_rust_with_dep\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/hello_rust_with_dep\n" "```" msgstr "" "```shell\n" -"$ source build/envsetup.sh\n" -"$ lunch aosp_cf_x86_64_phone-userdebug\n" -"$ acloud create\n" +"$ m hello_rust_with_dep\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/" +"tmp\n" +"$ adb shell /data/local/tmp/hello_rust_with_dep\n" +"Hello Bob, it is very\n" +"nice to meet you!\n" "```" -#: src/android/setup.md:12 +#: src/android/aidl.md:3 msgid "" -"Please see the [Android Developer Codelab](https://source.android.com/docs/" -"setup/start) for details." -msgstr "" -"更多细节请参考 [Android Developer Codelab](https://source.android.com/docs/" -"setup/start)." - -#: src/android/build-rules.md:3 -msgid "The Android build system (Soong) supports Rust via a number of modules:" -msgstr "Android 构建系统(Soong)通过一系列模块来支持Rust:" - -#: src/android/build-rules.md:5 -#, fuzzy -msgid "Module Type" +"The [Android Interface Definition Language (AIDL)](https://developer.android." +"com/guide/components/aidl) is supported in Rust:" msgstr "" -"\\| 模块类型 | 描" -"述 " -"| \\|—————————|——————————————————————————————————————————————————| \\| " -"`rust_binary` | Rust 二进制文" -"件。 " -"| \\| `rust_library` | 生成Rust 库,并且提供 `rlib` 和 `dylib` 变" -"体。 | \\| `rust_ffi` | 生成可由 cc 模块使用的 " -"Rust C 库,并提供静态和共享变体。 | \\| `rust_proc_macro` | 生成 proc-" -"macro Rust 库。 这些宏与编译器插件类似。 | \\| " -"`rust_test` | 生成使用标准 Rust 自动化测试框架的 Rust 测试二进制文" -"件。 | \\| `rust_fuzz` | 生成使用 " -"libfuzzer 的 Rust 模糊测试二进制文" -"件。 | \\| `rust_protobuf` | 生成源" -"代码,并生成为特定 protobuf 提供接口的 Rust 库。| \\| `rust_bindgen` | 生" -"成源代码,并生成包含与 C 库的 Rust 绑定的 Rust 库。|" - -#: src/android/build-rules.md:5 -msgid "Description" -msgstr "描述" - -#: src/android/build-rules.md:7 -msgid "`rust_binary`" -msgstr "`rust_binary`" +"Rust 支持 [Android 接口定义语言 (AIDL)](https://developer.android.com/guide/" +"components/aidl):" -#: src/android/build-rules.md:7 -msgid "Produces a Rust binary." -msgstr "生成一个Rust二进制文件。" +#: src/android/aidl.md:6 +msgid "Rust code can call existing AIDL servers," +msgstr "Rust 代码可以调用现有的 AIDL 服务器," -#: src/android/build-rules.md:8 -msgid "`rust_library`" -msgstr "`rust_library`" +#: src/android/aidl.md:7 +msgid "You can create new AIDL servers in Rust." +msgstr "您可以在 Rust 中创建新的 AIDL 服务器。" -#: src/android/build-rules.md:8 -msgid "Produces a Rust library, and provides both `rlib` and `dylib` variants." -msgstr "生成一个 Rust 库,并提供 `rlib` 和 `dylib` 两种变体。" +#: src/android/aidl/interface.md:1 +msgid "AIDL Interfaces" +msgstr "AIDL 接口" -#: src/android/build-rules.md:9 -msgid "`rust_ffi`" -msgstr "`rust_ffi`" +#: src/android/aidl/interface.md:3 +msgid "You declare the API of your service using an AIDL interface:" +msgstr "您可以使用 AIDL 接口声明您的服务的 API:" -#: src/android/build-rules.md:9 -msgid "Produces a Rust C library usable by `cc` modules, and provides both static and shared variants." -msgstr "生成一个可由 `cc` 模块使用的 Rust C 库,并提供静态和共享两种变体。" +#: src/android/aidl/interface.md:5 +msgid "" +"_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_:" +msgstr "" +"_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_:" -#: src/android/build-rules.md:10 -msgid "`rust_proc_macro`" -msgstr "`rust_proc_macro`" +#: src/android/aidl/interface.md:17 +msgid "_birthday_service/aidl/Android.bp_:" +msgstr "_birthday_service/aidl/Android.bp_:" -#: src/android/build-rules.md:10 +#: src/android/aidl/interface.md:19 msgid "" -"Produces a `proc-macro` Rust library. These are analogous to compiler " -"plugins." +"```javascript\n" +"aidl_interface {\n" +" name: \"com.example.birthdayservice\",\n" +" srcs: [\"com/example/birthdayservice/*.aidl\"],\n" +" unstable: true,\n" +" backend: {\n" +" rust: { // Rust is not enabled by default\n" +" enabled: true,\n" +" },\n" +" },\n" +"}\n" +"```" msgstr "" +"```javascript\n" +"aidl_interface {\n" +" name: \"com.example.birthdayservice\",\n" +" srcs: [\"com/example/birthdayservice/*.aidl\"],\n" +" unstable: true,\n" +" backend: {\n" +" rust: { // 默认情况下不启用 Rust \n" +" enabled: true,\n" +" },\n" +" },\n" +"}\n" +"```" -#: src/android/build-rules.md:11 -msgid "`rust_test`" -msgstr "`rust_test`" +#: src/android/aidl/interface.md:32 +msgid "" +"Add `vendor_available: true` if your AIDL file is used by a binary in the " +"vendor partition." +msgstr "" +"如果供应商分区中的二进制文件使用了您的 AIDL 文件,请添加 `vendor_available: " +"true`。" -#: src/android/build-rules.md:11 -msgid "Produces a Rust test binary that uses the standard Rust test harness." -msgstr "生成使用标准 Rust 测试框架的 Rust 测试二进制文件。" +#: src/android/aidl/implementation.md:1 +msgid "Service Implementation" +msgstr "服务实现" -#: src/android/build-rules.md:12 -msgid "`rust_fuzz`" -msgstr "`rust_fuzz`" +#: src/android/aidl/implementation.md:3 +msgid "We can now implement the AIDL service:" +msgstr "我们现在可以实现AIDL服务:" -#: src/android/build-rules.md:12 -msgid "Produces a Rust fuzz binary leveraging `libfuzzer`." -msgstr "" - -#: src/android/build-rules.md:13 -msgid "`rust_protobuf`" -msgstr "`rust_protobuf`" +#: src/android/aidl/implementation.md:5 +msgid "_birthday_service/src/lib.rs_:" +msgstr "_birthday_service/src/lib.rs_:" -#: src/android/build-rules.md:13 +#: src/android/aidl/implementation.md:7 msgid "" -"Generates source and produces a Rust library that provides an interface for " -"a particular protobuf." -msgstr "生成源代码并生成为特定 protobuf 提供接口的 Rust 库。" - -#: src/android/build-rules.md:14 -msgid "`rust_bindgen`" +"```rust,ignore\n" +"//! Implementation of the `IBirthdayService` AIDL interface.\n" +"use com_example_birthdayservice::aidl::com::example::birthdayservice::" +"IBirthdayService::IBirthdayService;\n" +"use com_example_birthdayservice::binder;\n" +"\n" +"/// The `IBirthdayService` implementation.\n" +"pub struct BirthdayService;\n" +"\n" +"impl binder::Interface for BirthdayService {}\n" +"\n" +"impl IBirthdayService for BirthdayService {\n" +" fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::" +"Result {\n" +" Ok(format!(\n" +" \"Happy Birthday {name}, congratulations with the {years} years!" +"\"\n" +" ))\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust,ignore\n" +"//! 实现了 `IBirthdayService` AIDL 接口。\n" +"use com_example_birthdayservice::aidl::com::example::birthdayservice::" +"IBirthdayService::IBirthdayService;\n" +"use com_example_birthdayservice::binder;\n" +"\n" +"/// `IBirthdayService` 接口的具体实现。\n" +"pub struct BirthdayService;\n" +"\n" +"impl binder::Interface for BirthdayService {}\n" +"\n" +"impl IBirthdayService for BirthdayService {\n" +" fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::" +"Result {\n" +" Ok(format!(\n" +" \"Happy Birthday {name}, congratulations with the {years} years!" +"\"\n" +" ))\n" +" }\n" +"}\n" +"```" -#: src/android/build-rules.md:14 -msgid "" -"Generates source and produces a Rust library containing Rust bindings to C " -"libraries." -msgstr "生成源代码并生成包含 Rust 绑定到 C 库的 Rust 库。" - -#: src/android/build-rules.md:16 -msgid "We will look at `rust_binary` and `rust_library` next." -msgstr "下面我们来看看 `rust_binary` 和 `rust_library`。" - -#: src/android/build-rules/binary.md:1 -msgid "Rust Binaries" -msgstr "Rust 二进制文件" - -#: src/android/build-rules/binary.md:3 -msgid "" -"Let us start with a simple application. At the root of an AOSP checkout, " -"create the following files:" -msgstr "让我们从一个简单的应用程序开始。在 AOSP 签出的根目录下,创建以下文件:" - -#: src/android/build-rules/binary.md:6 src/android/build-rules/library.md:13 -msgid "_hello_rust/Android.bp_:" -msgstr "_hello_rust/Android.bp_:" +#: src/android/aidl/implementation.md:26 src/android/aidl/server.md:28 +#: src/android/aidl/client.md:37 +msgid "_birthday_service/Android.bp_:" +msgstr "_birthday_service/Android.bp_:" -#: src/android/build-rules/binary.md:8 +#: src/android/aidl/implementation.md:28 msgid "" "```javascript\n" -"rust_binary {\n" -" name: \"hello_rust\",\n" -" crate_name: \"hello_rust\",\n" -" srcs: [\"src/main.rs\"],\n" +"rust_library {\n" +" name: \"libbirthdayservice\",\n" +" srcs: [\"src/lib.rs\"],\n" +" crate_name: \"birthdayservice\",\n" +" rustlibs: [\n" +" \"com.example.birthdayservice-rust\",\n" +" \"libbinder_rs\",\n" +" ],\n" "}\n" "```" msgstr "" "```javascript\n" -"rust_binary {\n" -" name: \"hello_rust\",\n" -" crate_name: \"hello_rust\",\n" -" srcs: [\"src/main.rs\"],\n" +"rust_library {\n" +" name: \"libbirthdayservice\",\n" +" srcs: [\"src/lib.rs\"],\n" +" crate_name: \"birthdayservice\",\n" +" rustlibs: [\n" +" \"com.example.birthdayservice-rust\",\n" +" \"libbinder_rs\",\n" +" ],\n" "}\n" "```" -#: src/android/build-rules/binary.md:16 src/android/build-rules/library.md:34 -msgid "_hello_rust/src/main.rs_:" -msgstr "_hello_rust/src/main.rs_:" +#: src/android/aidl/server.md:1 +msgid "AIDL Server" +msgstr "AIDL 服务器" -#: src/android/build-rules/binary.md:18 +#: src/android/aidl/server.md:3 +msgid "Finally, we can create a server which exposes the service:" +msgstr "最后,我们可以创建一个暴露服务的服务器:" + +#: src/android/aidl/server.md:5 +msgid "_birthday_service/src/server.rs_:" +msgstr "_birthday_service/src/server.rs_:" + +#: src/android/aidl/server.md:7 msgid "" -"```rust\n" -"//! Rust demo.\n" +"```rust,ignore\n" +"//! Birthday service.\n" +"use birthdayservice::BirthdayService;\n" +"use com_example_birthdayservice::aidl::com::example::birthdayservice::" +"IBirthdayService::BnBirthdayService;\n" +"use com_example_birthdayservice::binder;\n" "\n" -"/// Prints a greeting to standard output.\n" +"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" +"\n" +"/// Entry point for birthday service.\n" "fn main() {\n" -" println!(\"Hello from Rust!\");\n" +" let birthday_service = BirthdayService;\n" +" let birthday_service_binder = BnBirthdayService::new_binder(\n" +" birthday_service,\n" +" binder::BinderFeatures::default(),\n" +" );\n" +" binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder." +"as_binder())\n" +" .expect(\"Failed to register service\");\n" +" binder::ProcessState::join_thread_pool()\n" "}\n" "```" msgstr "" -"```rust\n" -"//! Rust demo.\n" +"```rust,ignore\n" +"//! 生日服务。\n" +"use birthdayservice::BirthdayService;\n" +"use com_example_birthdayservice::aidl::com::example::birthdayservice::" +"IBirthdayService::BnBirthdayService;\n" +"use com_example_birthdayservice::binder;\n" "\n" -"/// Prints a greeting to standard output.\n" +"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" +"\n" +"/// 生日服务的入口。\n" "fn main() {\n" -" println!(\"Hello from Rust!\");\n" +" let birthday_service = BirthdayService;\n" +" let birthday_service_binder = BnBirthdayService::new_binder(\n" +" birthday_service,\n" +" binder::BinderFeatures::default(),\n" +" );\n" +" binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder." +"as_binder())\n" +" .expect(\"Failed to register service\");\n" +" binder::ProcessState::join_thread_pool()\n" "}\n" "```" -#: src/android/build-rules/binary.md:27 -msgid "You can now build, push, and run the binary:" -msgstr "你现在可以构建、推送和运行二进制文件:" - -#: src/android/build-rules/binary.md:29 -msgid "" -"```shell\n" -"$ m hello_rust\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\n" -"$ adb shell /data/local/tmp/hello_rust\n" -"Hello from Rust!\n" -"```" -msgstr "" -"```shell\n" -"$ m hello_rust\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\n" -"$ adb shell /data/local/tmp/hello_rust\n" -"Hello from Rust!\n" -"```" - -#: src/android/build-rules/library.md:1 -msgid "Rust Libraries" -msgstr "Rust 库" - -#: src/android/build-rules/library.md:3 -msgid "You use `rust_library` to create a new Rust library for Android." -msgstr "您可以使用 `rust_library` 为 Android 创建一个新的 Rust 库。" - -#: src/android/build-rules/library.md:5 -msgid "Here we declare a dependency on two libraries:" -msgstr "在这里,我们声明了对两个库的依赖:" - -#: src/android/build-rules/library.md:7 -msgid "`libgreeting`, which we define below," -msgstr "`libgreeting`, 我们在下面进行了定义," - -#: src/android/build-rules/library.md:8 -msgid "" -"`libtextwrap`, which is a crate already vendored in [`external/rust/crates/`]" -"(https://cs.android.com/android/platform/superproject/+/master:external/rust/" -"crates/)." -msgstr "" -"`libtextwrap`, 一个已经在 [`external/rust/crates/`](https://cs.android.com/" -"android/platform/superproject/+/master:external/rust/crates/) 中提供的 " -"crate。" - -#: src/android/build-rules/library.md:15 +#: src/android/aidl/server.md:30 msgid "" "```javascript\n" "rust_binary {\n" -" name: \"hello_rust_with_dep\",\n" -" crate_name: \"hello_rust_with_dep\",\n" -" srcs: [\"src/main.rs\"],\n" +" name: \"birthday_server\",\n" +" crate_name: \"birthday_server\",\n" +" srcs: [\"src/server.rs\"],\n" " rustlibs: [\n" -" \"libgreetings\",\n" -" \"libtextwrap\",\n" +" \"com.example.birthdayservice-rust\",\n" +" \"libbinder_rs\",\n" +" \"libbirthdayservice\",\n" " ],\n" " prefer_rlib: true,\n" "}\n" -"\n" -"rust_library {\n" -" name: \"libgreetings\",\n" -" crate_name: \"greetings\",\n" -" srcs: [\"src/lib.rs\"],\n" -"}\n" "```" msgstr "" "```javascript\n" "rust_binary {\n" -" name: \"hello_rust_with_dep\",\n" -" crate_name: \"hello_rust_with_dep\",\n" -" srcs: [\"src/main.rs\"],\n" +" name: \"birthday_server\",\n" +" crate_name: \"birthday_server\",\n" +" srcs: [\"src/server.rs\"],\n" " rustlibs: [\n" -" \"libgreetings\",\n" -" \"libtextwrap\",\n" +" \"com.example.birthdayservice-rust\",\n" +" \"libbinder_rs\",\n" +" \"libbirthdayservice\",\n" " ],\n" " prefer_rlib: true,\n" "}\n" -"\n" -"rust_library {\n" -" name: \"libgreetings\",\n" -" crate_name: \"greetings\",\n" -" srcs: [\"src/lib.rs\"],\n" -"}\n" "```" -#: src/android/build-rules/library.md:36 -msgid "" -"```rust,ignore\n" -"//! Rust demo.\n" -"\n" -"use greetings::greeting;\n" -"use textwrap::fill;\n" -"\n" -"/// Prints a greeting to standard output.\n" -"fn main() {\n" -" println!(\"{}\", fill(&greeting(\"Bob\"), 24));\n" -"}\n" -"```" -msgstr "" -"```rust,ignore\n" -"//! Rust demo.\n" -"\n" -"use greetings::greeting;\n" -"use textwrap::fill;\n" -"\n" -"/// Prints a greeting to standard output.\n" -"fn main() {\n" -" println!(\"{}\", fill(&greeting(\"Bob\"), 24));\n" -"}\n" -"```" - -#: src/android/build-rules/library.md:48 -msgid "_hello_rust/src/lib.rs_:" -msgstr "_hello_rust/src/lib.rs_:" - -#: src/android/build-rules/library.md:50 -msgid "" -"```rust,ignore\n" -"//! Greeting library.\n" -"\n" -"/// Greet `name`.\n" -"pub fn greeting(name: &str) -> String {\n" -" format!(\"Hello {name}, it is very nice to meet you!\")\n" -"}\n" -"```" -msgstr "" -"```rust,ignore\n" -"//! Greeting library.\n" -"\n" -"/// Greet `name`.\n" -"pub fn greeting(name: &str) -> String {\n" -" format!(\"Hello {name}, it is very nice to meet you!\")\n" -"}\n" -"```" - -#: src/android/build-rules/library.md:59 -msgid "You build, push, and run the binary like before:" -msgstr "您可以像之前一样构建、推送和运行二进制文件:" - -#: src/android/build-rules/library.md:61 +#: src/android/aidl/deploy.md:3 +msgid "We can now build, push, and start the service:" +msgstr "我们现在可以构建、推送和启动服务:" + +#: src/android/aidl/deploy.md:5 +#, fuzzy msgid "" "```shell\n" -"$ m hello_rust_with_dep\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/" -"tmp\n" -"$ adb shell /data/local/tmp/hello_rust_with_dep\n" -"Hello Bob, it is very\n" -"nice to meet you!\n" +"m birthday_server\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/birthday_server\n" "```" msgstr "" "```shell\n" -"$ m hello_rust_with_dep\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/" -"tmp\n" -"$ adb shell /data/local/tmp/hello_rust_with_dep\n" -"Hello Bob, it is very\n" -"nice to meet you!\n" -"```" - -#: src/android/aidl.md:3 -msgid "" -"The [Android Interface Definition Language (AIDL)](https://developer.android." -"com/guide/components/aidl) is supported in Rust:" -msgstr "Rust 支持 [Android 接口定义语言 (AIDL)](https://developer.android.com/guide/components/aidl):" - -#: src/android/aidl.md:6 -msgid "Rust code can call existing AIDL servers," -msgstr "Rust 代码可以调用现有的 AIDL 服务器," - -#: src/android/aidl.md:7 -msgid "You can create new AIDL servers in Rust." -msgstr "您可以在 Rust 中创建新的 AIDL 服务器。" - -#: src/android/aidl/interface.md:1 -msgid "AIDL Interfaces" -msgstr "AIDL 接口" - -#: src/android/aidl/interface.md:3 -msgid "You declare the API of your service using an AIDL interface:" -msgstr "您可以使用 AIDL 接口声明您的服务的 API:" - -#: src/android/aidl/interface.md:5 -msgid "" -"_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_:" -msgstr "" -"_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_:" - -#: src/android/aidl/interface.md:7 -msgid "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** Birthday service interface. */\n" -"interface IBirthdayService {\n" -" /** Generate a Happy Birthday message. */\n" -" String wishHappyBirthday(String name, int years);\n" -"}\n" -"```" -msgstr "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** 生日服务接口。 */\n" -"interface IBirthdayService {\n" -" /** 生成一条生日快乐的祝福信息。 */\n" -" String wishHappyBirthday(String name, int years);\n" -"}\n" +"$ m birthday_server\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" +"$ adb shell /data/local/tmp/birthday_server\n" "```" -#: src/android/aidl/interface.md:17 -msgid "_birthday_service/aidl/Android.bp_:" -msgstr "_birthday_service/aidl/Android.bp_:" - -#: src/android/aidl/interface.md:19 -msgid "" -"```javascript\n" -"aidl_interface {\n" -" name: \"com.example.birthdayservice\",\n" -" srcs: [\"com/example/birthdayservice/*.aidl\"],\n" -" unstable: true,\n" -" backend: {\n" -" rust: { // Rust is not enabled by default\n" -" enabled: true,\n" -" },\n" -" },\n" -"}\n" -"```" -msgstr "" -"```javascript\n" -"aidl_interface {\n" -" name: \"com.example.birthdayservice\",\n" -" srcs: [\"com/example/birthdayservice/*.aidl\"],\n" -" unstable: true,\n" -" backend: {\n" -" rust: { // 默认情况下不启用 Rust \n" -" enabled: true,\n" -" },\n" -" },\n" -"}\n" -"```" +#: src/android/aidl/deploy.md:11 +msgid "In another terminal, check that the service runs:" +msgstr "在另一个终端中,检查该服务是否正在运行:" -#: src/android/aidl/interface.md:32 -msgid "" -"Add `vendor_available: true` if your AIDL file is used by a binary in the " -"vendor partition." -msgstr "" -"如果供应商分区中的二进制文件使用了您的 AIDL 文件,请添加 `vendor_available: " -"true`。" +#: src/android/aidl/deploy.md:21 +msgid "You can also call the service with `service call`:" +msgstr "您还可以使用 `service call` 命令调用该服务:" -#: src/android/aidl/implementation.md:1 -msgid "Service Implementation" -msgstr "服务实现" +#: src/android/aidl/client.md:1 +msgid "AIDL Client" +msgstr "AIDL 客户端" -#: src/android/aidl/implementation.md:3 -msgid "We can now implement the AIDL service:" -msgstr "我们现在可以实现AIDL服务:" +#: src/android/aidl/client.md:3 +msgid "Finally, we can create a Rust client for our new service." +msgstr "最后,我们可以为我们的新服务创建一个 Rust 客户端。" -#: src/android/aidl/implementation.md:5 -msgid "_birthday_service/src/lib.rs_:" -msgstr "_birthday_service/src/lib.rs_:" +#: src/android/aidl/client.md:5 +msgid "_birthday_service/src/client.rs_:" +msgstr "_birthday_service/src/client.rs_:" -#: src/android/aidl/implementation.md:7 +#: src/android/aidl/client.md:7 msgid "" "```rust,ignore\n" -"//! Implementation of the `IBirthdayService` AIDL interface.\n" +"//! Birthday service.\n" "use com_example_birthdayservice::aidl::com::example::birthdayservice::" "IBirthdayService::IBirthdayService;\n" "use com_example_birthdayservice::binder;\n" "\n" -"/// The `IBirthdayService` implementation.\n" -"pub struct BirthdayService;\n" +"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" "\n" -"impl binder::Interface for BirthdayService {}\n" +"/// Connect to the BirthdayService.\n" +"pub fn connect() -> Result, binder::" +"StatusCode> {\n" +" binder::get_interface(SERVICE_IDENTIFIER)\n" +"}\n" "\n" -"impl IBirthdayService for BirthdayService {\n" -" fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::" -"Result {\n" -" Ok(format!(\n" -" \"Happy Birthday {name}, congratulations with the {years} years!" -"\"\n" -" ))\n" -" }\n" +"/// Call the birthday service.\n" +"fn main() -> Result<(), binder::Status> {\n" +" let name = std::env::args()\n" +" .nth(1)\n" +" .unwrap_or_else(|| String::from(\"Bob\"));\n" +" let years = std::env::args()\n" +" .nth(2)\n" +" .and_then(|arg| arg.parse::().ok())\n" +" .unwrap_or(42);\n" +"\n" +" binder::ProcessState::start_thread_pool();\n" +" let service = connect().expect(\"Failed to connect to " +"BirthdayService\");\n" +" let msg = service.wishHappyBirthday(&name, years)?;\n" +" println!(\"{msg}\");\n" +" Ok(())\n" "}\n" "```" msgstr "" "```rust,ignore\n" -"//! 实现了 `IBirthdayService` AIDL 接口。\n" +"//! 生日服务。\n" "use com_example_birthdayservice::aidl::com::example::birthdayservice::" "IBirthdayService::IBirthdayService;\n" "use com_example_birthdayservice::binder;\n" "\n" -"/// `IBirthdayService` 接口的具体实现。\n" -"pub struct BirthdayService;\n" +"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" "\n" -"impl binder::Interface for BirthdayService {}\n" +"/// 连接到 BirthdayService。\n" +"pub fn connect() -> Result, binder::" +"StatusCode> {\n" +" binder::get_interface(SERVICE_IDENTIFIER)\n" +"}\n" "\n" -"impl IBirthdayService for BirthdayService {\n" -" fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::" -"Result {\n" -" Ok(format!(\n" -" \"Happy Birthday {name}, congratulations with the {years} years!" -"\"\n" -" ))\n" -" }\n" +"/// 调用生日服务。\n" +"fn main() -> Result<(), binder::Status> {\n" +" let name = std::env::args()\n" +" .nth(1)\n" +" .unwrap_or_else(|| String::from(\"Bob\"));\n" +" let years = std::env::args()\n" +" .nth(2)\n" +" .and_then(|arg| arg.parse::().ok())\n" +" .unwrap_or(42);\n" +"\n" +" binder::ProcessState::start_thread_pool();\n" +" let service = connect().expect(\"Failed to connect to " +"BirthdayService\");\n" +" let msg = service.wishHappyBirthday(&name, years)?;\n" +" println!(\"{msg}\");\n" +" Ok(())\n" "}\n" "```" -#: src/android/aidl/implementation.md:26 src/android/aidl/server.md:28 -#: src/android/aidl/client.md:37 -msgid "_birthday_service/Android.bp_:" -msgstr "_birthday_service/Android.bp_:" - -#: src/android/aidl/implementation.md:28 +#: src/android/aidl/client.md:39 msgid "" "```javascript\n" -"rust_library {\n" -" name: \"libbirthdayservice\",\n" -" srcs: [\"src/lib.rs\"],\n" -" crate_name: \"birthdayservice\",\n" +"rust_binary {\n" +" name: \"birthday_client\",\n" +" crate_name: \"birthday_client\",\n" +" srcs: [\"src/client.rs\"],\n" " rustlibs: [\n" " \"com.example.birthdayservice-rust\",\n" " \"libbinder_rs\",\n" " ],\n" -"}\n" -"```" -msgstr "" -"```javascript\n" -"rust_library {\n" -" name: \"libbirthdayservice\",\n" -" srcs: [\"src/lib.rs\"],\n" -" crate_name: \"birthdayservice\",\n" -" rustlibs: [\n" -" \"com.example.birthdayservice-rust\",\n" -" \"libbinder_rs\",\n" -" ],\n" -"}\n" -"```" - -#: src/android/aidl/server.md:1 -msgid "AIDL Server" -msgstr "AIDL 服务器" - -#: src/android/aidl/server.md:3 -msgid "Finally, we can create a server which exposes the service:" -msgstr "最后,我们可以创建一个暴露服务的服务器:" - -#: src/android/aidl/server.md:5 -msgid "_birthday_service/src/server.rs_:" -msgstr "_birthday_service/src/server.rs_:" - -#: src/android/aidl/server.md:7 -msgid "" -"```rust,ignore\n" -"//! Birthday service.\n" -"use birthdayservice::BirthdayService;\n" -"use com_example_birthdayservice::aidl::com::example::birthdayservice::" -"IBirthdayService::BnBirthdayService;\n" -"use com_example_birthdayservice::binder;\n" -"\n" -"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" -"\n" -"/// Entry point for birthday service.\n" -"fn main() {\n" -" let birthday_service = BirthdayService;\n" -" let birthday_service_binder = BnBirthdayService::new_binder(\n" -" birthday_service,\n" -" binder::BinderFeatures::default(),\n" -" );\n" -" binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder." -"as_binder())\n" -" .expect(\"Failed to register service\");\n" -" binder::ProcessState::join_thread_pool()\n" -"}\n" -"```" -msgstr "" -"```rust,ignore\n" -"//! 生日服务。\n" -"use birthdayservice::BirthdayService;\n" -"use com_example_birthdayservice::aidl::com::example::birthdayservice::" -"IBirthdayService::BnBirthdayService;\n" -"use com_example_birthdayservice::binder;\n" -"\n" -"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" -"\n" -"/// 生日服务的入口。\n" -"fn main() {\n" -" let birthday_service = BirthdayService;\n" -" let birthday_service_binder = BnBirthdayService::new_binder(\n" -" birthday_service,\n" -" binder::BinderFeatures::default(),\n" -" );\n" -" binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder." -"as_binder())\n" -" .expect(\"Failed to register service\");\n" -" binder::ProcessState::join_thread_pool()\n" -"}\n" -"```" - -#: src/android/aidl/server.md:30 -msgid "" -"```javascript\n" -"rust_binary {\n" -" name: \"birthday_server\",\n" -" crate_name: \"birthday_server\",\n" -" srcs: [\"src/server.rs\"],\n" -" rustlibs: [\n" -" \"com.example.birthdayservice-rust\",\n" -" \"libbinder_rs\",\n" -" \"libbirthdayservice\",\n" -" ],\n" -" prefer_rlib: true,\n" -"}\n" -"```" -msgstr "" -"```javascript\n" -"rust_binary {\n" -" name: \"birthday_server\",\n" -" crate_name: \"birthday_server\",\n" -" srcs: [\"src/server.rs\"],\n" -" rustlibs: [\n" -" \"com.example.birthdayservice-rust\",\n" -" \"libbinder_rs\",\n" -" \"libbirthdayservice\",\n" -" ],\n" -" prefer_rlib: true,\n" -"}\n" -"```" - -#: src/android/aidl/deploy.md:3 -msgid "We can now build, push, and start the service:" -msgstr "我们现在可以构建、推送和启动服务:" - -#: src/android/aidl/deploy.md:5 -msgid "" -"```shell\n" -"$ m birthday_server\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" -"$ adb shell /data/local/tmp/birthday_server\n" -"```" -msgstr "" -"```shell\n" -"$ m birthday_server\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" -"$ adb shell /data/local/tmp/birthday_server\n" -"```" - -#: src/android/aidl/deploy.md:11 -msgid "In another terminal, check that the service runs:" -msgstr "在另一个终端中,检查该服务是否正在运行:" - -#: src/android/aidl/deploy.md:13 -msgid "" -"```shell\n" -"$ adb shell service check birthdayservice\n" -"Service birthdayservice: found\n" -"```" -msgstr "" -"```shell\n" -"$ adb shell service check birthdayservice\n" -"Service birthdayservice: found\n" -"```" - -#: src/android/aidl/deploy.md:18 -msgid "You can also call the service with `service call`:" -msgstr "您还可以使用 `service call` 命令调用该服务:" - -#: src/android/aidl/deploy.md:20 -msgid "" -"```shell\n" -"$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n" -"Result: Parcel(\n" -" 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'\n" -" 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'\n" -" 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n" -" 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n" -" 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n" -" 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n" -" 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n" -" 0x00000070: 00210073 00000000 's.!..... ')\n" -"```" -msgstr "" -"```shell\n" -"$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n" -"Result: Parcel(\n" -" 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'\n" -" 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'\n" -" 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n" -" 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n" -" 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n" -" 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n" -" 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n" -" 0x00000070: 00210073 00000000 's.!..... ')\n" -"```" - -#: src/android/aidl/client.md:1 -msgid "AIDL Client" -msgstr "AIDL 客户端" - -#: src/android/aidl/client.md:3 -msgid "Finally, we can create a Rust client for our new service." -msgstr "最后,我们可以为我们的新服务创建一个 Rust 客户端。" - -#: src/android/aidl/client.md:5 -msgid "_birthday_service/src/client.rs_:" -msgstr "_birthday_service/src/client.rs_:" - -#: src/android/aidl/client.md:7 -msgid "" -"```rust,ignore\n" -"//! Birthday service.\n" -"use com_example_birthdayservice::aidl::com::example::birthdayservice::" -"IBirthdayService::IBirthdayService;\n" -"use com_example_birthdayservice::binder;\n" -"\n" -"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" -"\n" -"/// Connect to the BirthdayService.\n" -"pub fn connect() -> Result, binder::" -"StatusCode> {\n" -" binder::get_interface(SERVICE_IDENTIFIER)\n" -"}\n" -"\n" -"/// Call the birthday service.\n" -"fn main() -> Result<(), binder::Status> {\n" -" let name = std::env::args()\n" -" .nth(1)\n" -" .unwrap_or_else(|| String::from(\"Bob\"));\n" -" let years = std::env::args()\n" -" .nth(2)\n" -" .and_then(|arg| arg.parse::().ok())\n" -" .unwrap_or(42);\n" -"\n" -" binder::ProcessState::start_thread_pool();\n" -" let service = connect().expect(\"Failed to connect to " -"BirthdayService\");\n" -" let msg = service.wishHappyBirthday(&name, years)?;\n" -" println!(\"{msg}\");\n" -" Ok(())\n" -"}\n" -"```" -msgstr "" -"```rust,ignore\n" -"//! 生日服务。\n" -"use com_example_birthdayservice::aidl::com::example::birthdayservice::" -"IBirthdayService::IBirthdayService;\n" -"use com_example_birthdayservice::binder;\n" -"\n" -"const SERVICE_IDENTIFIER: &str = \"birthdayservice\";\n" -"\n" -"/// 连接到 BirthdayService。\n" -"pub fn connect() -> Result, binder::" -"StatusCode> {\n" -" binder::get_interface(SERVICE_IDENTIFIER)\n" -"}\n" -"\n" -"/// 调用生日服务。\n" -"fn main() -> Result<(), binder::Status> {\n" -" let name = std::env::args()\n" -" .nth(1)\n" -" .unwrap_or_else(|| String::from(\"Bob\"));\n" -" let years = std::env::args()\n" -" .nth(2)\n" -" .and_then(|arg| arg.parse::().ok())\n" -" .unwrap_or(42);\n" -"\n" -" binder::ProcessState::start_thread_pool();\n" -" let service = connect().expect(\"Failed to connect to BirthdayService" -"\");\n" -" let msg = service.wishHappyBirthday(&name, years)?;\n" -" println!(\"{msg}\");\n" -" Ok(())\n" -"}\n" -"```" - -#: src/android/aidl/client.md:39 -msgid "" -"```javascript\n" -"rust_binary {\n" -" name: \"birthday_client\",\n" -" crate_name: \"birthday_client\",\n" -" srcs: [\"src/client.rs\"],\n" -" rustlibs: [\n" -" \"com.example.birthdayservice-rust\",\n" -" \"libbinder_rs\",\n" -" ],\n" -" prefer_rlib: true,\n" +" prefer_rlib: true,\n" "}\n" "```" msgstr "" @@ -13026,19 +12709,19 @@ msgid "Build, push, and run the client on your device:" msgstr "在您的设备上构建、推送并运行客户端:" #: src/android/aidl/client.md:56 +#, fuzzy msgid "" "```shell\n" -"$ m birthday_client\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp\n" -"$ adb shell /data/local/tmp/birthday_client Charlie 60\n" -"Happy Birthday Charlie, congratulations with the 60 years!\n" +"m birthday_client\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/birthday_client Charlie 60\n" "```" msgstr "" "```shell\n" -"$ m birthday_client\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp\n" -"$ adb shell /data/local/tmp/birthday_client Charlie 60\n" -"Happy Birthday Charlie, congratulations with the 60 years!\n" +"$ m birthday_server\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" +"$ adb shell /data/local/tmp/birthday_server\n" "```" #: src/android/aidl/changing.md:3 @@ -13047,28 +12730,6 @@ msgid "" "specify a list of lines for the birthday card:" msgstr "让我们扩展API以提供更多功能:我们希望允许客户端指定生日贺卡的行列表:" -#: src/android/aidl/changing.md:6 -msgid "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** Birthday service interface. */\n" -"interface IBirthdayService {\n" -" /** Generate a Happy Birthday message. */\n" -" String wishHappyBirthday(String name, int years, in String[] text);\n" -"}\n" -"```" -msgstr "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** 生日服务接口。 */\n" -"interface IBirthdayService {\n" -" /** 生成一条生日快乐的祝福信息。 */\n" -" String wishHappyBirthday(String name, int years, in String[] text);\n" -"}\n" -"```" - #: src/android/logging.md:3 msgid "" "You should use the `log` crate to automatically log to `logcat` (on-device) " @@ -13159,11 +12820,13 @@ msgid "Build, push, and run the binary on your device:" msgstr "在你的设备上构建,推送,并运行二进制文件 :" #: src/android/logging.md:44 +#, fuzzy msgid "" "```shell\n" -"$ m hello_rust_logs\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp\n" -"$ adb shell /data/local/tmp/hello_rust_logs\n" +"m hello_rust_logs\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/hello_rust_logs\n" "```" msgstr "" "```shell\n" @@ -13176,26 +12839,6 @@ msgstr "" msgid "The logs show up in `adb logcat`:" msgstr "日志将会在 `adb logcat` 中显示:" -#: src/android/logging.md:52 -msgid "" -"```shell\n" -"$ adb logcat -s rust\n" -"09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.\n" -"09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going " -"fine.\n" -"09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went " -"wrong!\n" -"```" -msgstr "" -"```shell\n" -"$ adb logcat -s rust\n" -"09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.\n" -"09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going " -"fine.\n" -"09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went " -"wrong!\n" -"```" - #: src/android/interoperability.md:3 msgid "" "Rust has excellent support for interoperability with other languages. This " @@ -13279,18 +12922,6 @@ msgstr "" msgid "_interoperability/bindgen/libbirthday.h_:" msgstr "" -#: src/android/interoperability/with-c/bindgen.md:10 -msgid "" -"```c\n" -"typedef struct card {\n" -" const char* name;\n" -" int years;\n" -"} card;\n" -"\n" -"void print_card(const card* card);\n" -"```" -msgstr "" - #: src/android/interoperability/with-c/bindgen.md:19 msgid "_interoperability/bindgen/libbirthday.c_:" msgstr "" @@ -13405,14 +13036,20 @@ msgid "" msgstr "" #: src/android/interoperability/with-c/bindgen.md:100 +#, fuzzy msgid "" "```shell\n" -"$ m print_birthday_card\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/" -"tmp\n" -"$ adb shell /data/local/tmp/print_birthday_card\n" +"m print_birthday_card\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/print_birthday_card\n" "```" msgstr "" +"```shell\n" +"$ m birthday_server\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" +"$ adb shell /data/local/tmp/birthday_server\n" +"```" #: src/android/interoperability/with-c/bindgen.md:106 msgid "Finally, we can run auto-generated tests to ensure the bindings work:" @@ -13433,13 +13070,6 @@ msgid "" "```" msgstr "" -#: src/android/interoperability/with-c/bindgen.md:122 -msgid "" -"```shell\n" -"$ atest libbirthday_bindgen_test\n" -"```" -msgstr "" - #: src/android/interoperability/with-c/rust.md:1 msgid "Calling Rust" msgstr "" @@ -13543,13 +13173,20 @@ msgid "" msgstr "" #: src/android/interoperability/with-c/rust.md:75 +#, fuzzy msgid "" "```shell\n" -"$ m analyze_numbers\n" -"$ adb push $ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/tmp\n" -"$ adb shell /data/local/tmp/analyze_numbers\n" +"m analyze_numbers\n" +"adb push \"$ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/" +"tmp\"\n" +"adb shell /data/local/tmp/analyze_numbers\n" "```" msgstr "" +"```shell\n" +"$ m birthday_server\n" +"$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp\n" +"$ adb shell /data/local/tmp/birthday_server\n" +"```" #: src/android/interoperability/with-c/rust.md:83 msgid "" @@ -13574,6 +13211,42 @@ msgid "" "using this." msgstr "" +#: src/android/interoperability/cpp.md:14 +msgid "" +"At this point, the instructor should switch to the [CXX tutorial](https://" +"cxx.rs/tutorial.html)." +msgstr "" + +#: src/android/interoperability/cpp.md:16 +msgid "Walk the students through the tutorial step by step." +msgstr "" + +#: src/android/interoperability/cpp.md:18 +msgid "" +"Highlight how CXX presents a clean interface without unsafe code in _both " +"languages_." +msgstr "" + +#: src/android/interoperability/cpp.md:20 +msgid "" +"Show the correspondence between [Rust and C++ types](https://cxx.rs/bindings." +"html):" +msgstr "" + +#: src/android/interoperability/cpp.md:22 +msgid "" +"Explain how a Rust `String` cannot map to a C++ `std::string` (the latter " +"does not uphold the UTF-8 invariant). Show that despite being different " +"types, `rust::String` in C++ can be easily constructed from a C++ `std::" +"string`, making it very ergonomic to use." +msgstr "" + +#: src/android/interoperability/cpp.md:28 +msgid "" +"Explain that a Rust function returning `Result` becomes a function " +"which throws a `E` exception in C++ (and vice versa)." +msgstr "" + #: src/android/interoperability/java.md:1 msgid "Interoperability with Java" msgstr "" @@ -13676,15 +13349,6 @@ msgstr "" msgid "Finally, you can build, sync, and run the binary:" msgstr "" -#: src/android/interoperability/java.md:75 -msgid "" -"```shell\n" -"$ m helloworld_jni\n" -"$ adb sync # requires adb root && adb remount\n" -"$ adb shell /system/bin/helloworld_jni\n" -"```" -msgstr "" - #: src/exercises/android/morning.md:3 msgid "" "This is a group exercise: We will look at one of the projects you work with " @@ -13753,18 +13417,6 @@ msgid "" "To get started, install some tools we'll need later. On gLinux or Debian:" msgstr "" -#: src/bare-metal.md:22 -msgid "" -"```bash\n" -"sudo apt install gcc-aarch64-linux-gnu gdb-multiarch libudev-dev picocom pkg-" -"config qemu-system-arm\n" -"rustup update\n" -"rustup target add aarch64-unknown-none thumbv7em-none-eabihf\n" -"rustup component add llvm-tools-preview\n" -"cargo install cargo-binutils cargo-embed\n" -"```" -msgstr "" - #: src/bare-metal.md:30 msgid "" "And give users in the `plugdev` group access to the micro:bit programmer:" @@ -13784,19 +13436,6 @@ msgstr "" msgid "On MacOS:" msgstr "" -#: src/bare-metal.md:40 -msgid "" -"```bash\n" -"xcode-select --install\n" -"brew install gdb picocom qemu\n" -"brew install --cask gcc-aarch64-embedded\n" -"rustup update\n" -"rustup target add aarch64-unknown-none thumbv7em-none-eabihf\n" -"rustup component add llvm-tools-preview\n" -"cargo install cargo-binutils cargo-embed\n" -"```" -msgstr "" - #: src/bare-metal/no_std.md:1 msgid "`no_std`" msgstr "" @@ -13909,21 +13548,6 @@ msgstr "" msgid "A minimal `no_std` program" msgstr "" -#: src/bare-metal/minimal.md:3 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"use core::panic::PanicInfo;\n" -"\n" -"#[panic_handler]\n" -"fn panic(_panic: &PanicInfo) -> ! {\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/minimal.md:17 msgid "This will compile to an empty binary." msgstr "" @@ -14026,25 +13650,6 @@ msgid "" "Cortex M microcontrollers." msgstr "" -#: src/bare-metal/microcontrollers.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"extern crate panic_halt as _;\n" -"\n" -"mod interrupts;\n" -"\n" -"use cortex_m_rt::entry;\n" -"\n" -"#[entry]\n" -"fn main() -> ! {\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers.md:21 msgid "" "Next we'll look at how to access peripherals, with increasing levels of " @@ -14145,13 +13750,6 @@ msgstr "" msgid "Run the example with:" msgstr "" -#: src/bare-metal/microcontrollers/mmio.md:68 -msgid "" -"```sh\n" -"cargo embed --bin mmio\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/pacs.md:1 msgid "Peripheral Access Crates" msgstr "" @@ -14235,13 +13833,6 @@ msgid "" "pac -- -d --no-show-raw-insn` to see the resulting binary." msgstr "" -#: src/bare-metal/microcontrollers/pacs.md:61 -msgid "" -"```sh\n" -"cargo embed --bin pac\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/hals.md:1 msgid "HAL crates" msgstr "" @@ -14298,13 +13889,6 @@ msgid "" "STM32, GD32, nRF, NXP, MSP430, AVR and PIC microcontrollers." msgstr "" -#: src/bare-metal/microcontrollers/hals.md:45 -msgid "" -"```sh\n" -"cargo embed --bin hal\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/board-support.md:1 msgid "Board support crates" msgstr "" @@ -14315,30 +13899,6 @@ msgid "" "board for convenience." msgstr "" -#: src/bare-metal/microcontrollers/board-support.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"extern crate panic_halt as _;\n" -"\n" -"use cortex_m_rt::entry;\n" -"use microbit::hal::prelude::*;\n" -"use microbit::Board;\n" -"\n" -"#[entry]\n" -"fn main() -> ! {\n" -" let mut board = Board::take().unwrap();\n" -"\n" -" board.display_pins.col1.set_low().unwrap();\n" -" board.display_pins.row1.set_high().unwrap();\n" -"\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/board-support.md:28 msgid "" "In this case the board support crate is just providing more useful names, " @@ -14355,13 +13915,6 @@ msgstr "" msgid "`microbit-v2` includes a simple driver for the LED matrix." msgstr "" -#: src/bare-metal/microcontrollers/board-support.md:36 -msgid "" -"```sh\n" -"cargo embed --bin board_support\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/type-state.md:1 msgid "The type state pattern" msgstr "" @@ -14593,13 +14146,6 @@ msgstr "" msgid "In one terminal under `src/bare-metal/microcontrollers/examples/`:" msgstr "" -#: src/bare-metal/microcontrollers/debugging.md:15 -msgid "" -"```sh\n" -"cargo embed --bin board_support debug\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/debugging.md:19 msgid "In another terminal in the same directory:" msgstr "" @@ -14616,18 +14162,6 @@ msgstr "" msgid "In GDB, try running:" msgstr "" -#: src/bare-metal/microcontrollers/debugging.md:29 -msgid "" -"```gdb\n" -"b src/bin/board_support.rs:29\n" -"b src/bin/board_support.rs:30\n" -"b src/bin/board_support.rs:32\n" -"c\n" -"c\n" -"c\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/other-projects.md:1 #: src/bare-metal/aps/other-projects.md:1 msgid "Other projects" @@ -14876,25 +14410,11 @@ msgstr "" msgid "See the serial output on Linux with:" msgstr "" -#: src/exercises/bare-metal/compass.md:114 -msgid "" -"```sh\n" -"picocom --baud 115200 --imap lfcrlf /dev/ttyACM0\n" -"```" -msgstr "" - #: src/exercises/bare-metal/compass.md:118 msgid "" "Or on Mac OS something like (the device name may be slightly different):" msgstr "" -#: src/exercises/bare-metal/compass.md:120 -msgid "" -"```sh\n" -"picocom --baud 115200 --imap lfcrlf /dev/tty.usbmodem14502\n" -"```" -msgstr "" - #: src/exercises/bare-metal/compass.md:124 msgid "Use Ctrl+A Ctrl+Q to quit picocom." msgstr "" @@ -14925,6 +14445,154 @@ msgid "" "hardware, but is designed purely for virtual machines." msgstr "" +#: src/bare-metal/aps/entry-point.md:3 +msgid "" +"Before we can start running Rust code, we need to do some initialisation." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:5 +msgid "" +"```armasm\n" +".section .init.entry, \"ax\"\n" +".global entry\n" +"entry:\n" +" /*\n" +" * Load and apply the memory management configuration, ready to enable " +"MMU and\n" +" * caches.\n" +" */\n" +" adrp x30, idmap\n" +" msr ttbr0_el1, x30\n" +"\n" +" mov_i x30, .Lmairval\n" +" msr mair_el1, x30\n" +"\n" +" mov_i x30, .Ltcrval\n" +" /* Copy the supported PA range into TCR_EL1.IPS. */\n" +" mrs x29, id_aa64mmfr0_el1\n" +" bfi x30, x29, #32, #4\n" +"\n" +" msr tcr_el1, x30\n" +"\n" +" mov_i x30, .Lsctlrval\n" +"\n" +" /*\n" +" * Ensure everything before this point has completed, then invalidate " +"any\n" +" * potentially stale local TLB entries before they start being used.\n" +" */\n" +" isb\n" +" tlbi vmalle1\n" +" ic iallu\n" +" dsb nsh\n" +" isb\n" +"\n" +" /*\n" +" * Configure sctlr_el1 to enable MMU and cache and don't proceed until " +"this\n" +" * has completed.\n" +" */\n" +" msr sctlr_el1, x30\n" +" isb\n" +"\n" +" /* Disable trapping floating point access in EL1. */\n" +" mrs x30, cpacr_el1\n" +" orr x30, x30, #(0x3 << 20)\n" +" msr cpacr_el1, x30\n" +" isb\n" +"\n" +" /* Zero out the bss section. */\n" +" adr_l x29, bss_begin\n" +" adr_l x30, bss_end\n" +"0: cmp x29, x30\n" +" b.hs 1f\n" +" stp xzr, xzr, [x29], #16\n" +" b 0b\n" +"\n" +"1: /* Prepare the stack. */\n" +" adr_l x30, boot_stack_end\n" +" mov sp, x30\n" +"\n" +" /* Set up exception vector. */\n" +" adr x30, vector_table_el1\n" +" msr vbar_el1, x30\n" +"\n" +" /* Call into Rust code. */\n" +" bl main\n" +"\n" +" /* Loop forever waiting for interrupts. */\n" +"2: wfi\n" +" b 2b\n" +"```" +msgstr "" + +#: src/bare-metal/aps/entry-point.md:77 +msgid "" +"This is the same as it would be for C: initialising the processor state, " +"zeroing the BSS, and setting up the stack pointer." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:79 +msgid "" +"The BSS (block starting symbol, for historical reasons) is the part of the " +"object file which containing statically allocated variables which are " +"initialised to zero. They are omitted from the image, to avoid wasting space " +"on zeroes. The compiler assumes that the loader will take care of zeroing " +"them." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:83 +msgid "" +"The BSS may already be zeroed, depending on how memory is initialised and " +"the image is loaded, but we zero it to be sure." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:85 +msgid "" +"We need to enable the MMU and cache before reading or writing any memory. If " +"we don't:" +msgstr "" + +#: src/bare-metal/aps/entry-point.md:86 +msgid "" +"Unaligned accesses will fault. We build the Rust code for the `aarch64-" +"unknown-none` target which sets `+strict-align` to prevent the compiler " +"generating unaligned accesses, so it should be fine in this case, but this " +"is not necessarily the case in general." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:89 +msgid "" +"If it were running in a VM, this can lead to cache coherency issues. The " +"problem is that the VM is accessing memory directly with the cache disabled, " +"while the host has cacheable aliases to the same memory. Even if the host " +"doesn't explicitly access the memory, speculative accesses can lead to cache " +"fills, and then changes from one or the other will get lost when the cache " +"is cleaned or the VM enables the cache. (Cache is keyed by physical address, " +"not VA or IPA.)" +msgstr "" + +#: src/bare-metal/aps/entry-point.md:94 +msgid "" +"For simplicity, we just use a hardcoded pagetable (see `idmap.S`) which " +"identity maps the first 1 GiB of address space for devices, the next 1 GiB " +"for DRAM, and another 1 GiB higher up for more devices. This matches the " +"memory layout that QEMU uses." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:97 +msgid "" +"We also set up the exception vector (`vbar_el1`), which we'll see more about " +"later." +msgstr "" + +#: src/bare-metal/aps/entry-point.md:98 +msgid "" +"All examples this afternoon assume we will be running at exception level 1 " +"(EL1). If you need to run at a different exception level you'll need to " +"modify `entry.S` accordingly." +msgstr "" + #: src/bare-metal/aps/inline-assembly.md:1 msgid "Inline assembly" msgstr "" @@ -15425,43 +15093,6 @@ msgid "" "We can use a struct to represent the memory layout of the UART's registers." msgstr "" -#: src/bare-metal/aps/better-uart/registers.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#[repr(C, align(4))]\n" -"struct Registers {\n" -" dr: u16,\n" -" _reserved0: [u8; 2],\n" -" rsr: ReceiveStatus,\n" -" _reserved1: [u8; 19],\n" -" fr: Flags,\n" -" _reserved2: [u8; 6],\n" -" ilpr: u8,\n" -" _reserved3: [u8; 3],\n" -" ibrd: u16,\n" -" _reserved4: [u8; 2],\n" -" fbrd: u8,\n" -" _reserved5: [u8; 3],\n" -" lcr_h: u8,\n" -" _reserved6: [u8; 3],\n" -" cr: u16,\n" -" _reserved7: [u8; 3],\n" -" ifls: u8,\n" -" _reserved8: [u8; 3],\n" -" imsc: u16,\n" -" _reserved9: [u8; 2],\n" -" ris: u16,\n" -" _reserved10: [u8; 2],\n" -" mis: u16,\n" -" _reserved11: [u8; 2],\n" -" icr: u16,\n" -" _reserved12: [u8; 2],\n" -" dmacr: u8,\n" -" _reserved13: [u8; 3],\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/aps/better-uart/registers.md:41 msgid "" "[`#[repr(C)]`](https://doc.rust-lang.org/reference/type-layout.html#the-c-" @@ -15586,9 +15217,9 @@ msgid "" " writeln!(uart, \"main({x0:#x}, {x1:#x}, {x2:#x}, {x3:#x})\").unwrap();\n" "\n" " loop {\n" -" if let Some(b) = uart.read_byte() {\n" -" uart.write_byte(b);\n" -" match b {\n" +" if let Some(byte) = uart.read_byte() {\n" +" uart.write_byte(byte);\n" +" match byte {\n" " b'\\r' => {\n" " uart.write_byte(b'\\n');\n" " }\n" @@ -15732,100 +15363,182 @@ msgid "" "examples`." msgstr "" -#: src/bare-metal/aps/other-projects.md:3 -msgid "[oreboot](https://github.com/oreboot/oreboot)" -msgstr "" - -#: src/bare-metal/aps/other-projects.md:4 -msgid "\"coreboot without the C\"" -msgstr "" - -#: src/bare-metal/aps/other-projects.md:5 -msgid "Supports x86, aarch64 and RISC-V." -msgstr "" - -#: src/bare-metal/aps/other-projects.md:6 -msgid "Relies on LinuxBoot rather than having many drivers itself." -msgstr "" - -#: src/bare-metal/aps/other-projects.md:7 -msgid "" -"[Rust RaspberryPi OS tutorial](https://github.com/rust-embedded/rust-" -"raspberrypi-OS-tutorials)" -msgstr "" - -#: src/bare-metal/aps/other-projects.md:8 +#: src/bare-metal/aps/exceptions.md:3 msgid "" -"Initialisation, UART driver, simple bootloader, JTAG, exception levels, " -"exception handling, page tables" -msgstr "" - -#: src/bare-metal/aps/other-projects.md:9 -msgid "Not all very well written, so beware." -msgstr "" - -#: src/bare-metal/aps/other-projects.md:10 -msgid "[`cargo-call-stack`](https://crates.io/crates/cargo-call-stack)" -msgstr "" - -#: src/bare-metal/aps/other-projects.md:11 -msgid "Static analysis to determine maximum stack usage." +"AArch64 defines an exception vector table with 16 entries, for 4 types of " +"exceptions (synchronous, IRQ, FIQ, SError) from 4 states (current EL with " +"SP0, current EL with SPx, lower EL using AArch64, lower EL using AArch32). " +"We implement this in assembly to save volatile registers to the stack before " +"calling into Rust code:" msgstr "" -#: src/bare-metal/useful-crates.md:3 -msgid "" -"We'll go over a few crates which solve some common problems in bare-metal " -"programming." -msgstr "" - -#: src/bare-metal/useful-crates/zerocopy.md:1 -msgid "`zerocopy`" -msgstr "" - -#: src/bare-metal/useful-crates/zerocopy.md:3 -msgid "" -"The [`zerocopy`](https://docs.rs/zerocopy/) crate (from Fuchsia) provides " -"traits and macros for safely converting between byte sequences and other " -"types." -msgstr "" - -#: src/bare-metal/useful-crates/zerocopy.md:6 +#: src/bare-metal/aps/exceptions.md:8 msgid "" "```rust,editable,compile_fail\n" -"use zerocopy::AsBytes;\n" +"use log::error;\n" +"use smccc::psci::system_off;\n" +"use smccc::Hvc;\n" "\n" -"#[repr(u32)]\n" -"#[derive(AsBytes, Debug, Default)]\n" -"enum RequestType {\n" -" #[default]\n" -" In = 0,\n" -" Out = 1,\n" -" Flush = 4,\n" +"#[no_mangle]\n" +"extern \"C\" fn sync_exception_current(_elr: u64, _spsr: u64) {\n" +" error!(\"sync_exception_current\");\n" +" system_off::().unwrap();\n" "}\n" "\n" -"#[repr(C)]\n" -"#[derive(AsBytes, Debug, Default)]\n" -"struct VirtioBlockRequest {\n" -" request_type: RequestType,\n" -" reserved: u32,\n" -" sector: u64,\n" +"#[no_mangle]\n" +"extern \"C\" fn irq_current(_elr: u64, _spsr: u64) {\n" +" error!(\"irq_current\");\n" +" system_off::().unwrap();\n" "}\n" "\n" -"fn main() {\n" -" let request = VirtioBlockRequest {\n" -" request_type: RequestType::Flush,\n" -" sector: 42,\n" -" ..Default::default()\n" -" };\n" +"#[no_mangle]\n" +"extern \"C\" fn fiq_current(_elr: u64, _spsr: u64) {\n" +" error!(\"fiq_current\");\n" +" system_off::().unwrap();\n" +"}\n" "\n" -" assert_eq!(\n" -" request.as_bytes(),\n" -" &[4, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0]\n" -" );\n" +"#[no_mangle]\n" +"extern \"C\" fn serr_current(_elr: u64, _spsr: u64) {\n" +" error!(\"serr_current\");\n" +" system_off::().unwrap();\n" +"}\n" +"\n" +"#[no_mangle]\n" +"extern \"C\" fn sync_lower(_elr: u64, _spsr: u64) {\n" +" error!(\"sync_lower\");\n" +" system_off::().unwrap();\n" +"}\n" +"\n" +"#[no_mangle]\n" +"extern \"C\" fn irq_lower(_elr: u64, _spsr: u64) {\n" +" error!(\"irq_lower\");\n" +" system_off::().unwrap();\n" +"}\n" +"\n" +"#[no_mangle]\n" +"extern \"C\" fn fiq_lower(_elr: u64, _spsr: u64) {\n" +" error!(\"fiq_lower\");\n" +" system_off::().unwrap();\n" +"}\n" +"\n" +"#[no_mangle]\n" +"extern \"C\" fn serr_lower(_elr: u64, _spsr: u64) {\n" +" error!(\"serr_lower\");\n" +" system_off::().unwrap();\n" "}\n" "```" msgstr "" +#: src/bare-metal/aps/exceptions.md:64 +msgid "EL is exception level; all our examples this afternoon run in EL1." +msgstr "" + +#: src/bare-metal/aps/exceptions.md:65 +msgid "" +"For simplicity we aren't distinguishing between SP0 and SPx for the current " +"EL exceptions, or between AArch32 and AArch64 for the lower EL exceptions." +msgstr "" + +#: src/bare-metal/aps/exceptions.md:67 +msgid "" +"For this example we just log the exception and power down, as we don't " +"expect any of them to actually happen." +msgstr "" + +#: src/bare-metal/aps/exceptions.md:69 +msgid "" +"We can think of exception handlers and our main execution context more or " +"less like different threads. [`Send` and `Sync`](../../concurrency/send-sync." +"md) will control what we can share between them, just like with threads. For " +"example, if we want to share some value between exception handlers and the " +"rest of the program, and it's `Send` but not `Sync`, then we'll need to wrap " +"it in something like a `Mutex` and put it in a static." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:3 +msgid "[oreboot](https://github.com/oreboot/oreboot)" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:4 +msgid "\"coreboot without the C\"" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:5 +msgid "Supports x86, aarch64 and RISC-V." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:6 +msgid "Relies on LinuxBoot rather than having many drivers itself." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:7 +msgid "" +"[Rust RaspberryPi OS tutorial](https://github.com/rust-embedded/rust-" +"raspberrypi-OS-tutorials)" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:8 +msgid "" +"Initialisation, UART driver, simple bootloader, JTAG, exception levels, " +"exception handling, page tables" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:10 +msgid "" +"Some dodginess around cache maintenance and initialisation in Rust, not " +"necessarily a good example to copy for production code." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:12 +msgid "[`cargo-call-stack`](https://crates.io/crates/cargo-call-stack)" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:13 +msgid "Static analysis to determine maximum stack usage." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:17 +msgid "" +"The RaspberryPi OS tutorial runs Rust code before the MMU and caches are " +"enabled. This will read and write memory (e.g. the stack). However:" +msgstr "" + +#: src/bare-metal/aps/other-projects.md:19 +msgid "" +"Without the MMU and cache, unaligned accesses will fault. It builds with " +"`aarch64-unknown-none` which sets `+strict-align` to prevent the compiler " +"generating unaligned accesses so it should be alright, but this is not " +"necessarily the case in general." +msgstr "" + +#: src/bare-metal/aps/other-projects.md:22 +msgid "" +"If it were running in a VM, this can lead to cache coherency issues. The " +"problem is that the VM is accessing memory directly with the cache disabled, " +"while the host has cacheable aliases to the same memory. Even if the host " +"doesn't explicitly access the memory, speculative accesses can lead to cache " +"fills, and then changes from one or the other will get lost. Again this is " +"alright in this particular case (running directly on the hardware with no " +"hypervisor), but isn't a good pattern in general." +msgstr "" + +#: src/bare-metal/useful-crates.md:3 +msgid "" +"We'll go over a few crates which solve some common problems in bare-metal " +"programming." +msgstr "" + +#: src/bare-metal/useful-crates/zerocopy.md:1 +msgid "`zerocopy`" +msgstr "" + +#: src/bare-metal/useful-crates/zerocopy.md:3 +msgid "" +"The [`zerocopy`](https://docs.rs/zerocopy/) crate (from Fuchsia) provides " +"traits and macros for safely converting between byte sequences and other " +"types." +msgstr "" + #: src/bare-metal/useful-crates/zerocopy.md:40 msgid "" "This is not suitable for MMIO (as it doesn't use volatile reads and writes), " @@ -17339,17 +17052,21 @@ msgid "Normal threads cannot borrow from their environment:" msgstr "常规线程不能从它们所处的环境中借用:" #: src/concurrency/scoped-threads.md:5 +#, fuzzy msgid "" "```rust,editable,compile_fail\n" "use std::thread;\n" "\n" -"fn main() {\n" +"fn foo() {\n" " let s = String::from(\"Hello\");\n" -"\n" " thread::spawn(|| {\n" " println!(\"Length: {}\", s.len());\n" " });\n" "}\n" +"\n" +"fn main() {\n" +" foo();\n" +"}\n" "```" msgstr "" "```rust,editable,compile_fail\n" @@ -17364,7 +17081,7 @@ msgstr "" "}\n" "```" -#: src/concurrency/scoped-threads.md:17 +#: src/concurrency/scoped-threads.md:20 msgid "" "However, you can use a [scoped thread](https://doc.rust-lang.org/std/thread/" "fn.scope.html) for this:" @@ -17372,7 +17089,7 @@ msgstr "" "不过,你可以使用[范围线程](https://doc.rust-lang.org/std/thread/fn.scope." "html)来实现此目的:" -#: src/concurrency/scoped-threads.md:19 +#: src/concurrency/scoped-threads.md:22 msgid "" "```rust,editable\n" "use std::thread;\n" @@ -17402,7 +17119,7 @@ msgstr "" "}\n" "```" -#: src/concurrency/scoped-threads.md:37 +#: src/concurrency/scoped-threads.md:40 msgid "" "The reason for that is that when the `thread::scope` function completes, all " "the threads are guaranteed to be joined, so they can return borrowed data." @@ -17410,7 +17127,7 @@ msgstr "" "其原因在于,在 `thread::scope` 函数完成后,可保证所有线程都已联结在一起,使得" "线程能够返回借用的数据。" -#: src/concurrency/scoped-threads.md:38 +#: src/concurrency/scoped-threads.md:41 msgid "" "Normal Rust borrowing rules apply: you can either borrow mutably by one " "thread, or immutably by any number of threads." @@ -17538,7 +17255,9 @@ msgstr "" "```" #: src/concurrency/channels/bounded.md:3 -msgid "Bounded and synchronous channels make `send` block the current thread:" +#, fuzzy +msgid "" +"With bounded (synchronous) channels, `send` can block the current thread:" msgstr "有边界的同步通道会使 `send` 阻塞当前线程:" #: src/concurrency/channels/bounded.md:5 @@ -17591,6 +17310,25 @@ msgstr "" "}\n" "```" +#: src/concurrency/channels/bounded.md:31 +msgid "" +"Calling `send` will block the current thread until there is space in the " +"channel for the new message. The thread can be blocked indefinitely if there " +"is nobody who reads from the channel." +msgstr "" + +#: src/concurrency/channels/bounded.md:32 +msgid "" +"A call to `send` will abort with an error (that is why it returns `Result`) " +"if the channel is closed. A channel is closed when the receiver is dropped." +msgstr "" + +#: src/concurrency/channels/bounded.md:33 +msgid "" +"A bounded channel with a size of zero is called a \"rendezvous channel\". " +"Every send will block the current thread until another thread calls `read`." +msgstr "" + #: src/concurrency/send-sync.md:1 msgid "`Send` and `Sync`" msgstr "`Send` 和 `Sync`" @@ -17824,7 +17562,7 @@ msgid "" "deallocate `T` when the last reference is dropped," msgstr "" "[`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html),对 `T` 进行原" -"子计数:用于处理线程之间的共享,并负责 在最后一个引用被丢弃时取消分配 `T`。" +"子计数:用于处理线程之间的共享,并负责 在最后一个引用被丢弃时取消分配 `T`," #: src/concurrency/shared_state.md:8 msgid "" @@ -17895,9 +17633,10 @@ msgid "" msgstr "`Arc` 代表“原子引用计数”,它是使用原子操作的 `Rc` 的 线程安全版本。" #: src/concurrency/shared_state/arc.md:31 +#, fuzzy msgid "" "`Arc` implements `Clone` whether or not `T` does. It implements `Send` " -"and `Sync` iff `T` implements them both." +"and `Sync` if and only if `T` implements them both." msgstr "" "无论 `T` 是否实现 `Clone`,`Arc` 都会实现 `Clone`。如果 `T` 实现了 `Send` " "和 `Sync`,`Arc` 便会 实现二者。" @@ -17998,7 +17737,10 @@ msgstr "" "`&mut T` 存在的时间不会比持有锁的时间更长。" #: src/concurrency/shared_state/mutex.md:35 -msgid "`Mutex` implements both `Send` and `Sync` iff `T` implements `Send`." +#, fuzzy +msgid "" +"`Mutex` implements both `Send` and `Sync` iff (if and only if) `T` " +"implements `Send`." msgstr "如果 `T` 实现了 `Send`,`Mutex` 便会实现 `Send` 和 `Sync`。" #: src/concurrency/shared_state/mutex.md:36 @@ -18007,7 +17749,7 @@ msgstr "读写锁版本 - `RwLock`。" #: src/concurrency/shared_state/mutex.md:37 msgid "Why does `lock()` return a `Result`? " -msgstr "为什么 `lock()` 会返回 `Result`?" +msgstr "为什么 `lock()` 会返回 `Result`? " #: src/concurrency/shared_state/mutex.md:38 msgid "" @@ -18219,7 +17961,7 @@ msgid "" "\n" " // Create philosophers\n" "\n" -" // Make them think and eat\n" +" // Make each of them think and eat 100 times\n" "\n" " // Output their thoughts\n" "}\n" @@ -18254,15 +17996,6 @@ msgid "" "reqwest/). Create a new Cargo project and `reqwest` it as a dependency with:" msgstr "" -#: src/exercises/concurrency/link-checker.md:11 -msgid "" -"```shell\n" -"$ cargo new link-checker\n" -"$ cd link-checker\n" -"$ cargo add --features blocking,rustls-tls reqwest\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:17 msgid "" "If `cargo add` fails with `error: no such subcommand`, then please edit the " @@ -18275,26 +18008,12 @@ msgid "" "rs/scraper/) for that:" msgstr "" -#: src/exercises/concurrency/link-checker.md:22 -msgid "" -"```shell\n" -"$ cargo add scraper\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:26 msgid "" "Finally, we'll need some way of handling errors. We use [`thiserror`]" "(https://docs.rs/thiserror/) for that:" msgstr "" -#: src/exercises/concurrency/link-checker.md:29 -msgid "" -"```shell\n" -"$ cargo add thiserror\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:33 msgid "" "The `cargo add` calls will update the `Cargo.toml` file to look like this:" @@ -18330,8 +18049,7 @@ msgstr "" #: src/exercises/concurrency/link-checker.md:57 msgid "" "```rust,compile_fail\n" -"use reqwest::blocking::{get, Response};\n" -"use reqwest::Url;\n" +"use reqwest::{blocking::Client, Url};\n" "use scraper::{Html, Selector};\n" "use thiserror::Error;\n" "\n" @@ -18339,34 +18057,57 @@ msgid "" "enum Error {\n" " #[error(\"request error: {0}\")]\n" " ReqwestError(#[from] reqwest::Error),\n" +" #[error(\"bad http response: {0}\")]\n" +" BadResponse(String),\n" +"}\n" +"\n" +"#[derive(Debug)]\n" +"struct CrawlCommand {\n" +" url: Url,\n" +" extract_links: bool,\n" "}\n" "\n" -"fn extract_links(response: Response) -> Result, Error> {\n" +"fn visit_page(client: &Client, command: &CrawlCommand) -> Result, " +"Error> {\n" +" println!(\"Checking {:#}\", command.url);\n" +" let response = client.get(command.url.clone()).send()?;\n" +" if !response.status().is_success() {\n" +" return Err(Error::BadResponse(response.status().to_string()));\n" +" }\n" +"\n" +" let mut link_urls = Vec::new();\n" +" if !command.extract_links {\n" +" return Ok(link_urls);\n" +" }\n" +"\n" " let base_url = response.url().to_owned();\n" -" let document = response.text()?;\n" -" let html = Html::parse_document(&document);\n" -" let selector = Selector::parse(\"a\").unwrap();\n" +" let body_text = response.text()?;\n" +" let document = Html::parse_document(&body_text);\n" "\n" -" let mut valid_urls = Vec::new();\n" -" for element in html.select(&selector) {\n" -" if let Some(href) = element.value().attr(\"href\") {\n" -" match base_url.join(href) {\n" -" Ok(url) => valid_urls.push(url),\n" -" Err(err) => {\n" -" println!(\"On {base_url}: could not parse {href:?}: " -"{err} (ignored)\",);\n" -" }\n" +" let selector = Selector::parse(\"a\").unwrap();\n" +" let href_values = document\n" +" .select(&selector)\n" +" .filter_map(|element| element.value().attr(\"href\"));\n" +" for href in href_values {\n" +" match base_url.join(href) {\n" +" Ok(link_url) => {\n" +" link_urls.push(link_url);\n" +" }\n" +" Err(err) => {\n" +" println!(\"On {base_url:#}: ignored unparsable {href:?}: " +"{err}\");\n" " }\n" " }\n" " }\n" -"\n" -" Ok(valid_urls)\n" +" Ok(link_urls)\n" "}\n" "\n" "fn main() {\n" +" let client = Client::new();\n" " let start_url = Url::parse(\"https://www.google.org\").unwrap();\n" -" let response = get(start_url).unwrap();\n" -" match extract_links(response) {\n" +" let crawl_command = CrawlCommand{ url: start_url, extract_links: " +"true };\n" +" match visit_page(&client, &crawl_command) {\n" " Ok(links) => println!(\"Links: {links:#?}\"),\n" " Err(err) => println!(\"Could not extract links: {err:#}\"),\n" " }\n" @@ -18374,24 +18115,17 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/link-checker.md:100 +#: src/exercises/concurrency/link-checker.md:120 msgid "Run the code in `src/main.rs` with" msgstr "" -#: src/exercises/concurrency/link-checker.md:102 -msgid "" -"```shell\n" -"$ cargo run\n" -"```" -msgstr "" - -#: src/exercises/concurrency/link-checker.md:108 +#: src/exercises/concurrency/link-checker.md:128 msgid "" "Use threads to check the links in parallel: send the URLs to be checked to a " "channel and let a few threads check the URLs in parallel." msgstr "" -#: src/exercises/concurrency/link-checker.md:110 +#: src/exercises/concurrency/link-checker.md:130 msgid "" "Extend this to recursively extract links from all pages on the `www.google." "org` domain. Put an upper limit of 100 pages or so so that you don't end up " @@ -18494,7 +18228,7 @@ msgstr "在 `main` 中使用 `let future: () = async_main(10);` 来查看类型 msgid "" "The \"async\" keyword is syntactic sugar. The compiler replaces the return " "type with a future. " -msgstr "\"async\" 关键字是语法糖。编译器会将返回类型替换为 future。" +msgstr "\"async\" 关键字是语法糖。编译器会将返回类型替换为 future。 " #: src/async/async-await.md:36 msgid "" @@ -18534,25 +18268,6 @@ msgid "" "doc.rust-lang.org/std/task/enum.Poll.html)." msgstr "" -#: src/async/futures.md:8 -msgid "" -"```rust\n" -"use std::pin::Pin;\n" -"use std::task::Context;\n" -"\n" -"pub trait Future {\n" -" type Output;\n" -" fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll;\n" -"}\n" -"\n" -"pub enum Poll {\n" -" Ready(T),\n" -" Pending,\n" -"}\n" -"```" -msgstr "" - #: src/async/futures.md:23 msgid "" "An async function returns an `impl Future`. It's also possible (but " @@ -18601,19 +18316,19 @@ msgstr "" #: src/async/runtimes.md:7 msgid "" -"[Tokio](https://tokio.rs/) - performant, with a well-developed ecosystem of " +"[Tokio](https://tokio.rs/): performant, with a well-developed ecosystem of " "functionality like [Hyper](https://hyper.rs/) for HTTP or [Tonic](https://" "github.com/hyperium/tonic) for gRPC." msgstr "" #: src/async/runtimes.md:10 msgid "" -"[async-std](https://async.rs/) - aims to be a \"std for async\", and " -"includes a basic runtime in `async::task`." +"[async-std](https://async.rs/): aims to be a \"std for async\", and includes " +"a basic runtime in `async::task`." msgstr "" #: src/async/runtimes.md:12 -msgid "[smol](https://docs.rs/smol/latest/smol/) - simple and lightweight" +msgid "[smol](https://docs.rs/smol/latest/smol/): simple and lightweight" msgstr "" #: src/async/runtimes.md:14 @@ -18710,12 +18425,10 @@ msgid "Try awaiting the task returned from `tokio::spawn`." msgstr "" #: src/async/tasks.md:3 -msgid "" -"Runtimes have the concept of a \"task\", similar to a thread but much less " -"resource-intensive." +msgid "Rust has a task system, which is a form of lightweight threading." msgstr "" -#: src/async/tasks.md:6 +#: src/async/tasks.md:5 msgid "" "A task has a single top-level future which the executor polls to make " "progress. That future may have one or more nested futures that its `poll` " @@ -18724,7 +18437,7 @@ msgid "" "and an I/O operation." msgstr "" -#: src/async/tasks.md:11 +#: src/async/tasks.md:10 msgid "" "```rust,compile_fail\n" "use tokio::io::{self, AsyncReadExt, AsyncWriteExt};\n" @@ -18768,25 +18481,25 @@ msgid "" "```" msgstr "" -#: src/async/tasks.md:53 src/async/control-flow/join.md:36 +#: src/async/tasks.md:52 src/async/control-flow/join.md:36 msgid "" "Copy this example into your prepared `src/main.rs` and run it from there." msgstr "" -#: src/async/tasks.md:55 +#: src/async/tasks.md:54 msgid "" "Ask students to visualize what the state of the example server would be with " "a few connected clients. What tasks exist? What are their Futures?" msgstr "" -#: src/async/tasks.md:58 +#: src/async/tasks.md:57 msgid "" "This is the first time we've seen an `async` block. This is similar to a " "closure, but does not take any arguments. Its return value is a Future, " "similar to an `async fn`. " msgstr "" -#: src/async/tasks.md:62 +#: src/async/tasks.md:61 msgid "" "Refactor the async block into a function, and improve the error handling " "using `?`." @@ -18794,8 +18507,7 @@ msgstr "" #: src/async/channels.md:3 msgid "" -"Several crates have support for `async`/`await`. For instance `tokio` " -"channels:" +"Several crates have support for asynchronous channels. For instance `tokio`:" msgstr "" #: src/async/channels.md:5 @@ -18823,7 +18535,7 @@ msgid "" " println!(\"Sent {} pings so far.\", i + 1);\n" " }\n" "\n" -" std::mem::drop(sender);\n" +" drop(sender);\n" " ping_handler_task.await.expect(\"Something went wrong in ping handler " "task.\");\n" "}\n" @@ -18946,12 +18658,13 @@ msgstr "" #: src/async/control-flow/select.md:8 msgid "" -"This is usually a macro, similar to match, with each arm of the form " -"`pattern = future => statement`. When the future is ready, the statement is " -"executed with the variable bound to the future's result." +"Similar to a match statement, the body of `select!` has a number of arms, " +"each of the form `pattern = future => statement`. When the `future` is " +"ready, the `statement` is executed with the variables in `pattern` bound to " +"the `future`'s result." msgstr "" -#: src/async/control-flow/select.md:12 +#: src/async/control-flow/select.md:13 msgid "" "```rust,editable,compile_fail\n" "use tokio::sync::mpsc::{self, Receiver};\n" @@ -19001,32 +18714,36 @@ msgid "" "```" msgstr "" -#: src/async/control-flow/select.md:61 +#: src/async/control-flow/select.md:62 msgid "" "In this example, we have a race between a cat and a dog. " "`first_animal_to_finish_race` listens to both channels and will pick " "whichever arrives first. Since the dog takes 50ms, it wins against the cat " -"that take 500ms seconds." +"that take 500ms." msgstr "" -#: src/async/control-flow/select.md:66 +#: src/async/control-flow/select.md:67 msgid "" "You can use `oneshot` channels in this example as the channels are supposed " "to receive only one `send`." msgstr "" -#: src/async/control-flow/select.md:69 +#: src/async/control-flow/select.md:70 msgid "" "Try adding a deadline to the race, demonstrating selecting different sorts " "of futures." msgstr "" -#: src/async/control-flow/select.md:72 +#: src/async/control-flow/select.md:73 msgid "" -"Note that `select!` moves the values it is given. It is easiest to use when " -"every execution of `select!` creates new futures. An alternative is to pass " -"`&mut future` instead of the future itself, but this can lead to issues, " -"further discussed in the pinning slide." +"Note that `select!` drops unmatched branches, which cancels their futures. " +"It is easiest to use when every execution of `select!` creates new futures." +msgstr "" + +#: src/async/control-flow/select.md:76 +msgid "" +"An alternative is to pass `&mut future` instead of the future itself, but " +"this can lead to issues, further discussed in the pinning slide." msgstr "" #: src/async/pitfalls.md:1 @@ -19050,7 +18767,11 @@ msgid "[Pin](pitfalls/pin.md)" msgstr "" #: src/async/pitfalls.md:7 -msgid "[Async Traits](pitfall/async-traits.md)" +msgid "[Async Traits](pitfalls/async-traits.md)" +msgstr "" + +#: src/async/pitfalls.md:8 +msgid "[Cancellation](pitfalls/cancellation.md)" msgstr "" #: src/async/pitfalls/blocking-executor.md:1 @@ -19223,19 +18944,6 @@ msgid "" "Instead, add a `timeout_fut` containing that future outside of the `loop`:" msgstr "" -#: src/async/pitfalls/pin.md:79 -msgid "" -"```rust,compile_fail\n" -"let mut timeout_fut = sleep(Duration::from_millis(100));\n" -"loop {\n" -" select! {\n" -" ..,\n" -" _ = timeout_fut => { println!(..); },\n" -" }\n" -"}\n" -"```" -msgstr "" - #: src/async/pitfalls/pin.md:88 msgid "" "This still doesn't work. Follow the compiler errors, adding `&mut` to the " @@ -19243,19 +18951,6 @@ msgid "" "pin`:" msgstr "" -#: src/async/pitfalls/pin.md:92 -msgid "" -"```rust,compile_fail\n" -"let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100)));\n" -"loop {\n" -" select! {\n" -" ..,\n" -" _ = &mut timeout_fut => { println!(..); },\n" -" }\n" -"}\n" -"```" -msgstr "" - #: src/async/pitfalls/pin.md:102 msgid "" "This compiles, but once the timeout expires it is `Poll::Ready` on every " @@ -19350,7 +19045,10 @@ msgid "" "blog/2019/10/26/async-fn-in-traits-are-hard/) if you are interested in " "digging deeper." msgstr "" -"对于 `async trait` 的语言支持中的挑战是深入 Rust的,并且可能不值得深入描述。如果您对深入了解感兴趣,Niko Matsakis 在[这篇文章](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/)中对它们做了很好的解释。" +"对于 `async trait` 的语言支持中的挑战是深入 Rust的,并且可能不值得深入描述。" +"如果您对深入了解感兴趣,Niko Matsakis 在[这篇文章](https://" +"smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-" +"hard/)中对它们做了很好的解释。" #: src/async/pitfalls/async-traits.md:60 msgid "" @@ -19359,37 +19057,192 @@ msgid "" msgstr "" "尝试创建一个新的 sleeper 结构,使其随机休眠一段时间,并将其添加到 Vec 中。" -#: src/exercises/concurrency/afternoon.md:3 +#: src/async/pitfalls/cancellation.md:3 msgid "" -"To practice your Async Rust skills, we have again two exercises for you:" +"Dropping a future implies it can never be polled again. This is called " +"_cancellation_ and it can occur at any `await` point. Care is needed to " +"ensure the system works correctly even when futures are cancelled. For " +"example, it shouldn't deadlock or lose data." msgstr "" -"为了练习您的异步 Rust 技能,我们再次为您提供了两个练习:" -#: src/exercises/concurrency/afternoon.md:5 +#: src/async/pitfalls/cancellation.md:8 msgid "" -"Dining philosophers: we already saw this problem in the morning. This time " -"you are going to implement it with Async Rust." +"```rust,editable,compile_fail\n" +"use std::io::{self, ErrorKind};\n" +"use std::time::Duration;\n" +"use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream};\n" +"\n" +"struct LinesReader {\n" +" stream: DuplexStream,\n" +"}\n" +"\n" +"impl LinesReader {\n" +" fn new(stream: DuplexStream) -> Self {\n" +" Self { stream }\n" +" }\n" +"\n" +" async fn next(&mut self) -> io::Result> {\n" +" let mut bytes = Vec::new();\n" +" let mut buf = [0];\n" +" while self.stream.read(&mut buf[..]).await? != 0 {\n" +" bytes.push(buf[0]);\n" +" if buf[0] == b'\\n' {\n" +" break;\n" +" }\n" +" }\n" +" if bytes.is_empty() {\n" +" return Ok(None)\n" +" }\n" +" let s = String::from_utf8(bytes)\n" +" .map_err(|_| io::Error::new(ErrorKind::InvalidData, \"not " +"UTF-8\"))?;\n" +" Ok(Some(s))\n" +" }\n" +"}\n" +"\n" +"async fn slow_copy(source: String, mut dest: DuplexStream) -> std::io::" +"Result<()> {\n" +" for b in source.bytes() {\n" +" dest.write_u8(b).await?;\n" +" tokio::time::sleep(Duration::from_millis(10)).await\n" +" }\n" +" Ok(())\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() -> std::io::Result<()> {\n" +" let (client, server) = tokio::io::duplex(5);\n" +" let handle = tokio::spawn(slow_copy(\"hi\\nthere\\n\".to_owned(), " +"client));\n" +"\n" +" let mut lines = LinesReader::new(server);\n" +" let mut interval = tokio::time::interval(Duration::from_millis(60));\n" +" loop {\n" +" tokio::select! {\n" +" _ = interval.tick() => println!(\"tick!\"),\n" +" line = lines.next() => if let Some(l) = line? {\n" +" print!(\"{}\", l)\n" +" } else {\n" +" break\n" +" },\n" +" }\n" +" }\n" +" handle.await.unwrap()?;\n" +" Ok(())\n" +"}\n" +"```" msgstr "" -"哲学家进餐:我们已经在上午看到了这个问题。这次你将使用异步 Rust 来实现它。" -#: src/exercises/concurrency/afternoon.md:8 +#: src/async/pitfalls/cancellation.md:72 msgid "" -"A Broadcast Chat Application: this is a larger project that allows you " -"experiment with more advanced Async Rust features." +"The compiler doesn't help with cancellation-safety. You need to read API " +"documentation and consider what state your `async fn` holds." msgstr "" -"广播聊天应用:这是一个更大的项目,允许您尝试更高级的异步Rust功能。" - -#: src/exercises/concurrency/dining-philosophers-async.md:1 -#: src/exercises/concurrency/solutions-afternoon.md:3 -msgid "Dining Philosophers - Async" -msgstr "哲学家进餐 - 异步" -#: src/exercises/concurrency/dining-philosophers-async.md:3 +#: src/async/pitfalls/cancellation.md:75 +msgid "" +"Unlike `panic` and `?`, cancellation is part of normal control flow (vs " +"error-handling)." +msgstr "" + +#: src/async/pitfalls/cancellation.md:78 +msgid "The example loses parts of the string." +msgstr "" + +#: src/async/pitfalls/cancellation.md:80 +msgid "" +"Whenever the `tick()` branch finishes first, `next()` and its `buf` are " +"dropped." +msgstr "" + +#: src/async/pitfalls/cancellation.md:82 +msgid "" +"`LinesReader` can be made cancellation-safe by making `buf` part of the " +"struct:" +msgstr "" + +#: src/async/pitfalls/cancellation.md:83 +msgid "" +"```rust,compile_fail\n" +"struct LinesReader {\n" +" stream: DuplexStream,\n" +" bytes: Vec,\n" +" buf: [u8; 1],\n" +"}\n" +"\n" +"impl LinesReader {\n" +" fn new(stream: DuplexStream) -> Self {\n" +" Self { stream, bytes: Vec::new(), buf: [0] }\n" +" }\n" +" async fn next(&mut self) -> io::Result> {\n" +" // prefix buf and bytes with self.\n" +" // ...\n" +" let raw = std::mem::take(&mut self.bytes);\n" +" let s = String::from_utf8(raw)\n" +" // ...\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/async/pitfalls/cancellation.md:104 +#, fuzzy +msgid "" +"[`Interval::tick`](https://docs.rs/tokio/latest/tokio/time/struct.Interval." +"html#method.tick) is cancellation-safe because it keeps track of whether a " +"tick has been 'delivered'." +msgstr "" +"[BufReader::read_line()](https://docs.rs/tokio/latest/tokio/io/struct.Lines." +"html#method.next_line):用于从标准输入异步读取用户消息。" + +#: src/async/pitfalls/cancellation.md:107 +#, fuzzy +msgid "" +"[`AsyncReadExt::read`](https://docs.rs/tokio/latest/tokio/io/trait." +"AsyncReadExt.html#method.read) is cancellation-safe because it either " +"returns or doesn't read data." +msgstr "" +"[BufReader::read_line()](https://docs.rs/tokio/latest/tokio/io/struct.Lines." +"html#method.next_line):用于从标准输入异步读取用户消息。" + +#: src/async/pitfalls/cancellation.md:110 +#, fuzzy +msgid "" +"[`AsyncBufReadExt::read_line`](https://docs.rs/tokio/latest/tokio/io/trait." +"AsyncBufReadExt.html#method.read_line) is similar to the example and _isn't_ " +"cancellation-safe. See its documentation for details and alternatives." +msgstr "" +"[BufReader::read_line()](https://docs.rs/tokio/latest/tokio/io/struct.Lines." +"html#method.next_line):用于从标准输入异步读取用户消息。" + +#: src/exercises/concurrency/afternoon.md:3 +msgid "" +"To practice your Async Rust skills, we have again two exercises for you:" +msgstr "为了练习您的异步 Rust 技能,我们再次为您提供了两个练习:" + +#: src/exercises/concurrency/afternoon.md:5 +msgid "" +"Dining philosophers: we already saw this problem in the morning. This time " +"you are going to implement it with Async Rust." +msgstr "" +"哲学家进餐:我们已经在上午看到了这个问题。这次你将使用异步 Rust 来实现它。" + +#: src/exercises/concurrency/afternoon.md:8 +msgid "" +"A Broadcast Chat Application: this is a larger project that allows you " +"experiment with more advanced Async Rust features." +msgstr "广播聊天应用:这是一个更大的项目,允许您尝试更高级的异步Rust功能。" + +#: src/exercises/concurrency/dining-philosophers-async.md:1 +#: src/exercises/concurrency/solutions-afternoon.md:3 +msgid "Dining Philosophers - Async" +msgstr "哲学家进餐 - 异步" + +#: src/exercises/concurrency/dining-philosophers-async.md:3 msgid "" "See [dining philosophers](dining-philosophers.md) for a description of the " "problem." -msgstr "" -"查看[哲学家进餐](dining-philosophers.md)以获取问题的描述。" +msgstr "查看[哲学家进餐](dining-philosophers.md)以获取问题的描述。" #: src/exercises/concurrency/dining-philosophers-async.md:6 msgid "" @@ -19397,8 +19250,9 @@ msgid "" "locally.md) for this exercise. Copy the code below to a file called `src/" "main.rs`, fill out the blanks, and test that `cargo run` does not deadlock:" msgstr "" -"与之前一样,您需要一个本地的 [Cargo 安装](../../cargo/running-locally.md)来进行这个练习。将下面的代码复制到一个名为 `src/main.rs` 的文件中,填写空白部分,并测试确保 `cargo run` 不会死锁:" - +"与之前一样,您需要一个本地的 [Cargo 安装](../../cargo/running-locally.md)来进" +"行这个练习。将下面的代码复制到一个名为 `src/main.rs` 的文件中,填写空白部分," +"并测试确保 `cargo run` 不会死锁:" #: src/exercises/concurrency/dining-philosophers-async.md:13 msgid "" @@ -19453,7 +19307,8 @@ msgid "" "Since this time you are using Async Rust, you'll need a `tokio` dependency. " "You can use the following `Cargo.toml`:" msgstr "" -"因为这次您正在使用异步Rust,您将需要一个 `tokio` 依赖。您可以使用以下的 `Cargo.toml`:" +"因为这次您正在使用异步Rust,您将需要一个 `tokio` 依赖。您可以使用以下的 " +"`Cargo.toml`:" #: src/exercises/concurrency/dining-philosophers-async.md:62 msgid "" @@ -19473,12 +19328,11 @@ msgstr "" msgid "" "Also note that this time you have to use the `Mutex` and the `mpsc` module " "from the `tokio` crate." -msgstr "" -"另外,请注意,这次您必须使用来自 `tokio` 包的 `Mutex` 和 `mpsc` 模块。" +msgstr "另外,请注意,这次您必须使用来自 `tokio` 包的 `Mutex` 和 `mpsc` 模块。" #: src/exercises/concurrency/dining-philosophers-async.md:77 msgid "Can you make your implementation single-threaded? " -msgstr "您可以使您的实现为单线程吗?" +msgstr "您可以使您的实现为单线程吗? " #: src/exercises/concurrency/chat-app.md:3 msgid "" @@ -19488,16 +19342,22 @@ msgid "" "input, and sends them to the server. The chat server broadcasts each message " "that it receives to all the clients." msgstr "" -"在本练习中,我们想要使用我们的新知识来实现一个广播聊天应用。我们有一个聊天服务器,客户端连接到该服务器并发布他们的消息。客户端从标准输入读取用户消息,并将其发送到服务器。聊天服务器将收到的每条消息广播给所有客户端。" +"在本练习中,我们想要使用我们的新知识来实现一个广播聊天应用。我们有一个聊天服" +"务器,客户端连接到该服务器并发布他们的消息。客户端从标准输入读取用户消息,并" +"将其发送到服务器。聊天服务器将收到的每条消息广播给所有客户端。" #: src/exercises/concurrency/chat-app.md:9 +#, fuzzy msgid "" "For this, we use [a broadcast channel](https://docs.rs/tokio/latest/tokio/" "sync/broadcast/fn.channel.html) on the server, and [`tokio_websockets`]" -"(https://docs.rs/tokio-websockets/0.3.2/tokio_websockets/) for the " +"(https://docs.rs/tokio-websockets/0.4.0/tokio_websockets/) for the " "communication between the client and the server." msgstr "" -"为此,我们在服务器上使用一个[广播 channel](https://docs.rs/tokio/latest/tokio/sync/broadcast/fn.channel.html),并使用[`tokio_websockets`](https://docs.rs/tokio-websockets/0.3.2/tokio_websockets/) 来进行客户端与服务器之间的通信。" +"为此,我们在服务器上使用一个[广播 channel](https://docs.rs/tokio/latest/" +"tokio/sync/broadcast/fn.channel.html),并使用[`tokio_websockets`](https://" +"docs.rs/tokio-websockets/0.3.2/tokio_websockets/) 来进行客户端与服务器之间的" +"通信。" #: src/exercises/concurrency/chat-app.md:13 msgid "Create a new Cargo project and add the following dependencies:" @@ -19516,10 +19376,11 @@ msgid "" "edition = \"2021\"\n" "\n" "[dependencies]\n" -"futures-util = \"0.3.28\"\n" +"futures-util = { version = \"0.3.28\", features = [\"sink\"] }\n" "http = \"0.2.9\"\n" "tokio = { version = \"1.28.1\", features = [\"full\"] }\n" -"tokio-websockets = \"0.3.2\"\n" +"tokio-websockets = { version = \"0.4.0\", features = [\"client\", " +"\"fastrand\", \"server\", \"sha1_smol\"] }\n" "```" msgstr "" @@ -19528,23 +19389,26 @@ msgid "The required APIs" msgstr "所需的API" #: src/exercises/concurrency/chat-app.md:33 +#, fuzzy msgid "" "You are going to need the following functions from `tokio` and " -"[`tokio_websockets`](https://docs.rs/tokio-websockets/0.3.2/" +"[`tokio_websockets`](https://docs.rs/tokio-websockets/0.4.0/" "tokio_websockets/). Spend a few minutes to familiarize yourself with the " "API. " msgstr "" -"您将需要来自 `tokio` 和 [`tokio_websockets`](https://docs.rs/tokio-websockets/0.3.2/tokio_websockets/) 的以下函数。请花几分钟时间熟悉这些 API。" - +"您将需要来自 `tokio` 和 [`tokio_websockets`](https://docs.rs/tokio-" +"websockets/0.3.2/tokio_websockets/) 的以下函数。请花几分钟时间熟悉这些 API。" #: src/exercises/concurrency/chat-app.md:37 +#, fuzzy msgid "" -"[WebsocketStream::next()](https://docs.rs/tokio-websockets/0.3.2/" -"tokio_websockets/proto/struct.WebsocketStream.html#method.next): for " +"[StreamExt::next()](https://docs.rs/futures-util/0.3.28/futures_util/stream/" +"trait.StreamExt.html#method.next) implemented by `WebsocketStream`: for " "asynchronously reading messages from a Websocket Stream." msgstr "" -"[WebsocketStream::next()](https://docs.rs/tokio-websockets/0.3.2/" -"tokio_websockets/proto/struct.WebsocketStream.html#method.next):用于从Websocket流异步读取消息。" +"[SinkExt::send()](https://docs.rs/futures-util/0.3.28/futures_util/sink/" +"trait.SinkExt.html#method.send) 由`WebsocketStream`实现:用于在Websocket流上" +"异步发送消息。" #: src/exercises/concurrency/chat-app.md:39 msgid "" @@ -19553,11 +19417,13 @@ msgid "" "asynchronously sending messages on a Websocket Stream." msgstr "" "[SinkExt::send()](https://docs.rs/futures-util/0.3.28/futures_util/sink/" -"trait.SinkExt.html#method.send) 由`WebsocketStream`实现:用于在Websocket流上异步发送消息。" +"trait.SinkExt.html#method.send) 由`WebsocketStream`实现:用于在Websocket流上" +"异步发送消息。" #: src/exercises/concurrency/chat-app.md:41 +#, fuzzy msgid "" -"[BufReader::read_line()](https://docs.rs/tokio/latest/tokio/io/struct.Lines." +"[Lines::next_line()](https://docs.rs/tokio/latest/tokio/io/struct.Lines." "html#method.next_line): for asynchronously reading user messages from the " "standard input." msgstr "" @@ -19586,17 +19452,24 @@ msgid "" "(see the [documentation](https://doc.rust-lang.org/cargo/reference/cargo-" "targets.html#binaries)). " msgstr "" -"通常在一个Cargo项目中,你只能有一个二进制文件,和一个`src/main.rs`文件。在这个项目中,我们需要两个二进制文件。一个用于客户端,另一个用于服务器。你可能会考虑将它们制作成两个单独的Cargo项目,但我们将它们放在一个包含两个二进制文件的Cargo项目中。为了使这个工作,客户端和服务器的代码应该放在`src/bin`下(参见[文档](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries))。" +"通常在一个Cargo项目中,你只能有一个二进制文件,和一个`src/main.rs`文件。在这" +"个项目中,我们需要两个二进制文件。一个用于客户端,另一个用于服务器。你可能会" +"考虑将它们制作成两个单独的Cargo项目,但我们将它们放在一个包含两个二进制文件的" +"Cargo项目中。为了使这个工作,客户端和服务器的代码应该放在`src/bin`下(参见[文" +"档](https://doc.rust-lang.org/cargo/reference/cargo-targets." +"html#binaries))。 " #: src/exercises/concurrency/chat-app.md:55 msgid "" "Copy the following server and client code into `src/bin/server.rs` and `src/" "bin/client.rs`, respectively. Your task is to complete these files as " "described below. " -msgstr "将以下服务器和客户端代码分别复制到 `src/bin/server.rs` 和 `src/bin/client.rs` 中。您的任务是按照下面的描述完成这些文件。" +msgstr "" +"将以下服务器和客户端代码分别复制到 `src/bin/server.rs` 和 `src/bin/client." +"rs` 中。您的任务是按照下面的描述完成这些文件。 " #: src/exercises/concurrency/chat-app.md:59 -#: src/exercises/concurrency/solutions-afternoon.md:117 +#: src/exercises/concurrency/solutions-afternoon.md:99 msgid "`src/bin/server.rs`:" msgstr "`src/bin/server.rs`:" @@ -19604,6 +19477,7 @@ msgstr "`src/bin/server.rs`:" msgid "" "```rust,compile_fail\n" "use futures_util::sink::SinkExt;\n" +"use futures_util::stream::StreamExt;\n" "use std::error::Error;\n" "use std::net::SocketAddr;\n" "use tokio::net::{TcpListener, TcpStream};\n" @@ -19642,14 +19516,15 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/chat-app.md:102 -#: src/exercises/concurrency/solutions-afternoon.md:202 +#: src/exercises/concurrency/chat-app.md:103 +#: src/exercises/concurrency/solutions-afternoon.md:166 msgid "`src/bin/client.rs`:" msgstr "`src/bin/client.rs`:" -#: src/exercises/concurrency/chat-app.md:106 +#: src/exercises/concurrency/chat-app.md:107 msgid "" "```rust,compile_fail\n" +"use futures_util::stream::StreamExt;\n" "use futures_util::SinkExt;\n" "use http::Uri;\n" "use tokio::io::{AsyncBufReadExt, BufReader};\n" @@ -19657,13 +19532,13 @@ msgid "" "\n" "#[tokio::main]\n" "async fn main() -> Result<(), tokio_websockets::Error> {\n" -" let mut ws_stream = ClientBuilder::from_uri(Uri::" -"from_static(\"ws://127.0.0.1:2000\"))\n" -" .connect()\n" -" .await?;\n" +" let (mut ws_stream, _) =\n" +" ClientBuilder::from_uri(Uri::from_static(\"ws://127.0.0.1:2000\"))\n" +" .connect()\n" +" .await?;\n" "\n" " let stdin = tokio::io::stdin();\n" -" let mut stdin = BufReader::new(stdin);\n" +" let mut stdin = BufReader::new(stdin).lines();\n" "\n" "\n" " // TODO: For a hint, see the description of the task below.\n" @@ -19672,69 +19547,60 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/chat-app.md:127 +#: src/exercises/concurrency/chat-app.md:130 msgid "Running the binaries" msgstr "运行可执行文件" -#: src/exercises/concurrency/chat-app.md:128 +#: src/exercises/concurrency/chat-app.md:131 msgid "Run the server with:" msgstr "使用以下命令运行服务器:" -#: src/exercises/concurrency/chat-app.md:130 -msgid "" -"```shell\n" -"$ cargo run --bin server\n" -"```" -msgstr "" - -#: src/exercises/concurrency/chat-app.md:134 +#: src/exercises/concurrency/chat-app.md:137 msgid "and the client with:" msgstr "" -#: src/exercises/concurrency/chat-app.md:136 -msgid "" -"```shell\n" -"$ cargo run --bin client\n" -"```" -msgstr "" - -#: src/exercises/concurrency/chat-app.md:142 +#: src/exercises/concurrency/chat-app.md:145 msgid "Implement the `handle_connection` function in `src/bin/server.rs`." msgstr "在 `src/bin/server.rs` 中实现 `handle_connection` 函数。" -#: src/exercises/concurrency/chat-app.md:143 +#: src/exercises/concurrency/chat-app.md:146 msgid "" "Hint: Use `tokio::select!` for concurrently performing two tasks in a " "continuous loop. One task receives messages from the client and broadcasts " "them. The other sends messages received by the server to the client." -msgstr "提示:使用 `tokio::select!` 在一个连续的循环中并发执行两个任务。一个任务从客户端接收消息并广播它们。另一个任务将服务器接收到的消息发送给客户端。" +msgstr "" +"提示:使用 `tokio::select!` 在一个连续的循环中并发执行两个任务。一个任务从客" +"户端接收消息并广播它们。另一个任务将服务器接收到的消息发送给客户端。" -#: src/exercises/concurrency/chat-app.md:146 +#: src/exercises/concurrency/chat-app.md:149 msgid "Complete the main function in `src/bin/client.rs`." msgstr "完成 `src/bin/client.rs` 中的 `main` 函数。" -#: src/exercises/concurrency/chat-app.md:147 +#: src/exercises/concurrency/chat-app.md:150 +#, fuzzy msgid "" "Hint: As before, use `tokio::select!` in a continuous loop for concurrently " "performing two tasks: (1) reading user messages from standard input and " -"sending them to the server, and (2) receiving messages from the server and " +"sending them to the server, and (2) receiving messages from the server, and " "displaying them for the user." msgstr "" -"提示:与之前一样,使用 `tokio::select!` 在一个连续的循环中并发执行两个任务:(1) 从标准输入读取用户消息并发送给服务器,以及 (2) 从服务器接收消息并显示给用户。" +"提示:与之前一样,使用 `tokio::select!` 在一个连续的循环中并发执行两个任务:" +"(1) 从标准输入读取用户消息并发送给服务器,以及 (2) 从服务器接收消息并显示给用" +"户。" -#: src/exercises/concurrency/chat-app.md:151 +#: src/exercises/concurrency/chat-app.md:154 +#, fuzzy msgid "" "Optional: Once you are done, change the code to broadcast messages to all " -"clients except the sender of the message." -msgstr "" -"可选:完成后,将代码更改为将消息广播给除消息发送者以外的所有客户端。" +"clients, but the sender of the message." +msgstr "可选:完成后,将代码更改为将消息广播给除消息发送者以外的所有客户端。" #: src/thanks.md:3 msgid "" "_Thank you for taking Comprehensive Rust 🦀!_ We hope you enjoyed it and " -"found it useful." +"that it was useful." msgstr "" -"_感谢您参与学习 Comprehensive Rust 🦀!_ 希望您喜欢并且觉得它有用。" +"感谢您阅读 _《Comprehensive Rust》🦀!_ 我们希望您喜欢它,并希望它对您有用。" #: src/thanks.md:6 msgid "" @@ -19743,6 +19609,9 @@ msgid "" "please get in [contact with us on GitHub](https://github.com/google/" "comprehensive-rust/discussions). We would love to hear from you." msgstr "" +"我们在编写课程的过程中收获了很多乐趣。课程并不完美,如果您发现任何错误或有改" +"进意见,请与我们联系[在 GitHub 上与我们联系](https://github.com/google/" +"comprehensive-rust/discussions)。我们很乐意听到您的意见。" #: src/other-resources.md:1 msgid "Other Rust Resources" @@ -19768,6 +19637,8 @@ msgid "" "canonical free book about Rust. Covers the language in detail and includes a " "few projects for people to build." msgstr "" +"[The Rust Programming Language](https://doc.rust-lang.org/book/):关于 Rust " +"的经典免费书籍。该书详细介绍了 Rust 语言,并包含一些供人们构建的项目。" #: src/other-resources.md:13 msgid "" @@ -19776,22 +19647,29 @@ msgid "" "Sometimes includes small exercises where you are asked to expand on the code " "in the examples." msgstr "" +"[Rust By Example](https://doc.rust-lang.org/rust-by-example/):通过一系列展示" +"不同结构的示例介绍 Rust 语法。有时还包括一些小练习,要求你对示例中的代码进行" +"扩展。" #: src/other-resources.md:17 msgid "" "[Rust Standard Library](https://doc.rust-lang.org/std/): full documentation " "of the standard library for Rust." msgstr "" +"[Rust Standard Library](https://doc.rust-lang.org/std/):Rust 标准库的完整文" +"档。" #: src/other-resources.md:19 msgid "" "[The Rust Reference](https://doc.rust-lang.org/reference/): an incomplete " "book which describes the Rust grammar and memory model." msgstr "" +"[The Rust Reference](https://doc.rust-lang.org/reference/):这是一本介绍 " +"Rust 语法和内存模型的书籍,目前还没有完结。" #: src/other-resources.md:22 msgid "More specialized guides hosted on the official Rust site:" -msgstr "" +msgstr "更多专业指南请访问 Rust 官方网站:" #: src/other-resources.md:24 msgid "" @@ -19799,6 +19677,8 @@ msgid "" "including working with raw pointers and interfacing with other languages " "(FFI)." msgstr "" +"[The Rustonomicon](https://doc.rust-lang.org/nomicon/):涵盖不安全的 Rust,包" +"括使用原始指针和与其他语言的接口(FFI)。" #: src/other-resources.md:27 msgid "" @@ -19806,6 +19686,8 @@ msgid "" "covers the new asynchronous programming model which was introduced after the " "Rust Book was written." msgstr "" +"[Asynchronous Programming in Rust](https://rust-lang.github.io/async-book/):" +"涵盖新的异步编程模型,该模型在Rust Book撰写后推出。" #: src/other-resources.md:30 msgid "" @@ -19813,6 +19695,8 @@ msgid "" "an introduction to using Rust on embedded devices without an operating " "system." msgstr "" +"[The Embedded Rust Book](https://doc.rust-lang.org/stable/embedded-book/):介" +"绍如何在没有操作系统的嵌入式设备上使用 Rust。" #: src/other-resources.md:33 msgid "Unofficial Learning Material" @@ -19827,6 +19711,8 @@ msgid "" "[Learn Rust the Dangerous Way](http://cliffle.com/p/dangerust/): covers Rust " "from the perspective of low-level C programmers." msgstr "" +"[Learn Rust the Dangerous Way](http://cliffle.com/p/dangerust/):从低级 C 语" +"言程序员的角度介绍 Rust。" #: src/other-resources.md:39 msgid "" @@ -19834,6 +19720,8 @@ msgid "" "rust_for_c/): covers Rust from the perspective of developers who write " "firmware in C." msgstr "" +"[Rust for Embedded C Programmers](https://docs.opentitan.org/doc/ug/" +"rust_for_c/):从用 C 语言编写固件的开发人员的角度介绍 Rust。" #: src/other-resources.md:42 msgid "" @@ -19841,12 +19729,17 @@ msgid "" "covers the syntax of Rust using side-by-side comparisons with other " "languages such as C, C++, Java, JavaScript, and Python." msgstr "" +"[Rust for professionals](https://overexact.com/rust-for-professionals/):通过" +"与 C、C++、Java、JavaScript 和 Python 等其他语言的并排比较,介绍 Rust 的语" +"法。" #: src/other-resources.md:45 msgid "" "[Rust on Exercism](https://exercism.org/tracks/rust): 100+ exercises to help " "you learn Rust." msgstr "" +"[Rust on Exercism](https://exercism.org/tracks/rust): 100 多道练习题帮助你学" +"习 Rust。" #: src/other-resources.md:47 msgid "" @@ -19855,6 +19748,9 @@ msgid "" "and advanced part of the Rust language. Other topics such as WebAssembly, " "and async/await are also covered." msgstr "" +"[Ferrous Teaching Material](https://ferrous-systems.github.io/teaching-" +"material/index.html):一系列小型演示,涵盖 Rust 语言的基础和高级部分。还涉及 " +"WebAssembly 和 async/await 等其他主题。" #: src/other-resources.md:52 msgid "" @@ -19864,6 +19760,10 @@ msgid "" "new developers. The first is a set of 35 videos and the second is a set of " "11 modules which covers Rust syntax and basic constructs." msgstr "" +"[Beginner’s Series to Rust](https://docs.microsoft.com/en-us/shows/beginners-" +"series-to-rust/) 和[Take your first steps with Rust](https://docs.microsoft." +"com/en-us/learn/paths/rust-first-steps/):两本面向新开发人员的 Rust 指南。前" +"者由 35 个视频组成,后者由 11 个模块组成,涵盖了 Rust 语法和基本构造。" #: src/other-resources.md:58 msgid "" @@ -19871,12 +19771,17 @@ msgid "" "github.io/too-many-lists/): in-depth exploration of Rust's memory management " "rules, through implementing a few different types of list structures." msgstr "" +"[Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial." +"github.io/too-many-lists/):通过实现几种不同类型的列表结构,深入探讨 Rust 的" +"内存管理规则。" #: src/other-resources.md:63 msgid "" "Please see the [Little Book of Rust Books](https://lborb.github.io/book/) " "for even more Rust books." msgstr "" +"有关更多的 Rust 书籍,请参阅 [Little Book of Rust Books](https://lborb." +"github.io/book/)。" #: src/credits.md:3 msgid "" @@ -19890,16 +19795,17 @@ msgstr "" #: src/credits.md:7 msgid "" "The material of Comprehensive Rust is licensed under the terms of the Apache " -"2.0 license, please see [`LICENSE`](../LICENSE) for details." +"2.0 license, please see [`LICENSE`](https://github.com/google/comprehensive-" +"rust/blob/main/LICENSE) for details." msgstr "" -"我们根据 Apache 2.0 许可条款 授权你使用“全面了解 Rust”(Comprehensive Rust)的" -"资料。如需了解详情,请参阅[`许可`](../LICENSE)。" +"Comprehensive Rust 的材料根据 Apache 2.0 许可条款获得许可,详情请参见 " +"[`LICENSE`](https://github.com/google/comprehensive-rust/blob/main/LICENSE)。" -#: src/credits.md:10 +#: src/credits.md:12 msgid "Rust by Example" msgstr "Rust 示例" -#: src/credits.md:12 +#: src/credits.md:14 msgid "" "Some examples and exercises have been copied and adapted from [Rust by " "Example](https://doc.rust-lang.org/rust-by-example/). Please see the " @@ -19910,11 +19816,11 @@ msgstr "" "by-example/)。如需了解详情(包括许可 条款),请参阅 `third_party/rust-by-" "example/` 目录。" -#: src/credits.md:17 +#: src/credits.md:19 msgid "Rust on Exercism" msgstr "Rust on Exercism" -#: src/credits.md:19 +#: src/credits.md:21 msgid "" "Some exercises have been copied and adapted from [Rust on Exercism](https://" "exercism.org/tracks/rust). Please see the `third_party/rust-on-exercism/` " @@ -19923,11 +19829,11 @@ msgstr "" "部分练习复制并 改编自 [Rust on Exercism](https://exercism.org/tracks/rust)。" "如需了解详情(包括许可 条款),请参阅 `third_party/rust-on-exercism/` 目录。" -#: src/credits.md:24 +#: src/credits.md:26 msgid "CXX" msgstr "CXX" -#: src/credits.md:26 +#: src/credits.md:28 msgid "" "The [Interoperability with C++](android/interoperability/cpp.md) section " "uses an image from [CXX](https://cxx.rs/). Please see the `third_party/cxx/` " @@ -19947,14 +19853,9 @@ msgid "" "com/google/comprehensive-rust/discussions). Let us know if you have a " "different or better solution than what is presented here." msgstr "" -"欢迎您在 [GitHub](https://github.com/google/comprehensive-rust/discussions) 上提问关于解决方案的问题。如果您有与此处呈现的不同或更好的解决方案,请告诉我们。" - -#: src/exercises/solutions.md:10 -msgid "" -"**Note:** Please ignore the `// ANCHOR: label` and `// ANCHOR_END: label` " -"comments you see in the solutions. They are there to make it possible to re-" -"use parts of the solutions as the exercises." -msgstr "" +"欢迎您在 [GitHub](https://github.com/google/comprehensive-rust/discussions) " +"上提问关于解决方案的问题。如果您有与此处呈现的不同或更好的解决方案,请告诉我" +"们。" #: src/exercises/day-1/solutions-morning.md:1 msgid "Day 1 Morning Exercises" @@ -19967,23 +19868,7 @@ msgstr "([返回练习](for-loops.md))" #: src/exercises/day-1/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: transpose\n" "fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {\n" -" // ANCHOR_END: transpose\n" " let mut result = [[0; 3]; 3];\n" " for i in 0..3 {\n" " for j in 0..3 {\n" @@ -19993,15 +19878,12 @@ msgid "" " return result;\n" "}\n" "\n" -"// ANCHOR: pretty_print\n" "fn pretty_print(matrix: &[[i32; 3]; 3]) {\n" -" // ANCHOR_END: pretty_print\n" " for row in matrix {\n" " println!(\"{row:?}\");\n" " }\n" "}\n" "\n" -"// ANCHOR: tests\n" "#[test]\n" "fn test_transpose() {\n" " let matrix = [\n" @@ -20019,9 +19901,7 @@ msgid "" " ]\n" " );\n" "}\n" -"// ANCHOR_END: tests\n" "\n" -"// ANCHOR: main\n" "fn main() {\n" " let matrix = [\n" " [101, 102, 103], // <-- the comment makes rustfmt add a newline\n" @@ -20039,542 +19919,91 @@ msgid "" "```" msgstr "" -#: src/exercises/day-1/solutions-morning.md:78 +#: src/exercises/day-1/solutions-morning.md:57 msgid "Bonus question" msgstr "附加问题" -#: src/exercises/day-1/solutions-morning.md:80 +#: src/exercises/day-1/solutions-morning.md:59 msgid "" "It requires more advanced concepts. It might seem that we could use a slice-" "of-slices (`&[&[i32]]`) as the input type to transpose and thus make our " "function handle any size of matrix. However, this quickly breaks down: the " "return type cannot be `&[&[i32]]` since it needs to own the data you return." msgstr "" -"这需要更高级的概念。看起来,我们可以使用切片的切片(`&[&[i32]]`)作为输入类型来进行转置,从而使我们的函数能够处理任意大小的矩阵。然而,这很快就会崩溃:返回类型不能是 `&[&[i32]]`,因为它需要拥有您返回的数据。" +"这需要更高级的概念。看起来,我们可以使用切片的切片(`&[&[i32]]`)作为输入类型" +"来进行转置,从而使我们的函数能够处理任意大小的矩阵。然而,这很快就会崩溃:返" +"回类型不能是 `&[&[i32]]`,因为它需要拥有您返回的数据。" -#: src/exercises/day-1/solutions-morning.md:82 +#: src/exercises/day-1/solutions-morning.md:61 msgid "" "You can attempt to use something like `Vec>`, but this doesn't work " "out-of-the-box either: it's hard to convert from `Vec>` to " "`&[&[i32]]` so now you cannot easily use `pretty_print` either." msgstr "" -"您可以尝试使用类似 `Vec>` 的方式,但这也无法直接工作:从 `Vec>` 转换为 " -"`&[&[i32]]` 很困难,因此您现在也不能轻松使用 `pretty_print`。" +"您可以尝试使用类似 `Vec>` 的方式,但这也无法直接工作:从 " +"`Vec>` 转换为 `&[&[i32]]` 很困难,因此您现在也不能轻松使用 " +"`pretty_print`。" -#: src/exercises/day-1/solutions-morning.md:84 +#: src/exercises/day-1/solutions-morning.md:63 msgid "" "Once we get to traits and generics, we'll be able to use the [`std::convert::" "AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) trait to " -"abstract over anything that can be referenced as a slice." -msgstr "" - -#: src/exercises/day-1/solutions-morning.md:86 -msgid "" -"```rust\n" -"use std::convert::AsRef;\n" -"use std::fmt::Debug;\n" -"\n" -"fn pretty_print(matrix: Matrix)\n" -"where\n" -" T: Debug,\n" -" // A line references a slice of items\n" -" Line: AsRef<[T]>,\n" -" // A matrix references a slice of lines\n" -" Matrix: AsRef<[Line]>\n" -"{\n" -" for row in matrix.as_ref() {\n" -" println!(\"{:?}\", row.as_ref());\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" // &[&[i32]]\n" -" pretty_print(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]);\n" -" // [[&str; 2]; 2]\n" -" pretty_print([[\"a\", \"b\"], [\"c\", \"d\"]]);\n" -" // Vec>\n" -" pretty_print(vec![vec![1, 2], vec![3, 4]]);\n" -"}\n" -"```" -msgstr "" - -#: src/exercises/day-1/solutions-morning.md:113 -msgid "" -"In addition, the type itself would not enforce that the child slices are of " -"the same length, so such variable could contain an invalid matrix." -msgstr "此外,类型本身不会强制要求子切片具有相同的长度,因此这样的变量可能包含一个无效的矩阵。" - -#: src/exercises/day-1/solutions-afternoon.md:1 -msgid "Day 1 Afternoon Exercises" -msgstr "第一天下午的练习" - -#: src/exercises/day-1/solutions-afternoon.md:5 -msgid "([back to exercise](book-library.md))" -msgstr "([返回练习](book-library.md))" - -#: src/exercises/day-1/solutions-afternoon.md:7 -msgid "" -"```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" -"struct Library {\n" -" books: Vec,\n" -"}\n" -"\n" -"struct Book {\n" -" title: String,\n" -" year: u16,\n" -"}\n" -"\n" -"impl Book {\n" -" // This is a constructor, used below.\n" -" fn new(title: &str, year: u16) -> Book {\n" -" Book {\n" -" title: String::from(title),\n" -" year,\n" -" }\n" -" }\n" -"}\n" -"\n" -"// Implement the methods below. Update the `self` parameter to\n" -"// indicate the method's required level of ownership over the object:\n" -"//\n" -"// - `&self` for shared read-only access,\n" -"// - `&mut self` for unique and mutable access,\n" -"// - `self` for unique access by value.\n" -"impl Library {\n" -" // ANCHOR_END: setup\n" -"\n" -" // ANCHOR: Library_new\n" -" fn new() -> Library {\n" -" // ANCHOR_END: Library_new\n" -" Library { books: Vec::new() }\n" -" }\n" -"\n" -" // ANCHOR: Library_len\n" -" //fn len(self) -> usize {\n" -" // todo!(\"Return the length of `self.books`\")\n" -" //}\n" -" // ANCHOR_END: Library_len\n" -" fn len(&self) -> usize {\n" -" self.books.len()\n" -" }\n" -"\n" -" // ANCHOR: Library_is_empty\n" -" //fn is_empty(self) -> bool {\n" -" // todo!(\"Return `true` if `self.books` is empty\")\n" -" //}\n" -" // ANCHOR_END: Library_is_empty\n" -" fn is_empty(&self) -> bool {\n" -" self.books.is_empty()\n" -" }\n" -"\n" -" // ANCHOR: Library_add_book\n" -" //fn add_book(self, book: Book) {\n" -" // todo!(\"Add a new book to `self.books`\")\n" -" //}\n" -" // ANCHOR_END: Library_add_book\n" -" fn add_book(&mut self, book: Book) {\n" -" self.books.push(book)\n" -" }\n" -"\n" -" // ANCHOR: Library_print_books\n" -" //fn print_books(self) {\n" -" // todo!(\"Iterate over `self.books` and each book's title and " -"year\")\n" -" //}\n" -" // ANCHOR_END: Library_print_books\n" -" fn print_books(&self) {\n" -" for book in &self.books {\n" -" println!(\"{}, published in {}\", book.title, book.year);\n" -" }\n" -" }\n" -"\n" -" // ANCHOR: Library_oldest_book\n" -" //fn oldest_book(self) -> Option<&Book> {\n" -" // todo!(\"Return a reference to the oldest book (if any)\")\n" -" //}\n" -" // ANCHOR_END: Library_oldest_book\n" -" fn oldest_book(&self) -> Option<&Book> {\n" -" // Using a closure and a built-in method:\n" -" // self.books.iter().min_by_key(|book| book.year)\n" -"\n" -" // Longer hand-written solution:\n" -" let mut oldest: Option<&Book> = None;\n" -" for book in self.books.iter() {\n" -" if oldest.is_none() || book.year < oldest.unwrap().year {\n" -" oldest = Some(book);\n" -" }\n" -" }\n" -"\n" -" oldest\n" -" }\n" -"}\n" -"\n" -"// ANCHOR: main\n" -"// This shows the desired behavior. Uncomment the code below and\n" -"// implement the missing methods. You will need to update the\n" -"// method signatures, including the \"self\" parameter! You may\n" -"// also need to update the variable bindings within main.\n" -"fn main() {\n" -" let library = Library::new();\n" -"\n" -" //println!(\"The library is empty: {}\", library.is_empty());\n" -" //\n" -" //library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" //library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" //\n" -" //println!(\"The library is no longer empty: {}\", library.is_empty());\n" -" //\n" -" //\n" -" //library.print_books();\n" -" //\n" -" //match library.oldest_book() {\n" -" // Some(book) => println!(\"The oldest book is {}\", book.title),\n" -" // None => println!(\"The library is empty!\"),\n" -" //}\n" -" //\n" -" //println!(\"The library has {} books\", library.len());\n" -" //library.print_books();\n" -"}\n" -"// ANCHOR_END: main\n" -"\n" -"#[test]\n" -"fn test_library_len() {\n" -" let mut library = Library::new();\n" -" assert_eq!(library.len(), 0);\n" -" assert!(library.is_empty());\n" -"\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" assert_eq!(library.len(), 2);\n" -" assert!(!library.is_empty());\n" -"}\n" -"\n" -"#[test]\n" -"fn test_library_is_empty() {\n" -" let mut library = Library::new();\n" -" assert!(library.is_empty());\n" -"\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" assert!(!library.is_empty());\n" -"}\n" -"\n" -"#[test]\n" -"fn test_library_print_books() {\n" -" let mut library = Library::new();\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" // We could try and capture stdout, but let us just call the\n" -" // method to start with.\n" -" library.print_books();\n" -"}\n" -"\n" -"#[test]\n" -"fn test_library_oldest_book() {\n" -" let mut library = Library::new();\n" -" assert!(library.oldest_book().is_none());\n" -"\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" assert_eq!(\n" -" library.oldest_book().map(|b| b.title.as_str()),\n" -" Some(\"Lord of the Rings\")\n" -" );\n" -"\n" -" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" assert_eq!(\n" -" library.oldest_book().map(|b| b.title.as_str()),\n" -" Some(\"Alice's Adventures in Wonderland\")\n" -" );\n" -"}\n" -"```" -msgstr "" - -#: src/exercises/day-2/solutions-morning.md:1 -msgid "Day 2 Morning Exercises" -msgstr "第二天上午的练习" - -#: src/exercises/day-2/solutions-morning.md:5 -msgid "([back to exercise](points-polygons.md))" -msgstr "([返回练习](points-polygons.md))" - -#: src/exercises/day-2/solutions-morning.md:7 -msgid "" -"```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n" -"// ANCHOR: Point\n" -"pub struct Point {\n" -" // ANCHOR_END: Point\n" -" x: i32,\n" -" y: i32,\n" -"}\n" -"\n" -"// ANCHOR: Point-impl\n" -"impl Point {\n" -" // ANCHOR_END: Point-impl\n" -" pub fn new(x: i32, y: i32) -> Point {\n" -" Point { x, y }\n" -" }\n" -"\n" -" pub fn magnitude(self) -> f64 {\n" -" f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()\n" -" }\n" -"\n" -" pub fn dist(self, other: Point) -> f64 {\n" -" (self - other).magnitude()\n" -" }\n" -"}\n" -"\n" -"impl std::ops::Add for Point {\n" -" type Output = Self;\n" -"\n" -" fn add(self, other: Self) -> Self::Output {\n" -" Self {\n" -" x: self.x + other.x,\n" -" y: self.y + other.y,\n" -" }\n" -" }\n" -"}\n" -"\n" -"impl std::ops::Sub for Point {\n" -" type Output = Self;\n" -"\n" -" fn sub(self, other: Self) -> Self::Output {\n" -" Self {\n" -" x: self.x - other.x,\n" -" y: self.y - other.y,\n" -" }\n" -" }\n" -"}\n" -"\n" -"// ANCHOR: Polygon\n" -"pub struct Polygon {\n" -" // ANCHOR_END: Polygon\n" -" points: Vec,\n" -"}\n" -"\n" -"// ANCHOR: Polygon-impl\n" -"impl Polygon {\n" -" // ANCHOR_END: Polygon-impl\n" -" pub fn new() -> Polygon {\n" -" Polygon { points: Vec::new() }\n" -" }\n" -"\n" -" pub fn add_point(&mut self, point: Point) {\n" -" self.points.push(point);\n" -" }\n" -"\n" -" pub fn left_most_point(&self) -> Option {\n" -" self.points.iter().min_by_key(|p| p.x).copied()\n" -" }\n" -"\n" -" pub fn iter(&self) -> impl Iterator {\n" -" self.points.iter()\n" -" }\n" -"\n" -" pub fn length(&self) -> f64 {\n" -" if self.points.is_empty() {\n" -" return 0.0;\n" -" }\n" -"\n" -" let mut result = 0.0;\n" -" let mut last_point = self.points[0];\n" -" for point in &self.points[1..] {\n" -" result += last_point.dist(*point);\n" -" last_point = *point;\n" -" }\n" -" result += last_point.dist(self.points[0]);\n" -" result\n" -" }\n" -"}\n" -"\n" -"// ANCHOR: Circle\n" -"pub struct Circle {\n" -" // ANCHOR_END: Circle\n" -" center: Point,\n" -" radius: i32,\n" -"}\n" -"\n" -"// ANCHOR: Circle-impl\n" -"impl Circle {\n" -" // ANCHOR_END: Circle-impl\n" -" pub fn new(center: Point, radius: i32) -> Circle {\n" -" Circle { center, radius }\n" -" }\n" -"\n" -" pub fn circumference(&self) -> f64 {\n" -" 2.0 * std::f64::consts::PI * f64::from(self.radius)\n" -" }\n" -"\n" -" pub fn dist(&self, other: &Self) -> f64 {\n" -" self.center.dist(other.center)\n" -" }\n" -"}\n" -"\n" -"// ANCHOR: Shape\n" -"pub enum Shape {\n" -" Polygon(Polygon),\n" -" Circle(Circle),\n" -"}\n" -"// ANCHOR_END: Shape\n" -"\n" -"impl From for Shape {\n" -" fn from(poly: Polygon) -> Self {\n" -" Shape::Polygon(poly)\n" -" }\n" -"}\n" -"\n" -"impl From for Shape {\n" -" fn from(circle: Circle) -> Self {\n" -" Shape::Circle(circle)\n" -" }\n" -"}\n" -"\n" -"impl Shape {\n" -" pub fn perimeter(&self) -> f64 {\n" -" match self {\n" -" Shape::Polygon(poly) => poly.length(),\n" -" Shape::Circle(circle) => circle.circumference(),\n" -" }\n" -" }\n" -"}\n" -"\n" -"// ANCHOR: unit-tests\n" -"#[cfg(test)]\n" -"mod tests {\n" -" use super::*;\n" -"\n" -" fn round_two_digits(x: f64) -> f64 {\n" -" (x * 100.0).round() / 100.0\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_magnitude() {\n" -" let p1 = Point::new(12, 13);\n" -" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_dist() {\n" -" let p1 = Point::new(10, 10);\n" -" let p2 = Point::new(14, 13);\n" -" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_add() {\n" -" let p1 = Point::new(16, 16);\n" -" let p2 = p1 + Point::new(-4, 3);\n" -" assert_eq!(p2, Point::new(12, 19));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_left_most_point() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -" assert_eq!(poly.left_most_point(), Some(p1));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_iter() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -"\n" -" let points = poly.iter().cloned().collect::>();\n" -" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" -" }\n" +"abstract over anything that can be referenced as a slice." +msgstr "" +"一旦我们使用了特型和泛型,我们就可以使用 [`std::convert::AsRef`](https://doc." +"rust-lang.org/std/convert/trait.AsRef.html) 特型来抽象任何可以作为切片引用的" +"内容。" + +#: src/exercises/day-1/solutions-morning.md:65 +msgid "" +"```rust\n" +"use std::convert::AsRef;\n" +"use std::fmt::Debug;\n" "\n" -" #[test]\n" -" fn test_shape_perimeters() {\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(Point::new(12, 13));\n" -" poly.add_point(Point::new(17, 11));\n" -" poly.add_point(Point::new(16, 16));\n" -" let shapes = vec![\n" -" Shape::from(poly),\n" -" Shape::from(Circle::new(Point::new(10, 20), 5)),\n" -" ];\n" -" let perimeters = shapes\n" -" .iter()\n" -" .map(Shape::perimeter)\n" -" .map(round_two_digits)\n" -" .collect::>();\n" -" assert_eq!(perimeters, vec![15.48, 31.42]);\n" +"fn pretty_print(matrix: Matrix)\n" +"where\n" +" T: Debug,\n" +" // A line references a slice of items\n" +" Line: AsRef<[T]>,\n" +" // A matrix references a slice of lines\n" +" Matrix: AsRef<[Line]>\n" +"{\n" +" for row in matrix.as_ref() {\n" +" println!(\"{:?}\", row.as_ref());\n" " }\n" "}\n" -"// ANCHOR_END: unit-tests\n" "\n" -"fn main() {}\n" +"fn main() {\n" +" // &[&[i32]]\n" +" pretty_print(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]);\n" +" // [[&str; 2]; 2]\n" +" pretty_print([[\"a\", \"b\"], [\"c\", \"d\"]]);\n" +" // Vec>\n" +" pretty_print(vec![vec![1, 2], vec![3, 4]]);\n" +"}\n" "```" msgstr "" -#: src/exercises/day-2/solutions-afternoon.md:1 -msgid "Day 2 Afternoon Exercises" -msgstr "第二天下午的练习" +#: src/exercises/day-1/solutions-morning.md:92 +msgid "" +"In addition, the type itself would not enforce that the child slices are of " +"the same length, so such variable could contain an invalid matrix." +msgstr "" +"此外,类型本身不会强制要求子切片具有相同的长度,因此这样的变量可能包含一个无" +"效的矩阵。" -#: src/exercises/day-2/solutions-afternoon.md:5 +#: src/exercises/day-1/solutions-afternoon.md:1 +msgid "Day 1 Afternoon Exercises" +msgstr "第一天下午的练习" + +#: src/exercises/day-1/solutions-afternoon.md:5 msgid "([back to exercise](luhn.md))" msgstr "([返回练习](luhn.md))" -#: src/exercises/day-2/solutions-afternoon.md:7 +#: src/exercises/day-1/solutions-afternoon.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: luhn\n" "pub fn luhn(cc_number: &str) -> bool {\n" -" // ANCHOR_END: luhn\n" " let mut digits_seen = 0;\n" " let mut sum = 0;\n" " for (i, ch) in cc_number.chars().rev().filter(|&ch| ch != ' ')." @@ -20608,10 +20037,10 @@ msgid "" " );\n" "}\n" "\n" -"// ANCHOR: unit-tests\n" "#[test]\n" "fn test_non_digit_cc_number() {\n" " assert!(!luhn(\"foo\"));\n" +" assert!(!luhn(\"foo 0 0\"));\n" "}\n" "\n" "#[test]\n" @@ -20640,59 +20069,350 @@ msgid "" "}\n" "\n" "#[test]\n" -"fn test_invalid_cc_number() {\n" -" assert!(!luhn(\"4223 9826 4026 9299\"));\n" -" assert!(!luhn(\"4539 3195 0343 6476\"));\n" -" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +"fn test_invalid_cc_number() {\n" +" assert!(!luhn(\"4223 9826 4026 9299\"));\n" +" assert!(!luhn(\"4539 3195 0343 6476\"));\n" +" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-1/solutions-afternoon.md:80 +msgid "Pattern matching" +msgstr "模式匹配" + +#: src/exercises/day-1/solutions-afternoon.md:82 +msgid "" +"```rust\n" +"/// An operation to perform on two subexpressions.\n" +"#[derive(Debug)]\n" +"enum Operation {\n" +" Add,\n" +" Sub,\n" +" Mul,\n" +" Div,\n" +"}\n" +"\n" +"/// An expression, in tree form.\n" +"#[derive(Debug)]\n" +"enum Expression {\n" +" /// An operation on two subexpressions.\n" +" Op {\n" +" op: Operation,\n" +" left: Box,\n" +" right: Box,\n" +" },\n" +"\n" +" /// A literal value\n" +" Value(i64),\n" +"}\n" +"\n" +"/// The result of evaluating an expression.\n" +"#[derive(Debug, PartialEq, Eq)]\n" +"enum Res {\n" +" /// Evaluation was successful, with the given result.\n" +" Ok(i64),\n" +" /// Evaluation failed, with the given error message.\n" +" Err(String),\n" +"}\n" +"// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.\n" +"use Res::{Err, Ok};\n" +"\n" +"fn eval(e: Expression) -> Res {\n" +" match e {\n" +" Expression::Op { op, left, right } => {\n" +" let left = match eval(*left) {\n" +" Ok(v) => v,\n" +" Err(msg) => return Err(msg),\n" +" };\n" +" let right = match eval(*right) {\n" +" Ok(v) => v,\n" +" Err(msg) => return Err(msg),\n" +" };\n" +" Ok(match op {\n" +" Operation::Add => left + right,\n" +" Operation::Sub => left - right,\n" +" Operation::Mul => left * right,\n" +" Operation::Div => {\n" +" if right == 0 {\n" +" return Err(String::from(\"division by zero\"));\n" +" } else {\n" +" left / right\n" +" }\n" +" }\n" +" })\n" +" }\n" +" Expression::Value(v) => Ok(v),\n" +" }\n" +"}\n" +"\n" +"#[test]\n" +"fn test_value() {\n" +" assert_eq!(eval(Expression::Value(19)), Ok(19));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_sum() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(20)),\n" +" }),\n" +" Ok(30)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_recursion() {\n" +" let term1 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(9)),\n" +" };\n" +" let term2 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(3)),\n" +" right: Box::new(Expression::Value(4)),\n" +" }),\n" +" right: Box::new(Expression::Value(5)),\n" +" };\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(term1),\n" +" right: Box::new(term2),\n" +" }),\n" +" Ok(85)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_error() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Div,\n" +" left: Box::new(Expression::Value(99)),\n" +" right: Box::new(Expression::Value(0)),\n" +" }),\n" +" Err(String::from(\"division by zero\"))\n" +" );\n" +"}\n" +"fn main() {\n" +" let expr = Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(20)),\n" +" right: Box::new(Expression::Value(10)),\n" +" };\n" +" println!(\"expr: {:?}\", expr);\n" +" println!(\"result: {:?}\", eval(expr));\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-2/solutions-morning.md:1 +msgid "Day 2 Morning Exercises" +msgstr "第二天上午的练习" + +#: src/exercises/day-2/solutions-morning.md:3 +msgid "Designing a Library" +msgstr "设计一个库" + +#: src/exercises/day-2/solutions-morning.md:5 +msgid "([back to exercise](book-library.md))" +msgstr "([返回练习](book-library.md))" + +#: src/exercises/day-2/solutions-morning.md:7 +msgid "" +"```rust\n" +"struct Library {\n" +" books: Vec,\n" +"}\n" +"\n" +"struct Book {\n" +" title: String,\n" +" year: u16,\n" +"}\n" +"\n" +"impl Book {\n" +" // This is a constructor, used below.\n" +" fn new(title: &str, year: u16) -> Book {\n" +" Book {\n" +" title: String::from(title),\n" +" year,\n" +" }\n" +" }\n" +"}\n" +"\n" +"// Implement the methods below. Notice how the `self` parameter\n" +"// changes type to indicate the method's required level of ownership\n" +"// over the object:\n" +"//\n" +"// - `&self` for shared read-only access,\n" +"// - `&mut self` for unique and mutable access,\n" +"// - `self` for unique access by value.\n" +"impl Library {\n" +"\n" +" fn new() -> Library {\n" +" Library { books: Vec::new() }\n" +" }\n" +"\n" +" fn len(&self) -> usize {\n" +" self.books.len()\n" +" }\n" +"\n" +" fn is_empty(&self) -> bool {\n" +" self.books.is_empty()\n" +" }\n" +"\n" +" fn add_book(&mut self, book: Book) {\n" +" self.books.push(book)\n" +" }\n" +"\n" +" fn print_books(&self) {\n" +" for book in &self.books {\n" +" println!(\"{}, published in {}\", book.title, book.year);\n" +" }\n" +" }\n" +"\n" +" fn oldest_book(&self) -> Option<&Book> {\n" +" // Using a closure and a built-in method:\n" +" // self.books.iter().min_by_key(|book| book.year)\n" +"\n" +" // Longer hand-written solution:\n" +" let mut oldest: Option<&Book> = None;\n" +" for book in self.books.iter() {\n" +" if oldest.is_none() || book.year < oldest.unwrap().year {\n" +" oldest = Some(book);\n" +" }\n" +" }\n" +"\n" +" oldest\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let mut library = Library::new();\n" +"\n" +" println!(\n" +" \"The library is empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +"\n" +" println!(\n" +" \"The library is no longer empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.print_books();\n" +"\n" +" match library.oldest_book() {\n" +" Some(book) => println!(\"The oldest book is {}\", book.title),\n" +" None => println!(\"The library is empty!\"),\n" +" }\n" +"\n" +" println!(\"The library has {} books\", library.len());\n" +" library.print_books();\n" +"}\n" +"\n" +"#[test]\n" +"fn test_library_len() {\n" +" let mut library = Library::new();\n" +" assert_eq!(library.len(), 0);\n" +" assert!(library.is_empty());\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +" assert_eq!(library.len(), 2);\n" +" assert!(!library.is_empty());\n" +"}\n" +"\n" +"#[test]\n" +"fn test_library_is_empty() {\n" +" let mut library = Library::new();\n" +" assert!(library.is_empty());\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" assert!(!library.is_empty());\n" +"}\n" +"\n" +"#[test]\n" +"fn test_library_print_books() {\n" +" let mut library = Library::new();\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +" // We could try and capture stdout, but let us just call the\n" +" // method to start with.\n" +" library.print_books();\n" +"}\n" +"\n" +"#[test]\n" +"fn test_library_oldest_book() {\n" +" let mut library = Library::new();\n" +" assert!(library.oldest_book().is_none());\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" assert_eq!(\n" +" library.oldest_book().map(|b| b.title.as_str()),\n" +" Some(\"Lord of the Rings\")\n" +" );\n" +"\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +" assert_eq!(\n" +" library.oldest_book().map(|b| b.title.as_str()),\n" +" Some(\"Alice's Adventures in Wonderland\")\n" +" );\n" "}\n" -"// ANCHOR_END: unit-tests\n" "```" msgstr "" -#: src/exercises/day-2/solutions-afternoon.md:99 +#: src/exercises/day-2/solutions-afternoon.md:1 +msgid "Day 2 Afternoon Exercises" +msgstr "第二天下午的练习" + +#: src/exercises/day-2/solutions-afternoon.md:5 msgid "([back to exercise](strings-iterators.md))" msgstr "([返回练习](strings-iterators.md))" -#: src/exercises/day-2/solutions-afternoon.md:101 +#: src/exercises/day-2/solutions-afternoon.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: prefix_matches\n" "pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" -" // ANCHOR_END: prefix_matches\n" -" let prefixes = prefix.split('/');\n" -" let request_paths = request_path\n" -" .split('/')\n" -" .map(|p| Some(p))\n" -" .chain(std::iter::once(None));\n" -"\n" -" for (prefix, request_path) in prefixes.zip(request_paths) {\n" -" match request_path {\n" -" Some(request_path) => {\n" -" if (prefix != \"*\") && (prefix != request_path) {\n" -" return false;\n" -" }\n" -" }\n" -" None => return false,\n" +"\n" +" let mut request_segments = request_path.split('/');\n" +"\n" +" for prefix_segment in prefix.split('/') {\n" +" let Some(request_segment) = request_segments.next() else {\n" +" return false;\n" +" };\n" +" if request_segment != prefix_segment && prefix_segment != \"*\" {\n" +" return false;\n" " }\n" " }\n" " true\n" +"\n" +" // Alternatively, Iterator::zip() lets us iterate simultaneously over " +"prefix\n" +" // and request segments. The zip() iterator is finished as soon as one " +"of\n" +" // the source iterators is finished, but we need to iterate over all " +"request\n" +" // segments. A neat trick that makes zip() work is to use map() and " +"chain()\n" +" // to produce an iterator that returns Some(str) for each pattern " +"segments,\n" +" // and then returns None indefinitely.\n" "}\n" "\n" -"// ANCHOR: unit-tests\n" "#[test]\n" "fn test_matches_without_wildcard() {\n" " assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" @@ -20729,7 +20449,6 @@ msgid "" " \"/v1/publishers/foo/booksByAuthor\"\n" " ));\n" "}\n" -"// ANCHOR_END: unit-tests\n" "\n" "fn main() {}\n" "```" @@ -20746,21 +20465,6 @@ msgstr "([返回练习](simple-gui.md))" #: src/exercises/day-3/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" "pub trait Widget {\n" " /// Natural width of `self`.\n" " fn width(&self) -> usize;\n" @@ -20827,19 +20531,14 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR_END: setup\n" "\n" -"// ANCHOR: Window-width\n" "impl Widget for Window {\n" " fn width(&self) -> usize {\n" -" // ANCHOR_END: Window-width\n" " // Add 4 paddings for borders\n" " self.inner_width() + 4\n" " }\n" "\n" -" // ANCHOR: Window-draw_into\n" " fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" // ANCHOR_END: Window-draw_into\n" " let mut inner = String::new();\n" " for widget in &self.widgets {\n" " widget.draw_into(&mut inner);\n" @@ -20860,16 +20559,12 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Button-width\n" "impl Widget for Button {\n" " fn width(&self) -> usize {\n" -" // ANCHOR_END: Button-width\n" " self.label.width() + 8 // add a bit of padding\n" " }\n" "\n" -" // ANCHOR: Button-draw_into\n" " fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" // ANCHOR_END: Button-draw_into\n" " let width = self.width();\n" " let mut label = String::new();\n" " self.label.draw_into(&mut label);\n" @@ -20882,10 +20577,8 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Label-width\n" "impl Widget for Label {\n" " fn width(&self) -> usize {\n" -" // ANCHOR_END: Label-width\n" " self.label\n" " .lines()\n" " .map(|line| line.chars().count())\n" @@ -20893,14 +20586,11 @@ msgid "" " .unwrap_or(0)\n" " }\n" "\n" -" // ANCHOR: Label-draw_into\n" " fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" -" // ANCHOR_END: Label-draw_into\n" " writeln!(buffer, \"{}\", &self.label).unwrap();\n" " }\n" "}\n" "\n" -"// ANCHOR: main\n" "fn main() {\n" " let mut window = Window::new(\"Rust GUI Demo 1.23\");\n" " window.add_widget(Box::new(Label::new(\"This is a small text GUI demo." @@ -20911,7 +20601,223 @@ msgid "" " )));\n" " window.draw();\n" "}\n" -"// ANCHOR_END: main\n" +"```" +msgstr "" + +#: src/exercises/day-3/solutions-morning.md:147 +msgid "([back to exercise](points-polygons.md))" +msgstr "([返回练习](points-polygons.md))" + +#: src/exercises/day-3/solutions-morning.md:149 +msgid "" +"```rust\n" +"#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n" +"pub struct Point {\n" +" x: i32,\n" +" y: i32,\n" +"}\n" +"\n" +"impl Point {\n" +" pub fn new(x: i32, y: i32) -> Point {\n" +" Point { x, y }\n" +" }\n" +"\n" +" pub fn magnitude(self) -> f64 {\n" +" f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()\n" +" }\n" +"\n" +" pub fn dist(self, other: Point) -> f64 {\n" +" (self - other).magnitude()\n" +" }\n" +"}\n" +"\n" +"impl std::ops::Add for Point {\n" +" type Output = Self;\n" +"\n" +" fn add(self, other: Self) -> Self::Output {\n" +" Self {\n" +" x: self.x + other.x,\n" +" y: self.y + other.y,\n" +" }\n" +" }\n" +"}\n" +"\n" +"impl std::ops::Sub for Point {\n" +" type Output = Self;\n" +"\n" +" fn sub(self, other: Self) -> Self::Output {\n" +" Self {\n" +" x: self.x - other.x,\n" +" y: self.y - other.y,\n" +" }\n" +" }\n" +"}\n" +"\n" +"pub struct Polygon {\n" +" points: Vec,\n" +"}\n" +"\n" +"impl Polygon {\n" +" pub fn new() -> Polygon {\n" +" Polygon { points: Vec::new() }\n" +" }\n" +"\n" +" pub fn add_point(&mut self, point: Point) {\n" +" self.points.push(point);\n" +" }\n" +"\n" +" pub fn left_most_point(&self) -> Option {\n" +" self.points.iter().min_by_key(|p| p.x).copied()\n" +" }\n" +"\n" +" pub fn iter(&self) -> impl Iterator {\n" +" self.points.iter()\n" +" }\n" +"\n" +" pub fn length(&self) -> f64 {\n" +" if self.points.is_empty() {\n" +" return 0.0;\n" +" }\n" +"\n" +" let mut result = 0.0;\n" +" let mut last_point = self.points[0];\n" +" for point in &self.points[1..] {\n" +" result += last_point.dist(*point);\n" +" last_point = *point;\n" +" }\n" +" result += last_point.dist(self.points[0]);\n" +" result\n" +" // Alternatively, Iterator::zip() lets us iterate over the points as " +"pairs\n" +" // but we need to pair each point with the next one, and the last " +"point\n" +" // with the first point. The zip() iterator is finished as soon as " +"one of \n" +" // the source iterators is finished, a neat trick is to combine " +"Iterator::cycle\n" +" // with Iterator::skip to create the second iterator for the zip and " +"using map \n" +" // and sum to calculate the total length.\n" +" }\n" +"}\n" +"\n" +"pub struct Circle {\n" +" center: Point,\n" +" radius: i32,\n" +"}\n" +"\n" +"impl Circle {\n" +" pub fn new(center: Point, radius: i32) -> Circle {\n" +" Circle { center, radius }\n" +" }\n" +"\n" +" pub fn circumference(&self) -> f64 {\n" +" 2.0 * std::f64::consts::PI * f64::from(self.radius)\n" +" }\n" +"\n" +" pub fn dist(&self, other: &Self) -> f64 {\n" +" self.center.dist(other.center)\n" +" }\n" +"}\n" +"\n" +"pub enum Shape {\n" +" Polygon(Polygon),\n" +" Circle(Circle),\n" +"}\n" +"\n" +"impl From for Shape {\n" +" fn from(poly: Polygon) -> Self {\n" +" Shape::Polygon(poly)\n" +" }\n" +"}\n" +"\n" +"impl From for Shape {\n" +" fn from(circle: Circle) -> Self {\n" +" Shape::Circle(circle)\n" +" }\n" +"}\n" +"\n" +"impl Shape {\n" +" pub fn perimeter(&self) -> f64 {\n" +" match self {\n" +" Shape::Polygon(poly) => poly.length(),\n" +" Shape::Circle(circle) => circle.circumference(),\n" +" }\n" +" }\n" +"}\n" +"\n" +"#[cfg(test)]\n" +"mod tests {\n" +" use super::*;\n" +"\n" +" fn round_two_digits(x: f64) -> f64 {\n" +" (x * 100.0).round() / 100.0\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_magnitude() {\n" +" let p1 = Point::new(12, 13);\n" +" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_dist() {\n" +" let p1 = Point::new(10, 10);\n" +" let p2 = Point::new(14, 13);\n" +" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_point_add() {\n" +" let p1 = Point::new(16, 16);\n" +" let p2 = p1 + Point::new(-4, 3);\n" +" assert_eq!(p2, Point::new(12, 19));\n" +" }\n" +"\n" +" #[test]\n" +" fn test_polygon_left_most_point() {\n" +" let p1 = Point::new(12, 13);\n" +" let p2 = Point::new(16, 16);\n" +"\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(p1);\n" +" poly.add_point(p2);\n" +" assert_eq!(poly.left_most_point(), Some(p1));\n" +" }\n" +"\n" +" #[test]\n" +" fn test_polygon_iter() {\n" +" let p1 = Point::new(12, 13);\n" +" let p2 = Point::new(16, 16);\n" +"\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(p1);\n" +" poly.add_point(p2);\n" +"\n" +" let points = poly.iter().cloned().collect::>();\n" +" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" +" }\n" +"\n" +" #[test]\n" +" fn test_shape_perimeters() {\n" +" let mut poly = Polygon::new();\n" +" poly.add_point(Point::new(12, 13));\n" +" poly.add_point(Point::new(17, 11));\n" +" poly.add_point(Point::new(16, 16));\n" +" let shapes = vec![\n" +" Shape::from(poly),\n" +" Shape::from(Circle::new(Point::new(10, 20), 5)),\n" +" ];\n" +" let perimeters = shapes\n" +" .iter()\n" +" .map(Shape::perimeter)\n" +" .map(round_two_digits)\n" +" .collect::>();\n" +" assert_eq!(perimeters, vec![15.48, 31.42]);\n" +" }\n" +"}\n" +"\n" +"fn main() {}\n" "```" msgstr "" @@ -20926,25 +20832,10 @@ msgstr "([返回练习](safe-ffi-wrapper.md))" #: src/exercises/day-3/solutions-afternoon.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: ffi\n" "mod ffi {\n" " use std::os::raw::{c_char, c_int};\n" " #[cfg(not(target_os = \"macos\"))]\n" -" use std::os::raw::{c_long, c_ulong, c_ushort};\n" +" use std::os::raw::{c_long, c_ulong, c_ushort, c_uchar};\n" "\n" " // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.\n" " #[repr(C)]\n" @@ -20954,23 +20845,25 @@ msgid "" "PhantomPinned)>,\n" " }\n" "\n" -" // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-" -"gnu.\n" +" // Layout according to the Linux man page for readdir(3), where ino_t " +"and\n" +" // off_t are resolved according to the definitions in\n" +" // /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h}.\n" " #[cfg(not(target_os = \"macos\"))]\n" " #[repr(C)]\n" " pub struct dirent {\n" -" pub d_ino: c_long,\n" -" pub d_off: c_ulong,\n" +" pub d_ino: c_ulong,\n" +" pub d_off: c_long,\n" " pub d_reclen: c_ushort,\n" -" pub d_type: c_char,\n" +" pub d_type: c_uchar,\n" " pub d_name: [c_char; 256],\n" " }\n" "\n" -" // Layout as per man entry for dirent\n" -" #[cfg(target_os = \"macos\")]\n" +" // Layout according to the macOS man page for dir(5).\n" +" #[cfg(all(target_os = \"macos\"))]\n" " #[repr(C)]\n" " pub struct dirent {\n" -" pub d_ino: u64,\n" +" pub d_fileno: u64,\n" " pub d_seekoff: u64,\n" " pub d_reclen: u16,\n" " pub d_namlen: u16,\n" @@ -20980,7 +20873,22 @@ msgid "" "\n" " extern \"C\" {\n" " pub fn opendir(s: *const c_char) -> *mut DIR;\n" +"\n" +" #[cfg(not(all(target_os = \"macos\", target_arch = \"x86_64\")))]\n" +" pub fn readdir(s: *mut DIR) -> *const dirent;\n" +"\n" +" // See https://github.com/rust-lang/libc/issues/414 and the section " +"on\n" +" // _DARWIN_FEATURE_64_BIT_INODE in the macOS man page for stat(2).\n" +" //\n" +" // \"Platforms that existed before these updates were available\" " +"refers\n" +" // to macOS (as opposed to iOS / wearOS / etc.) on Intel and " +"PowerPC.\n" +" #[cfg(all(target_os = \"macos\", target_arch = \"x86_64\"))]\n" +" #[link_name = \"readdir$INODE64\"]\n" " pub fn readdir(s: *mut DIR) -> *const dirent;\n" +"\n" " pub fn closedir(s: *mut DIR) -> c_int;\n" " }\n" "}\n" @@ -20993,14 +20901,11 @@ msgid "" " path: CString,\n" " dir: *mut ffi::DIR,\n" "}\n" -"// ANCHOR_END: ffi\n" "\n" -"// ANCHOR: DirectoryIterator\n" "impl DirectoryIterator {\n" " fn new(path: &str) -> Result {\n" " // Call opendir and return a Ok value if that worked,\n" " // otherwise return Err with a message.\n" -" // ANCHOR_END: DirectoryIterator\n" " let path = CString::new(path).map_err(|err| format!(\"Invalid path: " "{err}\"))?;\n" " // SAFETY: path.as_ptr() cannot be NULL.\n" @@ -21013,12 +20918,10 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Iterator\n" "impl Iterator for DirectoryIterator {\n" " type Item = OsString;\n" " fn next(&mut self) -> Option {\n" " // Keep calling readdir until we get a NULL pointer back.\n" -" // ANCHOR_END: Iterator\n" " // SAFETY: self.dir is never NULL.\n" " let dirent = unsafe { ffi::readdir(self.dir) };\n" " if dirent.is_null() {\n" @@ -21033,11 +20936,9 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Drop\n" "impl Drop for DirectoryIterator {\n" " fn drop(&mut self) {\n" " // Call closedir as needed.\n" -" // ANCHOR_END: Drop\n" " if !self.dir.is_null() {\n" " // SAFETY: self.dir is not NULL.\n" " if unsafe { ffi::closedir(self.dir) } != 0 {\n" @@ -21047,13 +20948,11 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: main\n" "fn main() -> Result<(), String> {\n" " let iter = DirectoryIterator::new(\".\")?;\n" " println!(\"files: {:#?}\", iter.collect::>());\n" " Ok(())\n" "}\n" -"// ANCHOR_END: main\n" "\n" "#[cfg(test)]\n" "mod tests {\n" @@ -21100,7 +20999,7 @@ msgstr "" #: src/exercises/bare-metal/solutions-morning.md:1 msgid "Bare Metal Rust Morning Exercise" -msgstr "" +msgstr "嵌入式 Rust:入门篇" #: src/exercises/bare-metal/solutions-morning.md:5 msgid "([back to exercise](compass.md))" @@ -21109,21 +21008,6 @@ msgstr "([返回练习](compass.md))" #: src/exercises/bare-metal/solutions-morning.md:7 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: top\n" "#![no_main]\n" "#![no_std]\n" "\n" @@ -21131,7 +21015,6 @@ msgid "" "\n" "use core::fmt::Write;\n" "use cortex_m_rt::entry;\n" -"// ANCHOR_END: top\n" "use core::cmp::{max, min};\n" "use lsm303agr::{AccelOutputDataRate, Lsm303agr, MagOutputDataRate};\n" "use microbit::display::blocking::Display;\n" @@ -21145,7 +21028,6 @@ msgid "" "const COMPASS_SCALE: i32 = 30000;\n" "const ACCELEROMETER_SCALE: i32 = 700;\n" "\n" -"// ANCHOR: main\n" "#[entry]\n" "fn main() -> ! {\n" " let board = Board::take().unwrap();\n" @@ -21159,7 +21041,6 @@ msgid "" " );\n" "\n" " // Set up the I2C controller and Inertial Measurement Unit.\n" -" // ANCHOR_END: main\n" " writeln!(serial, \"Setting up IMU...\").unwrap();\n" " let i2c = Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::" "K100);\n" @@ -21176,12 +21057,10 @@ msgid "" " let mut mode = Mode::Compass;\n" " let mut button_pressed = false;\n" "\n" -" // ANCHOR: loop\n" " writeln!(serial, \"Ready.\").unwrap();\n" "\n" " loop {\n" " // Read compass data and log it to the serial port.\n" -" // ANCHOR_END: loop\n" " while !(imu.mag_status().unwrap().xyz_new_data\n" " && imu.accel_status().unwrap().xyz_new_data)\n" " {}\n" @@ -21284,28 +21163,12 @@ msgstr "`main.rs`:" #: src/exercises/bare-metal/solutions-afternoon.md:9 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: top\n" "#![no_main]\n" "#![no_std]\n" "\n" "mod exceptions;\n" "mod logger;\n" "mod pl011;\n" -"// ANCHOR_END: top\n" "mod pl031;\n" "\n" "use crate::pl031::Rtc;\n" @@ -21313,7 +21176,6 @@ msgid "" "use arm_gic::{irq_enable, wfi};\n" "use chrono::{TimeZone, Utc};\n" "use core::hint::spin_loop;\n" -"// ANCHOR: imports\n" "use crate::pl011::Uart;\n" "use arm_gic::gicv3::GicV3;\n" "use core::panic::PanicInfo;\n" @@ -21327,14 +21189,12 @@ msgid "" "\n" "/// Base address of the primary PL011 UART.\n" "const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;\n" -"// ANCHOR_END: imports\n" "\n" "/// Base address of the PL031 RTC.\n" "const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;\n" "/// The IRQ used by the PL031 RTC.\n" "const PL031_IRQ: IntId = IntId::spi(2);\n" "\n" -"// ANCHOR: main\n" "#[no_mangle]\n" "extern \"C\" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {\n" " // Safe because `PL011_BASE_ADDRESS` is the base address of a PL011 " @@ -21352,7 +21212,6 @@ msgid "" " let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, " "GICR_BASE_ADDRESS) };\n" " gic.setup();\n" -" // ANCHOR_END: main\n" "\n" " // Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 " "device,\n" @@ -21414,7 +21273,6 @@ msgid "" " );\n" " info!(\"Finished waiting\");\n" "\n" -" // ANCHOR: main_end\n" " system_off::().unwrap();\n" "}\n" "\n" @@ -21424,31 +21282,16 @@ msgid "" " system_off::().unwrap();\n" " loop {}\n" "}\n" -"// ANCHOR_END: main_end\n" "```" msgstr "" -#: src/exercises/bare-metal/solutions-afternoon.md:149 +#: src/exercises/bare-metal/solutions-afternoon.md:127 msgid "`pl031.rs`:" msgstr "" -#: src/exercises/bare-metal/solutions-afternoon.md:151 +#: src/exercises/bare-metal/solutions-afternoon.md:129 msgid "" "```rust\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" "use core::ptr::{addr_of, addr_of_mut};\n" "\n" "#[repr(C, align(4))]\n" @@ -21529,8 +21372,8 @@ msgid "" "\n" " /// Returns whether there is currently an interrupt pending.\n" " ///\n" -" /// This should be true iff `matched` returns true and the interrupt is\n" -" /// masked.\n" +" /// This should be true if and only if `matched` returns true and the\n" +" /// interrupt is masked.\n" " pub fn interrupt_pending(&self) -> bool {\n" " // Safe because we know that self.registers points to the control\n" " // registers of a PL031 device which is appropriately mapped.\n" @@ -21572,26 +21415,11 @@ msgstr "并发编程:上午练习" #: src/exercises/concurrency/solutions-morning.md:5 msgid "([back to exercise](dining-philosophers.md))" -msgstr "" +msgstr "([返回练习](dining-philosophers.md))" #: src/exercises/concurrency/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: Philosopher\n" "use std::sync::{mpsc, Arc, Mutex};\n" "use std::thread;\n" "use std::time::Duration;\n" @@ -21600,29 +21428,23 @@ msgid "" "\n" "struct Philosopher {\n" " name: String,\n" -" // ANCHOR_END: Philosopher\n" " left_fork: Arc>,\n" " right_fork: Arc>,\n" " thoughts: mpsc::SyncSender,\n" "}\n" "\n" -"// ANCHOR: Philosopher-think\n" "impl Philosopher {\n" " fn think(&self) {\n" " self.thoughts\n" " .send(format!(\"Eureka! {} has a new idea!\", &self.name))\n" " .unwrap();\n" " }\n" -" // ANCHOR_END: Philosopher-think\n" "\n" -" // ANCHOR: Philosopher-eat\n" " fn eat(&self) {\n" -" // ANCHOR_END: Philosopher-eat\n" " println!(\"{} is trying to eat\", &self.name);\n" " let left = self.left_fork.lock().unwrap();\n" " let right = self.right_fork.lock().unwrap();\n" "\n" -" // ANCHOR: Philosopher-eat-end\n" " println!(\"{} is eating...\", &self.name);\n" " thread::sleep(Duration::from_millis(10));\n" " }\n" @@ -21632,7 +21454,6 @@ msgid "" " &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n" "\n" "fn main() {\n" -" // ANCHOR_END: Philosopher-eat-end\n" " let (tx, rx) = mpsc::sync_channel(10);\n" "\n" " let forks = (0..PHILOSOPHERS.len())\n" @@ -21641,8 +21462,8 @@ msgid "" "\n" " for i in 0..forks.len() {\n" " let tx = tx.clone();\n" -" let mut left_fork = forks[i].clone();\n" -" let mut right_fork = forks[(i + 1) % forks.len()].clone();\n" +" let mut left_fork = Arc::clone(&forks[i]);\n" +" let mut right_fork = Arc::clone(&forks[(i + 1) % forks.len()]);\n" "\n" " // To avoid a deadlock, we have to break the symmetry\n" " // somewhere. This will swap the forks without deinitializing\n" @@ -21674,6 +21495,191 @@ msgid "" "```" msgstr "" +#: src/exercises/concurrency/solutions-morning.md:82 +msgid "Link Checker" +msgstr "Link 检查器" + +#: src/exercises/concurrency/solutions-morning.md:84 +msgid "([back to exercise](link-checker.md))" +msgstr "([返回练习](link-checker.md))" + +#: src/exercises/concurrency/solutions-morning.md:86 +msgid "" +"```rust,compile_fail\n" +"use std::{sync::Arc, sync::Mutex, sync::mpsc, thread};\n" +"\n" +"use reqwest::{blocking::Client, Url};\n" +"use scraper::{Html, Selector};\n" +"use thiserror::Error;\n" +"\n" +"#[derive(Error, Debug)]\n" +"enum Error {\n" +" #[error(\"request error: {0}\")]\n" +" ReqwestError(#[from] reqwest::Error),\n" +" #[error(\"bad http response: {0}\")]\n" +" BadResponse(String),\n" +"}\n" +"\n" +"#[derive(Debug)]\n" +"struct CrawlCommand {\n" +" url: Url,\n" +" extract_links: bool,\n" +"}\n" +"\n" +"fn visit_page(client: &Client, command: &CrawlCommand) -> Result, " +"Error> {\n" +" println!(\"Checking {:#}\", command.url);\n" +" let response = client.get(command.url.clone()).send()?;\n" +" if !response.status().is_success() {\n" +" return Err(Error::BadResponse(response.status().to_string()));\n" +" }\n" +"\n" +" let mut link_urls = Vec::new();\n" +" if !command.extract_links {\n" +" return Ok(link_urls);\n" +" }\n" +"\n" +" let base_url = response.url().to_owned();\n" +" let body_text = response.text()?;\n" +" let document = Html::parse_document(&body_text);\n" +"\n" +" let selector = Selector::parse(\"a\").unwrap();\n" +" let href_values = document\n" +" .select(&selector)\n" +" .filter_map(|element| element.value().attr(\"href\"));\n" +" for href in href_values {\n" +" match base_url.join(href) {\n" +" Ok(link_url) => {\n" +" link_urls.push(link_url);\n" +" }\n" +" Err(err) => {\n" +" println!(\"On {base_url:#}: ignored unparsable {href:?}: " +"{err}\");\n" +" }\n" +" }\n" +" }\n" +" Ok(link_urls)\n" +"}\n" +"\n" +"struct CrawlState {\n" +" domain: String,\n" +" visited_pages: std::collections::HashSet,\n" +"}\n" +"\n" +"impl CrawlState {\n" +" fn new(start_url: &Url) -> CrawlState {\n" +" let mut visited_pages = std::collections::HashSet::new();\n" +" visited_pages.insert(start_url.as_str().to_string());\n" +" CrawlState {\n" +" domain: start_url.domain().unwrap().to_string(),\n" +" visited_pages,\n" +" }\n" +" }\n" +"\n" +" /// Determine whether links within the given page should be extracted.\n" +" fn should_extract_links(&self, url: &Url) -> bool {\n" +" let Some(url_domain) = url.domain() else {\n" +" return false;\n" +" };\n" +" url_domain == self.domain\n" +" }\n" +"\n" +" /// Mark the given page as visited, returning true if it had already\n" +" /// been visited.\n" +" fn mark_visited(&mut self, url: &Url) -> bool {\n" +" self.visited_pages.insert(url.as_str().to_string())\n" +" }\n" +"}\n" +"\n" +"type CrawlResult = Result, (Url, Error)>;\n" +"fn spawn_crawler_threads(\n" +" command_receiver: mpsc::Receiver,\n" +" result_sender: mpsc::Sender,\n" +" thread_count: u32,\n" +") {\n" +" let command_receiver = Arc::new(Mutex::new(command_receiver));\n" +"\n" +" for _ in 0..thread_count {\n" +" let result_sender = result_sender.clone();\n" +" let command_receiver = command_receiver.clone();\n" +" thread::spawn(move || {\n" +" let client = Client::new();\n" +" loop {\n" +" let command_result = {\n" +" let receiver_guard = command_receiver.lock().unwrap();\n" +" receiver_guard.recv()\n" +" };\n" +" let Ok(crawl_command) = command_result else {\n" +" // The sender got dropped. No more commands coming in.\n" +" break;\n" +" };\n" +" let crawl_result = match visit_page(&client, &crawl_command) " +"{\n" +" Ok(link_urls) => Ok(link_urls),\n" +" Err(error) => Err((crawl_command.url, error)),\n" +" };\n" +" result_sender.send(crawl_result).unwrap();\n" +" }\n" +" });\n" +" }\n" +"}\n" +"\n" +"fn control_crawl(\n" +" start_url: Url,\n" +" command_sender: mpsc::Sender,\n" +" result_receiver: mpsc::Receiver,\n" +") -> Vec {\n" +" let mut crawl_state = CrawlState::new(&start_url);\n" +" let start_command = CrawlCommand { url: start_url, extract_links: " +"true };\n" +" command_sender.send(start_command).unwrap();\n" +" let mut pending_urls = 1;\n" +"\n" +" let mut bad_urls = Vec::new();\n" +" while pending_urls > 0 {\n" +" let crawl_result = result_receiver.recv().unwrap();\n" +" pending_urls -= 1;\n" +"\n" +" match crawl_result {\n" +" Ok(link_urls) => {\n" +" for url in link_urls {\n" +" if crawl_state.mark_visited(&url) {\n" +" let extract_links = crawl_state." +"should_extract_links(&url);\n" +" let crawl_command = CrawlCommand { url, " +"extract_links };\n" +" command_sender.send(crawl_command).unwrap();\n" +" pending_urls += 1;\n" +" }\n" +" }\n" +" }\n" +" Err((url, error)) => {\n" +" bad_urls.push(url);\n" +" println!(\"Got crawling error: {:#}\", error);\n" +" continue;\n" +" }\n" +" }\n" +" }\n" +" bad_urls\n" +"}\n" +"\n" +"fn check_links(start_url: Url) -> Vec {\n" +" let (result_sender, result_receiver) = mpsc::channel::();\n" +" let (command_sender, command_receiver) = mpsc::channel::" +"();\n" +" spawn_crawler_threads(command_receiver, result_sender, 16);\n" +" control_crawl(start_url, command_sender, result_receiver)\n" +"}\n" +"\n" +"fn main() {\n" +" let start_url = reqwest::Url::parse(\"https://www.google.org\")." +"unwrap();\n" +" let bad_urls = check_links(start_url);\n" +" println!(\"Bad URLs: {:#?}\", bad_urls);\n" +"}\n" +"```" +msgstr "" + #: src/exercises/concurrency/solutions-afternoon.md:1 msgid "Concurrency Afternoon Exercise" msgstr "并发编程:下午练习" @@ -21685,21 +21691,6 @@ msgstr "([返回练习](dining-philosophers-async.md))" #: src/exercises/concurrency/solutions-afternoon.md:7 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: Philosopher\n" "use std::sync::Arc;\n" "use tokio::time;\n" "use tokio::sync::mpsc::{self, Sender};\n" @@ -21709,13 +21700,11 @@ msgid "" "\n" "struct Philosopher {\n" " name: String,\n" -" // ANCHOR_END: Philosopher\n" " left_fork: Arc>,\n" " right_fork: Arc>,\n" " thoughts: Sender,\n" "}\n" "\n" -"// ANCHOR: Philosopher-think\n" "impl Philosopher {\n" " async fn think(&self) {\n" " self.thoughts\n" @@ -21723,12 +21712,9 @@ msgid "" "await\n" " .unwrap();\n" " }\n" -" // ANCHOR_END: Philosopher-think\n" "\n" -" // ANCHOR: Philosopher-eat\n" " async fn eat(&self) {\n" " // Pick up forks...\n" -" // ANCHOR_END: Philosopher-eat\n" " let _first_lock = self.left_fork.lock().await;\n" " // Add a delay before picking the second fork to allow the " "execution\n" @@ -21736,13 +21722,10 @@ msgid "" " time::sleep(time::Duration::from_millis(1)).await;\n" " let _second_lock = self.right_fork.lock().await;\n" "\n" -" // ANCHOR: Philosopher-eat-body\n" " println!(\"{} is eating...\", &self.name);\n" " time::sleep(time::Duration::from_millis(5)).await;\n" -" // ANCHOR_END: Philosopher-eat-body\n" "\n" " // The locks are dropped here\n" -" // ANCHOR: Philosopher-eat-end\n" " }\n" "}\n" "\n" @@ -21751,7 +21734,6 @@ msgid "" "\n" "#[tokio::main]\n" "async fn main() {\n" -" // ANCHOR_END: Philosopher-eat-end\n" " // Create forks\n" " let mut forks = vec![];\n" " (0..PHILOSOPHERS.len()).for_each(|_| forks.push(Arc::new(Mutex::" @@ -21762,14 +21744,19 @@ msgid "" " let mut philosophers = vec![];\n" " let (tx, rx) = mpsc::channel(10);\n" " for (i, name) in PHILOSOPHERS.iter().enumerate() {\n" -" let left_fork = forks[i].clone();\n" -" let right_fork = forks[(i + 1) % PHILOSOPHERS.len()].clone();\n" +" let left_fork = Arc::clone(&forks[i]);\n" +" let right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS." +"len()]);\n" +" // To avoid a deadlock, we have to break the symmetry\n" +" // somewhere. This will swap the forks without deinitializing\n" +" // either of them.\n" +" if i == 0 {\n" +" std::mem::swap(&mut left_fork, &mut right_fork);\n" +" }\n" " philosophers.push(Philosopher {\n" " name: name.to_string(),\n" -" left_fork: if i % 2 == 0 { left_fork.clone() } else " -"{ right_fork.clone() },\n" -" right_fork: if i % 2 == 0 { right_fork } else " -"{ left_fork },\n" +" left_fork,\n" +" right_fork,\n" " thoughts: tx.clone(),\n" " });\n" " }\n" @@ -21796,43 +21783,26 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:115 +#: src/exercises/concurrency/solutions-afternoon.md:97 msgid "([back to exercise](chat-app.md))" -msgstr "" +msgstr "([返回练习](chat-app.md))" -#: src/exercises/concurrency/solutions-afternoon.md:119 +#: src/exercises/concurrency/solutions-afternoon.md:101 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" "use futures_util::sink::SinkExt;\n" +"use futures_util::stream::StreamExt;\n" "use std::error::Error;\n" "use std::net::SocketAddr;\n" "use tokio::net::{TcpListener, TcpStream};\n" "use tokio::sync::broadcast::{channel, Sender};\n" "use tokio_websockets::{Message, ServerBuilder, WebsocketStream};\n" -"// ANCHOR_END: setup\n" "\n" -"// ANCHOR: handle_connection\n" "async fn handle_connection(\n" " addr: SocketAddr,\n" " mut ws_stream: WebsocketStream,\n" " bcast_tx: Sender,\n" ") -> Result<(), Box> {\n" -" // ANCHOR_END: handle_connection\n" "\n" " ws_stream\n" " .send(Message::text(\"Welcome to chat! Type a message\".into()))\n" @@ -21848,9 +21818,10 @@ msgid "" " incoming = ws_stream.next() => {\n" " match incoming {\n" " Some(Ok(msg)) => {\n" -" let msg = msg.as_text()?;\n" -" println!(\"From client {addr:?} {msg:?}\");\n" -" bcast_tx.send(msg.into())?;\n" +" if let Some(text) = msg.as_text() {\n" +" println!(\"From client {addr:?} {text:?}\");\n" +" bcast_tx.send(text.into())?;\n" +" }\n" " }\n" " Some(Err(err)) => return Err(err.into()),\n" " None => return Ok(()),\n" @@ -21861,7 +21832,6 @@ msgid "" " }\n" " }\n" " }\n" -" // ANCHOR: main\n" "}\n" "\n" "#[tokio::main]\n" @@ -21883,28 +21853,13 @@ msgid "" " });\n" " }\n" "}\n" -"// ANCHOR_END: main\n" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:204 +#: src/exercises/concurrency/solutions-afternoon.md:168 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" +"use futures_util::stream::StreamExt;\n" "use futures_util::SinkExt;\n" "use http::Uri;\n" "use tokio::io::{AsyncBufReadExt, BufReader};\n" @@ -21912,31 +21867,32 @@ msgid "" "\n" "#[tokio::main]\n" "async fn main() -> Result<(), tokio_websockets::Error> {\n" -" let mut ws_stream = ClientBuilder::from_uri(Uri::" -"from_static(\"ws://127.0.0.1:2000\"))\n" -" .connect()\n" -" .await?;\n" +" let (mut ws_stream, _) =\n" +" ClientBuilder::from_uri(Uri::from_static(\"ws://127.0.0.1:2000\"))\n" +" .connect()\n" +" .await?;\n" "\n" " let stdin = tokio::io::stdin();\n" -" let mut stdin = BufReader::new(stdin);\n" +" let mut stdin = BufReader::new(stdin).lines();\n" "\n" -" // ANCHOR_END: setup\n" " // Continuous loop for concurrently sending and receiving messages.\n" " loop {\n" -" let mut line = String::new();\n" " tokio::select! {\n" " incoming = ws_stream.next() => {\n" " match incoming {\n" -" Some(Ok(msg)) => println!(\"From server: {}\", msg." -"as_text()?),\n" +" Some(Ok(msg)) => {\n" +" if let Some(text) = msg.as_text() {\n" +" println!(\"From server: {}\", text);\n" +" }\n" +" },\n" " Some(Err(err)) => return Err(err.into()),\n" " None => return Ok(()),\n" " }\n" " }\n" -" res = stdin.read_line(&mut line) => {\n" +" res = stdin.next_line() => {\n" " match res {\n" -" Ok(0) => return Ok(()),\n" -" Ok(_) => ws_stream.send(Message::text(line.trim_end()." +" Ok(None) => return Ok(()),\n" +" Ok(Some(line)) => ws_stream.send(Message::text(line." "to_string())).await?,\n" " Err(err) => return Err(err.into()),\n" " }\n" @@ -21948,90 +21904,764 @@ msgid "" "```" msgstr "" -#: src/running-the-course/translations.md:11 -msgid "Incomplete Translations" -msgstr "未完成的翻译" - -#: src/running-the-course/translations.md:13 -msgid "" -"There is a large number of in-progress translations. We link to the most " -"recently updated translations:" -msgstr "多数语言版本仍在翻译中。我们会提供最近更新的翻译的链接:" +#~ msgid "Comparison" +#~ msgstr "比较" + +#~ msgid "" +#~ "[![Build workflow](https://img.shields.io/github/actions/workflow/status/" +#~ "google/comprehensive-rust/build.yml?style=flat-square)](https://github." +#~ "com/google/comprehensive-rust/actions/workflows/build.yml?" +#~ "query=branch%3Amain)" +#~ msgstr "" +#~ "[![构建工作流](https://img.shields.io/github/actions/workflow/status/" +#~ "google/comprehensive-rust/build.yml?style=flat-square)](https://github." +#~ "com/google/comprehensive-rust/actions/workflows/build.yml?" +#~ "query=branch%3Amain)" + +#~ msgid "Build workflow" +#~ msgstr "构建工作流" + +#~ msgid "GitHub contributors" +#~ msgstr "GitHub 贡献者" + +#~ msgid "GitHub stars" +#~ msgstr "GitHub stars" + +#~ msgid "" +#~ "[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-" +#~ "rust?style=flat-square)](https://github.com/google/comprehensive-rust/" +#~ "stargazers)" +#~ msgstr "" +#~ "[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-" +#~ "rust?style=flat-square)](https://github.com/google/comprehensive-rust/" +#~ "stargazers)" + +#~ msgid "The course is fast paced and covers a lot of ground:" +#~ msgstr "本课程节奏紧凑,涵盖内容广泛:" + +#~ msgid "Day 1: Basic Rust, ownership and the borrow checker." +#~ msgstr "第 1 天:Rust 的基础知识、所有权和借用检查器。" + +#~ msgid "Concurrency" +#~ msgstr "并发" + +#~ msgid "" +#~ "```shell\n" +#~ "cargo init concurrency\n" +#~ "cd concurrency\n" +#~ "cargo add tokio --features full\n" +#~ "cargo run\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "cargo init concurrency\n" +#~ "cd concurrency\n" +#~ "cargo add tokio --features full\n" +#~ "cargo run\n" +#~ "```" + +#~ msgid "" +#~ "\\[Brazilian Portuguese\\]\\[pt-BR\\] by \\[@rastringer\\] and " +#~ "\\[@hugojacob\\]." +#~ msgstr "" +#~ "[巴西葡萄牙语](https://google.github.io/comprehensive-rust/pt-BR/):" +#~ "[@rastringer](https://github.com/rastringer) 和 [@hugojacob](https://" +#~ "github.com/hugojacob)。" + +#~ msgid "" +#~ "\\[Korean\\]\\[ko\\] by \\[@keispace\\], \\[@jiyongp\\] and " +#~ "\\[@jooyunghan\\]." +#~ msgstr "" +#~ "[韩语](https://google.github.io/comprehensive-rust/ko/):[@keispace]" +#~ "(https://github.com/keispace)、[@jiyongp](https://github.com/jiyongp) 和 " +#~ "[@jooyunghan](https://github.com/jooyunghan)。" + +#~ msgid "Rustup (Recommended)" +#~ msgstr "Rustup(推荐)" + +#~ msgid "" +#~ "You can follow the instructions to install cargo and rust compiler, among " +#~ "other standard ecosystem tools with the [rustup](https://rust-analyzer." +#~ "github.io/) tool, which is maintained by the Rust Foundation." +#~ msgstr "" +#~ "你可以按照说明,使用由 Rust 基金会维护的 [rustup](https://rust-analyzer." +#~ "github.io/) 工具安装 cargo 和 rust 编译器,以及其他标准的生态系统工具。" + +#~ msgid "Package Managers" +#~ msgstr "软件包管理系统" + +#~ msgid "Debian" +#~ msgstr "Debian" + +#~ msgid "" +#~ "```shell\n" +#~ "$ sudo apt install cargo rust-src rustfmt\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ sudo apt install cargo rust-src rustfmt\n" +#~ "```" + +#~ msgid "" +#~ "This will allow \\[rust-analyzer\\]\\[1\\] to jump to the definitions. We " +#~ "suggest using [VS Code](https://code.visualstudio.com/) to edit the code " +#~ "(but any LSP compatible editor works)." +#~ msgstr "" +#~ "这将允许 \\[rust-analyzer\\]\\[1\\] 跳到定义。我们建议使用 [VS Code]" +#~ "(https://code.visualstudio.com/) 来编写代码(也可使用任何兼容 LSP 的编辑" +#~ "器)。" + +#~ msgid "" +#~ "Some folks also like to use the [JetBrains](https://www.jetbrains.com/" +#~ "clion/) family of IDEs, which do their own analysis but have their own " +#~ "tradeoffs. If you prefer them, you can install the [Rust Plugin](https://" +#~ "www.jetbrains.com/rust/). Please take note that as of January 2023 " +#~ "debugging only works on the CLion version of the JetBrains IDEA suite." +#~ msgstr "" +#~ "有些人还喜欢使用 [JetBrains](https://www.jetbrains.com/clion/) IDE 系列自" +#~ "行分析,但会根据自身情况加以权衡。如果你想使用这些工具,可以安装 [Rust 插" +#~ "件](https://www.jetbrains.com/rust/)。请注意,自 2023 年 1 月起,调试仅适" +#~ "用于 CLion 版本的 JetBrains IDEA 套件。" + +#~ msgid "" +#~ "```shell\n" +#~ "% rustc --version\n" +#~ "rustc 1.69.0 (84c898d65 2023-04-16)\n" +#~ "% cargo --version\n" +#~ "cargo 1.69.0 (6e9a83356 2023-04-12)\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "% rustc --version\n" +#~ "rustc 1.69.0 (84c898d65 2023-04-16)\n" +#~ "% cargo --version\n" +#~ "cargo 1.69.0 (6e9a83356 2023-04-12)\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ cargo new exercise\n" +#~ " Created binary (application) `exercise` package\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ cargo new exercise\n" +#~ " Created binary (application) `exercise` package\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ cd exercise\n" +#~ "$ cargo run\n" +#~ " Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" +#~ " Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" +#~ " Running `target/debug/exercise`\n" +#~ "Hello, world!\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ cd exercise\n" +#~ "$ cargo run\n" +#~ " Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" +#~ " Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" +#~ " Running `target/debug/exercise`\n" +#~ "Hello, world!\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ cargo run\n" +#~ " Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" +#~ " Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" +#~ " Running `target/debug/exercise`\n" +#~ "Edit me!\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ cargo run\n" +#~ " Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" +#~ " Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" +#~ " Running `target/debug/exercise`\n" +#~ "Edit me!\n" +#~ "```" + +#~ msgid "" +#~ "```rust,editable,compile_fail\n" +#~ "fn new(width: u32, height: u32) -> Rectangle {\n" +#~ " Rectangle { width, height }\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,editable,compile_fail\n" +#~ "fn new(width: u32, height: u32) -> Rectangle {\n" +#~ " Rectangle { width, height }\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```rust\n" +#~ "let array = [10, 20, 30];\n" +#~ "```" +#~ msgstr "" +#~ "```rust\n" +#~ "let array = [10, 20, 30];\n" +#~ "```" + +#~ msgid "" +#~ "Note that since `println!` is a macro, `x` is not moved, even using the " +#~ "function like syntax of `println!(\"x: {}\", x)`" +#~ msgstr "" +#~ "需要注意的是由于 `println!` 是一个宏,尽管使用了一个 `println!(\"x: {}\", " +#~ "x)` 这样形如函数的语法,`x` 也不会被移动。" + +#~ msgid "Global state is managed with static and constant variables." +#~ msgstr "全局的状态是由静态变量和常数变量来管理的。" + +#~ msgid "You can declare compile-time constants:" +#~ msgstr "你可以声明编译期 (compile-time) 常量:" + +#~ msgid "You can also declare static variables:" +#~ msgstr "你也可以声明静态变量:" + +#~ msgid "" +#~ "We will look at mutating static data in the [chapter on Unsafe Rust](../" +#~ "unsafe.md)." +#~ msgstr "我们会在[关于不安全的Rust的章节](../unsafe.md)中研究改变静态数据。" + +#~ msgid "" +#~ "```bob\n" +#~ " Stack Heap\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| H | e | l | l | o | :\n" +#~ ": | len | 5 | : : +----+----+----+----+----+ :\n" +#~ ": | capacity | 5 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" +#~ msgstr "" +#~ "```bob\n" +#~ " 栈 堆\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| H | e | l | l | o | :\n" +#~ ": | len | 5 | : : +----+----+----+----+----+ :\n" +#~ ": | capacity | 5 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" + +#~ msgid "" +#~ "Depending on which abstraction (or combination of abstractions) you " +#~ "choose, can be a single unique pointer, reference counted, or atomically " +#~ "reference counted." +#~ msgstr "" +#~ "根据你选择的抽象(或抽象组合),可以是单个唯一指针,也可以是引用计数,或原" +#~ "子引用计数。" + +#~ msgid "Here is a rough comparison of the memory management techniques." +#~ msgstr "下面是对内存管理技术的粗略比较。" + +#~ msgid "Pros of Different Memory Management Techniques" +#~ msgstr "不同内存管理技术的优点" + +#~ msgid "Manual like C:" +#~ msgstr "手动(如 C):" + +#~ msgid "No runtime overhead." +#~ msgstr "无运行时开销。" + +#~ msgid "Automatic like Java:" +#~ msgstr "自动(如 Java):" + +#~ msgid "Fully automatic." +#~ msgstr "完全自动。" + +#~ msgid "Safe and correct." +#~ msgstr "安全且正确。" + +#~ msgid "Scope-based like C++:" +#~ msgstr "基于作用域(如 C++):" + +#~ msgid "Partially automatic." +#~ msgstr "部分自动。" + +#~ msgid "Compiler-enforced scope-based like Rust:" +#~ msgstr "由编译器强制执行、基于作用域(如 Rust):" + +#~ msgid "Enforced by compiler." +#~ msgstr "由编译器强制执行。" + +#~ msgid "Cons of Different Memory Management Techniques" +#~ msgstr "不同内存管理技术的缺点" + +#~ msgid "Use-after-free." +#~ msgstr "释放后使用。" + +#~ msgid "Double-frees." +#~ msgstr "双重释放。" + +#~ msgid "Memory leaks." +#~ msgstr "内存泄漏。" + +#~ msgid "Garbage collection pauses." +#~ msgstr "垃圾回收暂停。" + +#~ msgid "Destructor delays." +#~ msgstr "析构函数延迟。" + +#~ msgid "Complex, opt-in by programmer." +#~ msgstr "复杂,由程序员自选。" + +#~ msgid "Potential for use-after-free." +#~ msgstr "有望释放后使用。" + +#~ msgid "Compiler-enforced and scope-based like Rust:" +#~ msgstr "由编译器强制执行、基于作用域(如 Rust):" + +#~ msgid "Some upfront complexity." +#~ msgstr "前期有些复杂。" + +#~ msgid "Can reject valid programs." +#~ msgstr "可能拒绝有效的程序。" + +#~ msgid "The data was _moved_ from `s1` and `s1` is no longer accessible." +#~ msgstr "数据从 `s1`“移出”,且 `s1` 不再可供访问。" + +#~ msgid "" +#~ "```bob\n" +#~ " Stack Heap\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| R | u | s | t | :\n" +#~ ": | len | 4 | : : +----+----+----+----+ :\n" +#~ ": | capacity | 4 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - - - -'\n" +#~ ": :\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" +#~ msgstr "" +#~ "```bob\n" +#~ " 栈 堆\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| R | u | s | t | :\n" +#~ ": | len | 4 | : : +----+----+----+----+ :\n" +#~ ": | capacity | 4 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - - - -'\n" +#~ ": :\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" + +#~ msgid "" +#~ "```bob\n" +#~ " Stack Heap\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+--+--+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" +#~ msgstr "" +#~ "```bob\n" +#~ " 栈 堆\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+--+--+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" + +#~ msgid "" +#~ "```bob\n" +#~ " Stack Heap\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+--+--+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : : :\n" +#~ ": s2 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" +#~ msgstr "" +#~ "```bob\n" +#~ " 栈 堆\n" +#~ ".- - - - - - - - - - - - - -. .- - - - - - - - - - - -.\n" +#~ ": : : :\n" +#~ ": s1 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+--+--+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : : :\n" +#~ ": s2 : : :\n" +#~ ": +-----------+-------+ : : +----+----+----+ :\n" +#~ ": | ptr | o---+---+-----+-->| C | p | p | :\n" +#~ ": | len | 3 | : : +----+----+----+ :\n" +#~ ": | capacity | 3 | : : :\n" +#~ ": +-----------+-------+ : : :\n" +#~ ": : `- - - - - - - - - - - -'\n" +#~ "`- - - - - - - - - - - - - -'\n" +#~ "```" + +#~ msgid "Simple struct which tracks health statistics." +#~ msgstr "实现一个简单的结构体中的方法,用于追踪健康统计数据。" + +#~ msgid "Multiple structs and enums for a drawing library." +#~ msgstr "实现多个结构体和枚举中的方法,用于绘图库。" + +#~ msgid "" +#~ "```rust,editable\n" +#~ "fn main() {\n" +#~ " let mut x = 10;\n" +#~ " if x % 2 == 0 {\n" +#~ " x = x / 2;\n" +#~ " } else {\n" +#~ " x = 3 * x + 1;\n" +#~ " }\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,editable\n" +#~ "fn main() {\n" +#~ " let mut x = 10;\n" +#~ " if x % 2 == 0 {\n" +#~ " x = x / 2;\n" +#~ " } else {\n" +#~ " x = 3 * x + 1;\n" +#~ " }\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```rust,editable\n" +#~ "fn main() {\n" +#~ " let mut x = 10;\n" +#~ " x = if x % 2 == 0 {\n" +#~ " x / 2\n" +#~ " } else {\n" +#~ " 3 * x + 1\n" +#~ " };\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,editable\n" +#~ "fn main() {\n" +#~ " let mut x = 10;\n" +#~ " x = if x % 2 == 0 {\n" +#~ " x / 2\n" +#~ " } else {\n" +#~ " 3 * x + 1\n" +#~ " };\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "`if let` can be more concise than `match`, e.g., when only one case is " +#~ "interesting. In contrast, `match` requires all branches to be covered." +#~ msgstr "" +#~ "`if let` 可能比 `match` 更简洁,例如,当只关注一种情况时。相比之下," +#~ "`match` 要求覆盖所有分支。" + +#~ msgid "`binary_search` returns `Result`." +#~ msgstr "`binary_search` 会返回 `Result`。" + +#~ msgid "If found, `Result::Ok` holds the index where the element is found." +#~ msgstr "如果找到该元素,`Result::Ok` 会保留该元素所在位置的索引。" + +#~ msgid "" +#~ "Otherwise, `Result::Err` contains the index where such an element should " +#~ "be inserted." +#~ msgstr "如果没有找到该元素,`Result::Err` 会包含应插入这类元素的索引。" + +#~ msgid "" +#~ "If you need to mutate the data inside an `Rc`, you will need to wrap the " +#~ "data in a type such as [`Cell` or `RefCell`](../concurrency/shared_state/" +#~ "arc.md)." +#~ msgstr "" +#~ "如果您需要在 `Rc` 内修改数据,则需将数据封装入 [`Cell` 或 `RefCell`](../" +#~ "concurrency/shared_state/arc.md) 等类型中。" + +#~ msgid "Like C++'s `std::shared_ptr`." +#~ msgstr "类似 C++ 的 `std::shared_ptr`。" + +#~ msgid "" +#~ "Compare the different datatypes mentioned. `Box` enables (im)mutable " +#~ "borrows that are enforced at compile time. `RefCell` enables (im)mutable " +#~ "borrows that are enforced at run time and will panic if it fails at " +#~ "runtime." +#~ msgstr "" +#~ "比较提及的不同数据类型。`Box` 可启用在编译时强制执行的可变/不可变借用。" +#~ "`RefCell` 可启用在运行时强制执行的可变/不可变借用,如果在运行时失败,将产" +#~ "生恐慌。" + +#~ msgid "" +#~ "```rust,ignore\n" +#~ "fn duplicate(a: T) -> (T, T)\n" +#~ "where\n" +#~ " T: Clone,\n" +#~ "{\n" +#~ " (a.clone(), a.clone())\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,ignore\n" +#~ "fn duplicate(a: T) -> (T, T)\n" +#~ "where\n" +#~ " T: Clone,\n" +#~ "{\n" +#~ " (a.clone(), a.clone())\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```rust,ignore\n" +#~ "match some_expression {\n" +#~ " Ok(value) => value,\n" +#~ " Err(err) => return Err(err),\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,ignore\n" +#~ "match some_expression {\n" +#~ " Ok(value) => value,\n" +#~ " Err(err) => return Err(err),\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```rust,ignore\n" +#~ "some_expression?\n" +#~ "```" +#~ msgstr "" +#~ "```rust,ignore\n" +#~ "some_expression?\n" +#~ "```" + +#~ msgid "" +#~ "```rust,ignore\n" +#~ "expression?\n" +#~ "```" +#~ msgstr "" +#~ "```rust,ignore\n" +#~ "expression?\n" +#~ "```" + +#~ msgid "" +#~ "```rust,ignore\n" +#~ "match expression {\n" +#~ " Ok(value) => value,\n" +#~ " Err(err) => return Err(From::from(err)),\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```rust,ignore\n" +#~ "match expression {\n" +#~ " Ok(value) => value,\n" +#~ " Err(err) => return Err(From::from(err)),\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ source build/envsetup.sh\n" +#~ "$ lunch aosp_cf_x86_64_phone-userdebug\n" +#~ "$ acloud create\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ source build/envsetup.sh\n" +#~ "$ lunch aosp_cf_x86_64_phone-userdebug\n" +#~ "$ acloud create\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ m hello_rust\n" +#~ "$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\n" +#~ "$ adb shell /data/local/tmp/hello_rust\n" +#~ "Hello from Rust!\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ m hello_rust\n" +#~ "$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp\n" +#~ "$ adb shell /data/local/tmp/hello_rust\n" +#~ "Hello from Rust!\n" +#~ "```" + +#~ msgid "" +#~ "```java\n" +#~ "package com.example.birthdayservice;\n" +#~ "\n" +#~ "/** Birthday service interface. */\n" +#~ "interface IBirthdayService {\n" +#~ " /** Generate a Happy Birthday message. */\n" +#~ " String wishHappyBirthday(String name, int years);\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```java\n" +#~ "package com.example.birthdayservice;\n" +#~ "\n" +#~ "/** 生日服务接口。 */\n" +#~ "interface IBirthdayService {\n" +#~ " /** 生成一条生日快乐的祝福信息。 */\n" +#~ " String wishHappyBirthday(String name, int years);\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ adb shell service check birthdayservice\n" +#~ "Service birthdayservice: found\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ adb shell service check birthdayservice\n" +#~ "Service birthdayservice: found\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n" +#~ "Result: Parcel(\n" +#~ " 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'\n" +#~ " 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'\n" +#~ " 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n" +#~ " 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n" +#~ " 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n" +#~ " 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n" +#~ " 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n" +#~ " 0x00000070: 00210073 00000000 's.!..... ')\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n" +#~ "Result: Parcel(\n" +#~ " 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'\n" +#~ " 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'\n" +#~ " 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n" +#~ " 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n" +#~ " 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n" +#~ " 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n" +#~ " 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n" +#~ " 0x00000070: 00210073 00000000 's.!..... ')\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ m birthday_client\n" +#~ "$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/" +#~ "tmp\n" +#~ "$ adb shell /data/local/tmp/birthday_client Charlie 60\n" +#~ "Happy Birthday Charlie, congratulations with the 60 years!\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ m birthday_client\n" +#~ "$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/" +#~ "tmp\n" +#~ "$ adb shell /data/local/tmp/birthday_client Charlie 60\n" +#~ "Happy Birthday Charlie, congratulations with the 60 years!\n" +#~ "```" + +#~ msgid "" +#~ "```java\n" +#~ "package com.example.birthdayservice;\n" +#~ "\n" +#~ "/** Birthday service interface. */\n" +#~ "interface IBirthdayService {\n" +#~ " /** Generate a Happy Birthday message. */\n" +#~ " String wishHappyBirthday(String name, int years, in String[] text);\n" +#~ "}\n" +#~ "```" +#~ msgstr "" +#~ "```java\n" +#~ "package com.example.birthdayservice;\n" +#~ "\n" +#~ "/** 生日服务接口。 */\n" +#~ "interface IBirthdayService {\n" +#~ " /** 生成一条生日快乐的祝福信息。 */\n" +#~ " String wishHappyBirthday(String name, int years, in String[] text);\n" +#~ "}\n" +#~ "```" + +#~ msgid "" +#~ "```shell\n" +#~ "$ adb logcat -s rust\n" +#~ "09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting " +#~ "program.\n" +#~ "09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going " +#~ "fine.\n" +#~ "09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went " +#~ "wrong!\n" +#~ "```" +#~ msgstr "" +#~ "```shell\n" +#~ "$ adb logcat -s rust\n" +#~ "09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting " +#~ "program.\n" +#~ "09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going " +#~ "fine.\n" +#~ "09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went " +#~ "wrong!\n" +#~ "```" + +#~ msgid "" +#~ "[WebsocketStream::next()](https://docs.rs/tokio-websockets/0.3.2/" +#~ "tokio_websockets/proto/struct.WebsocketStream.html#method.next): for " +#~ "asynchronously reading messages from a Websocket Stream." +#~ msgstr "" +#~ "[WebsocketStream::next()](https://docs.rs/tokio-websockets/0.3.2/" +#~ "tokio_websockets/proto/struct.WebsocketStream.html#method.next):用于从" +#~ "Websocket流异步读取消息。" -#: src/running-the-course/translations.md:16 src/enums.md:6 #, fuzzy -msgid "\\[French\\]\\[fr\\] by \\[@KookaS\\] and \\[@vcaen\\]." -msgstr "\\[法语\\]\\[fr\\]:\\[@KookaS\\] 和 \\[@vcaen\\]。" +#~ msgid "\\[French\\]\\[fr\\] by \\[@KookaS\\] and \\[@vcaen\\]." +#~ msgstr "\\[法语\\]\\[fr\\]:\\[@KookaS\\] 和 \\[@vcaen\\]。" -#: src/running-the-course/translations.md:17 src/enums.md:7 #, fuzzy -msgid "\\[German\\]\\[de\\] by \\[@Throvn\\] and \\[@ronaldfw\\]." -msgstr "\\[德语\\]\\[de\\]:\\[@Throvn\\] 和 \\[@ronaldfw\\]。" +#~ msgid "\\[German\\]\\[de\\] by \\[@Throvn\\] and \\[@ronaldfw\\]." +#~ msgstr "\\[德语\\]\\[de\\]:\\[@Throvn\\] 和 \\[@ronaldfw\\]。" -#: src/running-the-course/translations.md:18 src/enums.md:8 #, fuzzy -msgid "" -"\\[Japanese\\]\\[ja\\] by \\[@CoinEZ-JPN\\] and \\[@momotaro1105\\]." -"\\```rust,editable fn generate_random_number() -> i32 { // Implementation " -"based on https://xkcd.com/221/ 4 // Chosen by fair dice roll. Guaranteed to " -"be random. }" -msgstr "\\[日语\\]\\[ja\\]:\\[@CoinEZ-JPN\\] 和 \\[@momotaro1105\\]。" - -#: src/running-the-course/translations.md:24 src/enums.md:14 -msgid "\\#\\[derive(Debug)\\] enum CoinFlip { Heads, Tails, }" -msgstr "" - -#: src/running-the-course/translations.md:30 src/enums.md:20 -msgid "" -"fn flip_coin() -> CoinFlip { let random_number = generate_random_number(); " -"if random_number % 2 == 0 { return CoinFlip::Heads; } else { return " -"CoinFlip::Tails; } }" -msgstr "" - -#: src/running-the-course/translations.md:39 src/enums.md:29 -msgid "fn main() { println!(\"You got: {:?}\", flip_coin()); }" -msgstr "" - -#: src/running-the-course/translations.md:42 src/enums.md:32 -msgid "" -"```\n" -"```" -msgstr "" - -#: src/ownership/lifetimes.md:13 -msgid "" -"Lifetimes for function arguments and return values must be fully specified, " -"but Rust allows these to be elided in most cases with [a few simple rules]" -"(https://doc.rust-lang.org/nomicon/lifetime-elision.html)." -msgstr "" -"必须完全指定函数参数和返回值的生命周期, 但 Rust 允许在大多数情况下通过\\[一" -"些简单的 规则\\](https://doc.rust-lang.org/nomicon/lifetime-elision.html)来" -"省略此操作。" - -#: src/ownership/lifetimes-function-calls.md:31 -msgid "" -"Move the declaration of `p2` and `p3` into a new scope (`{ ... }`), " -"resulting in the following code:" -msgstr "将 `p2` 和 `p3` 的声明移至新作用域 (`{ ... }`),以产生以下代码:" - -#: src/ownership/lifetimes-function-calls.md:57 -msgid "" -"Which one is it? The compiler needs to know, so at the call site the " -"returned reference is not used for longer than a variable from where the " -"reference came from." -msgstr "" -"是哪一个呢?编译器需要知道这一点,因此在调用点,返回的引用 的使用时间不会超过" -"引用的来源中的变量。" - -#: src/structs/tuple-structs.md:42 -msgid "Operator overloading is discussed on Day 3 (generics)." -msgstr "运算符过载在第 3 天(泛型)讨论。" - -#: src/structs/tuple-structs.md:43 -msgid "" -"The example is a subtle reference to the [Mars Climate Orbiter](https://en." -"wikipedia.org/wiki/Mars_Climate_Orbiter) failure." -msgstr "" -"此示例巧妙地引用了[火星气候探测者号](https://zh.wikipedia.org/wiki/" -"%E7%81%AB%E6%98%9F%E6%B0%A3%E5%80%99%E6%8E%A2%E6%B8%AC%E8%80%85%E8%99%9F) 的" -"失败事故。" \ No newline at end of file +#~ msgid "" +#~ "\\[Japanese\\]\\[ja\\] by \\[@CoinEZ-JPN\\] and \\[@momotaro1105\\]." +#~ "\\```rust,editable fn generate_random_number() -> i32 { // Implementation " +#~ "based on https://xkcd.com/221/ 4 // Chosen by fair dice roll. Guaranteed " +#~ "to be random. }" +#~ msgstr "\\[日语\\]\\[ja\\]:\\[@CoinEZ-JPN\\] 和 \\[@momotaro1105\\]。"