diff --git a/po/uk.po b/po/uk.po index 63ced83741d2..fed866c5bfae 100644 --- a/po/uk.po +++ b/po/uk.po @@ -2,136 +2,137 @@ msgid "" msgstr "" "Project-Id-Version: Comprehensive Rust 🦀\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2023-06-08 23:41+0200\n" +"PO-Revision-Date: 2023-08-25 14:55-0700\n" "Last-Translator: Andrew Kushyk \n" "Language-Team: \n" +"Language: ua\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ua\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:137 src/SUMMARY.md:195 +#: src/SUMMARY.md:221 src/SUMMARY.md:271 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 "" -#: src/SUMMARY.md:20 src/hello-world.md:1 +#: src/SUMMARY.md:21 src/hello-world.md:1 msgid "Hello World!" msgstr "" -#: 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 "" -#: 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 "" -#: 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 "" -#: src/SUMMARY.md:35 src/SUMMARY.md:82 src/basic-syntax/methods.md:1 +#: src/SUMMARY.md:36 src/SUMMARY.md:107 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:100 src/SUMMARY.md:110 +#: src/SUMMARY.md:129 src/SUMMARY.md:157 src/SUMMARY.md:187 src/SUMMARY.md:214 +#: src/SUMMARY.md:235 src/SUMMARY.md:263 src/SUMMARY.md:285 src/SUMMARY.md:306 #: 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,934 +140,924 @@ 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 "" -#: src/SUMMARY.md:41 +#: src/SUMMARY.md:42 msgid "Day 1: Afternoon" msgstr "" -#: src/SUMMARY.md:43 src/basic-syntax/variables.md:1 -msgid "Variables" +#: src/SUMMARY.md:44 src/SUMMARY.md:298 src/control-flow.md:1 +msgid "Control Flow" msgstr "" -#: src/SUMMARY.md:44 src/basic-syntax/type-inference.md:1 -msgid "Type Inference" +#: src/SUMMARY.md:45 src/control-flow/blocks.md:1 +msgid "Blocks" msgstr "" -#: src/SUMMARY.md:45 -msgid "static & const" +#: src/SUMMARY.md:46 +msgid "if expressions" msgstr "" -#: src/SUMMARY.md:46 src/basic-syntax/scopes-shadowing.md:1 -msgid "Scopes and Shadowing" +#: src/SUMMARY.md:47 +msgid "for expressions" msgstr "" -#: src/SUMMARY.md:47 src/memory-management.md:1 -msgid "Memory Management" +#: src/SUMMARY.md:48 +msgid "while expressions" msgstr "" -#: src/SUMMARY.md:48 -msgid "Stack vs Heap" +#: src/SUMMARY.md:49 +msgid "break & continue" msgstr "" -#: src/SUMMARY.md:49 src/memory-management/stack.md:1 -msgid "Stack Memory" +#: src/SUMMARY.md:50 +msgid "loop expressions" msgstr "" -#: src/SUMMARY.md:50 src/memory-management/manual.md:1 -msgid "Manual Memory Management" +#: src/SUMMARY.md:52 src/basic-syntax/variables.md:1 +msgid "Variables" msgstr "" -#: src/SUMMARY.md:51 src/memory-management/scope-based.md:1 -msgid "Scope-Based Memory Management" +#: src/SUMMARY.md:53 src/basic-syntax/type-inference.md:1 +msgid "Type Inference" msgstr "" -#: src/SUMMARY.md:52 -msgid "Garbage Collection" +#: src/SUMMARY.md:54 +msgid "static & const" msgstr "" -#: src/SUMMARY.md:53 -msgid "Rust Memory Management" +#: src/SUMMARY.md:55 src/basic-syntax/scopes-shadowing.md:1 +msgid "Scopes and Shadowing" msgstr "" -#: src/SUMMARY.md:54 src/memory-management/comparison.md:1 -msgid "Comparison" +#: src/SUMMARY.md:56 src/enums.md:1 +msgid "Enums" msgstr "" -#: src/SUMMARY.md:55 src/ownership.md:1 -msgid "Ownership" +#: src/SUMMARY.md:57 src/enums/variant-payloads.md:1 +msgid "Variant Payloads" msgstr "" -#: src/SUMMARY.md:56 src/ownership/move-semantics.md:1 -msgid "Move Semantics" +#: src/SUMMARY.md:58 src/enums/sizes.md:1 +msgid "Enum Sizes" msgstr "" -#: src/SUMMARY.md:57 src/ownership/moved-strings-rust.md:1 -msgid "Moved Strings in Rust" +#: src/SUMMARY.md:60 src/control-flow/novel.md:1 +msgid "Novel Control Flow" msgstr "" -#: src/SUMMARY.md:58 src/ownership/double-free-modern-cpp.md:1 -msgid "Double Frees in Modern C++" +#: src/SUMMARY.md:61 +msgid "if let expressions" msgstr "" -#: src/SUMMARY.md:59 src/ownership/moves-function-calls.md:1 -msgid "Moves in Function Calls" +#: src/SUMMARY.md:62 +msgid "while let expressions" msgstr "" -#: src/SUMMARY.md:60 src/ownership/copy-clone.md:1 -msgid "Copying and Cloning" +#: src/SUMMARY.md:63 +msgid "match expressions" msgstr "" -#: src/SUMMARY.md:61 src/ownership/borrowing.md:1 -msgid "Borrowing" +#: src/SUMMARY.md:65 src/pattern-matching.md:1 +msgid "Pattern Matching" msgstr "" -#: src/SUMMARY.md:62 src/ownership/shared-unique-borrows.md:1 -msgid "Shared and Unique Borrows" +#: src/SUMMARY.md:66 src/pattern-matching/destructuring-enums.md:1 +msgid "Destructuring Enums" msgstr "" -#: src/SUMMARY.md:63 src/ownership/lifetimes.md:1 -msgid "Lifetimes" +#: src/SUMMARY.md:67 src/pattern-matching/destructuring-structs.md:1 +msgid "Destructuring Structs" msgstr "" -#: src/SUMMARY.md:64 src/ownership/lifetimes-function-calls.md:1 -msgid "Lifetimes in Function Calls" +#: src/SUMMARY.md:68 src/pattern-matching/destructuring-arrays.md:1 +msgid "Destructuring Arrays" msgstr "" -#: src/SUMMARY.md:65 src/ownership/lifetimes-data-structures.md:1 -msgid "Lifetimes in Data Structures" +#: src/SUMMARY.md:69 src/pattern-matching/match-guards.md:1 +msgid "Match Guards" msgstr "" -#: src/SUMMARY.md:67 src/exercises/day-1/book-library.md:1 +#: src/SUMMARY.md:72 src/exercises/day-1/luhn.md:1 #: src/exercises/day-1/solutions-afternoon.md:3 -msgid "Designing a Library" +msgid "Luhn Algorithm" msgstr "" -#: src/SUMMARY.md:68 src/exercises/day-1/iterators-and-ownership.md:1 -msgid "Iterators and Ownership" +#: src/SUMMARY.md:73 +msgid "Pattern Matching (TBD)" msgstr "" -#: src/SUMMARY.md:71 +#: src/SUMMARY.md:75 msgid "Day 2: Morning" msgstr "" -#: src/SUMMARY.md:76 src/structs.md:1 -msgid "Structs" +#: src/SUMMARY.md:81 src/memory-management.md:1 +msgid "Memory Management" msgstr "" -#: src/SUMMARY.md:77 src/structs/tuple-structs.md:1 -msgid "Tuple Structs" +#: src/SUMMARY.md:82 +msgid "Stack vs Heap" msgstr "" -#: src/SUMMARY.md:78 src/structs/field-shorthand.md:1 -msgid "Field Shorthand Syntax" +#: src/SUMMARY.md:83 +msgid "Stack Memory" msgstr "" -#: src/SUMMARY.md:79 src/enums.md:1 -msgid "Enums" +#: src/SUMMARY.md:84 src/memory-management/manual.md:1 +msgid "Manual Memory Management" msgstr "" -#: src/SUMMARY.md:80 src/enums/variant-payloads.md:1 -msgid "Variant Payloads" +#: src/SUMMARY.md:85 src/memory-management/scope-based.md:1 +msgid "Scope-Based Memory Management" msgstr "" -#: src/SUMMARY.md:81 src/enums/sizes.md:1 -msgid "Enum Sizes" +#: src/SUMMARY.md:86 +msgid "Garbage Collection" msgstr "" -#: src/SUMMARY.md:83 src/methods/receiver.md:1 -msgid "Method Receiver" +#: src/SUMMARY.md:87 +msgid "Rust Memory Management" msgstr "" -#: src/SUMMARY.md:84 src/SUMMARY.md:159 src/SUMMARY.md:272 -#: src/methods/example.md:1 src/concurrency/shared_state/example.md:1 -msgid "Example" +#: src/SUMMARY.md:88 src/memory-management/comparison.md:1 +msgid "Comparison" msgstr "" -#: src/SUMMARY.md:85 src/pattern-matching.md:1 -msgid "Pattern Matching" +#: src/SUMMARY.md:89 src/ownership.md:1 +msgid "Ownership" msgstr "" -#: src/SUMMARY.md:86 src/pattern-matching/destructuring-enums.md:1 -msgid "Destructuring Enums" +#: src/SUMMARY.md:90 src/ownership/move-semantics.md:1 +msgid "Move Semantics" msgstr "" -#: src/SUMMARY.md:87 src/pattern-matching/destructuring-structs.md:1 -msgid "Destructuring Structs" +#: src/SUMMARY.md:91 src/ownership/moved-strings-rust.md:1 +msgid "Moved Strings in Rust" msgstr "" -#: src/SUMMARY.md:88 src/pattern-matching/destructuring-arrays.md:1 -msgid "Destructuring Arrays" +#: src/SUMMARY.md:92 +msgid "Double Frees in Modern C++" msgstr "" -#: src/SUMMARY.md:89 src/pattern-matching/match-guards.md:1 -msgid "Match Guards" +#: src/SUMMARY.md:93 src/ownership/moves-function-calls.md:1 +msgid "Moves in Function Calls" msgstr "" -#: src/SUMMARY.md:91 src/exercises/day-2/health-statistics.md:1 -msgid "Health Statistics" +#: src/SUMMARY.md:94 src/ownership/copy-clone.md:1 +msgid "Copying and Cloning" msgstr "" -#: src/SUMMARY.md:92 src/exercises/day-2/solutions-morning.md:3 -msgid "Points and Polygons" +#: src/SUMMARY.md:95 src/ownership/borrowing.md:1 +msgid "Borrowing" msgstr "" -#: src/SUMMARY.md:94 -msgid "Day 2: Afternoon" +#: src/SUMMARY.md:96 src/ownership/shared-unique-borrows.md:1 +msgid "Shared and Unique Borrows" msgstr "" -#: src/SUMMARY.md:96 src/SUMMARY.md:286 src/control-flow.md:1 -msgid "Control Flow" +#: src/SUMMARY.md:97 src/ownership/lifetimes.md:1 +msgid "Lifetimes" msgstr "" -#: src/SUMMARY.md:97 src/control-flow/blocks.md:1 -msgid "Blocks" +#: src/SUMMARY.md:98 src/ownership/lifetimes-function-calls.md:1 +msgid "Lifetimes in Function Calls" msgstr "" -#: src/SUMMARY.md:98 -msgid "if expressions" +#: src/SUMMARY.md:99 src/ownership/lifetimes-data-structures.md:1 +msgid "Lifetimes in Data Structures" msgstr "" -#: src/SUMMARY.md:99 -msgid "if let expressions" +#: src/SUMMARY.md:101 src/exercises/day-2/book-library.md:1 +msgid "Storing Books" msgstr "" -#: src/SUMMARY.md:100 -msgid "while expressions" +#: src/SUMMARY.md:102 src/exercises/day-2/iterators-and-ownership.md:1 +msgid "Iterators and Ownership" msgstr "" -#: src/SUMMARY.md:101 -msgid "while let expressions" +#: src/SUMMARY.md:104 src/structs.md:1 +msgid "Structs" msgstr "" -#: src/SUMMARY.md:102 -msgid "for expressions" +#: src/SUMMARY.md:105 src/structs/tuple-structs.md:1 +msgid "Tuple Structs" msgstr "" -#: src/SUMMARY.md:103 -msgid "loop expressions" +#: src/SUMMARY.md:106 src/structs/field-shorthand.md:1 +msgid "Field Shorthand Syntax" msgstr "" -#: src/SUMMARY.md:104 -msgid "match expressions" +#: src/SUMMARY.md:108 src/methods/receiver.md:1 +msgid "Method Receiver" msgstr "" -#: src/SUMMARY.md:105 -msgid "break & continue" +#: src/SUMMARY.md:109 src/SUMMARY.md:169 src/SUMMARY.md:284 +#: src/methods/example.md:1 src/concurrency/shared_state/example.md:1 +msgid "Example" +msgstr "" + +#: src/SUMMARY.md:111 src/exercises/day-2/health-statistics.md:1 +msgid "Health Statistics" msgstr "" -#: src/SUMMARY.md:106 src/std.md:1 +#: src/SUMMARY.md:113 +msgid "Day 2: Afternoon" +msgstr "" + +#: src/SUMMARY.md:115 src/std.md:1 msgid "Standard Library" msgstr "" -#: src/SUMMARY.md:107 +#: src/SUMMARY.md:116 msgid "Option and Result" msgstr "" -#: src/SUMMARY.md:108 src/std/string.md:1 +#: src/SUMMARY.md:117 src/std/string.md:1 msgid "String" msgstr "" -#: src/SUMMARY.md:109 +#: src/SUMMARY.md:118 msgid "Vec" msgstr "" -#: src/SUMMARY.md:110 +#: src/SUMMARY.md:119 msgid "HashMap" msgstr "" -#: src/SUMMARY.md:111 +#: src/SUMMARY.md:120 msgid "Box" msgstr "" -#: src/SUMMARY.md:112 +#: src/SUMMARY.md:121 msgid "Recursive Data Types" msgstr "" -#: src/SUMMARY.md:113 src/std/box-niche.md:1 +#: src/SUMMARY.md:122 src/std/box-niche.md:1 msgid "Niche Optimization" msgstr "" -#: src/SUMMARY.md:114 +#: src/SUMMARY.md:123 msgid "Rc" msgstr "" -#: src/SUMMARY.md:115 src/modules.md:1 +#: src/SUMMARY.md:124 +msgid "Cell/RefCell" +msgstr "" + +#: src/SUMMARY.md:125 src/modules.md:1 msgid "Modules" msgstr "" -#: src/SUMMARY.md:116 src/modules/visibility.md:1 +#: src/SUMMARY.md:126 src/modules/visibility.md:1 msgid "Visibility" msgstr "" -#: src/SUMMARY.md:117 src/modules/paths.md:1 +#: src/SUMMARY.md:127 src/modules/paths.md:1 msgid "Paths" msgstr "" -#: src/SUMMARY.md:118 src/modules/filesystem.md:1 +#: src/SUMMARY.md:128 src/modules/filesystem.md:1 msgid "Filesystem Hierarchy" msgstr "" -#: src/SUMMARY.md:120 src/exercises/day-2/luhn.md:1 +#: src/SUMMARY.md:130 src/exercises/day-2/strings-iterators.md:1 #: src/exercises/day-2/solutions-afternoon.md:3 -msgid "Luhn Algorithm" -msgstr "" - -#: src/SUMMARY.md:121 src/exercises/day-2/strings-iterators.md:1 -#: src/exercises/day-2/solutions-afternoon.md:97 msgid "Strings and Iterators" msgstr "" -#: src/SUMMARY.md:124 +#: src/SUMMARY.md:133 msgid "Day 3: Morning" msgstr "" -#: src/SUMMARY.md:129 src/generics.md:1 +#: src/SUMMARY.md:138 src/generics.md:1 msgid "Generics" msgstr "" -#: src/SUMMARY.md:130 src/generics/data-types.md:1 +#: src/SUMMARY.md:139 src/generics/data-types.md:1 msgid "Generic Data Types" msgstr "" -#: src/SUMMARY.md:131 src/generics/methods.md:1 +#: src/SUMMARY.md:140 src/generics/methods.md:1 msgid "Generic Methods" msgstr "" -#: src/SUMMARY.md:132 src/generics/monomorphization.md:1 +#: src/SUMMARY.md:141 src/generics/monomorphization.md:1 msgid "Monomorphization" msgstr "" -#: src/SUMMARY.md:133 src/traits.md:1 +#: src/SUMMARY.md:142 src/traits.md:1 msgid "Traits" msgstr "" -#: src/SUMMARY.md:134 src/traits/trait-objects.md:1 +#: src/SUMMARY.md:143 src/traits/trait-objects.md:1 msgid "Trait Objects" msgstr "" -#: src/SUMMARY.md:135 src/traits/deriving-traits.md:1 +#: src/SUMMARY.md:144 src/traits/deriving-traits.md:1 msgid "Deriving Traits" msgstr "" -#: src/SUMMARY.md:136 src/traits/default-methods.md:1 +#: src/SUMMARY.md:145 src/traits/default-methods.md:1 msgid "Default Methods" msgstr "" -#: src/SUMMARY.md:137 src/traits/trait-bounds.md:1 +#: src/SUMMARY.md:146 src/traits/trait-bounds.md:1 msgid "Trait Bounds" msgstr "" -#: src/SUMMARY.md:138 +#: src/SUMMARY.md:147 msgid "impl Trait" msgstr "" -#: src/SUMMARY.md:139 src/traits/important-traits.md:1 +#: src/SUMMARY.md:148 src/traits/important-traits.md:1 msgid "Important Traits" msgstr "" -#: src/SUMMARY.md:140 +#: src/SUMMARY.md:149 msgid "Iterator" msgstr "" -#: src/SUMMARY.md:141 src/traits/from-iterator.md:1 +#: src/SUMMARY.md:150 src/traits/from-iterator.md:1 msgid "FromIterator" msgstr "" -#: src/SUMMARY.md:142 +#: src/SUMMARY.md:151 msgid "From and Into" msgstr "" -#: src/SUMMARY.md:143 +#: src/SUMMARY.md:152 msgid "Read and Write" msgstr "" -#: src/SUMMARY.md:144 +#: src/SUMMARY.md:153 msgid "Drop" msgstr "" -#: src/SUMMARY.md:145 +#: src/SUMMARY.md:154 msgid "Default" msgstr "" -#: src/SUMMARY.md:146 +#: src/SUMMARY.md:155 msgid "Operators: Add, Mul, ..." msgstr "" -#: src/SUMMARY.md:147 +#: src/SUMMARY.md:156 msgid "Closures: Fn, FnMut, FnOnce" msgstr "" -#: src/SUMMARY.md:149 src/exercises/day-3/simple-gui.md:1 +#: src/SUMMARY.md:158 src/exercises/day-3/simple-gui.md:1 #: src/exercises/day-3/solutions-morning.md:3 msgid "A Simple GUI Library" msgstr "" -#: src/SUMMARY.md:151 +#: src/SUMMARY.md:159 src/exercises/day-3/solutions-morning.md:175 +msgid "Points and Polygons" +msgstr "" + +#: src/SUMMARY.md:161 msgid "Day 3: Afternoon" msgstr "" -#: src/SUMMARY.md:153 src/error-handling.md:1 +#: src/SUMMARY.md:163 src/error-handling.md:1 msgid "Error Handling" msgstr "" -#: src/SUMMARY.md:154 src/error-handling/panics.md:1 +#: src/SUMMARY.md:164 src/error-handling/panics.md:1 msgid "Panics" msgstr "" -#: src/SUMMARY.md:155 +#: src/SUMMARY.md:165 msgid "Catching Stack Unwinding" msgstr "" -#: src/SUMMARY.md:156 +#: src/SUMMARY.md:166 msgid "Structured Error Handling" msgstr "" -#: src/SUMMARY.md:157 +#: src/SUMMARY.md:167 msgid "Propagating Errors with ?" msgstr "" -#: src/SUMMARY.md:158 src/error-handling/converting-error-types.md:1 +#: src/SUMMARY.md:168 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:170 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:171 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:172 src/error-handling/error-contexts.md:1 msgid "Adding Context to Errors" msgstr "" -#: src/SUMMARY.md:163 src/testing.md:1 +#: src/SUMMARY.md:173 src/testing.md:1 msgid "Testing" msgstr "" -#: src/SUMMARY.md:164 src/testing/unit-tests.md:1 +#: src/SUMMARY.md:174 src/testing/unit-tests.md:1 msgid "Unit Tests" msgstr "" -#: src/SUMMARY.md:165 src/testing/test-modules.md:1 +#: src/SUMMARY.md:175 src/testing/test-modules.md:1 msgid "Test Modules" msgstr "" -#: src/SUMMARY.md:166 src/testing/doc-tests.md:1 +#: src/SUMMARY.md:176 src/testing/doc-tests.md:1 msgid "Documentation Tests" msgstr "" -#: src/SUMMARY.md:167 src/testing/integration-tests.md:1 +#: src/SUMMARY.md:177 src/testing/integration-tests.md:1 msgid "Integration Tests" msgstr "" -#: src/SUMMARY.md:168 src/bare-metal/useful-crates.md:1 +#: src/SUMMARY.md:178 src/bare-metal/useful-crates.md:1 msgid "Useful crates" msgstr "" -#: src/SUMMARY.md:169 src/unsafe.md:1 +#: src/SUMMARY.md:179 src/unsafe.md:1 msgid "Unsafe Rust" msgstr "" -#: src/SUMMARY.md:170 src/unsafe/raw-pointers.md:1 +#: src/SUMMARY.md:180 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:181 src/unsafe/mutable-static-variables.md:1 msgid "Mutable Static Variables" msgstr "" -#: src/SUMMARY.md:172 src/unsafe/unions.md:1 +#: src/SUMMARY.md:182 src/unsafe/unions.md:1 msgid "Unions" msgstr "" -#: src/SUMMARY.md:173 src/unsafe/calling-unsafe-functions.md:1 +#: src/SUMMARY.md:183 src/unsafe/calling-unsafe-functions.md:1 msgid "Calling Unsafe Functions" msgstr "" -#: src/SUMMARY.md:174 src/unsafe/writing-unsafe-functions.md:1 +#: src/SUMMARY.md:184 src/unsafe/writing-unsafe-functions.md:1 msgid "Writing Unsafe Functions" msgstr "" -#: src/SUMMARY.md:175 +#: src/SUMMARY.md:185 msgid "Extern Functions" msgstr "" -#: src/SUMMARY.md:176 src/unsafe/unsafe-traits.md:1 +#: src/SUMMARY.md:186 src/unsafe/unsafe-traits.md:1 msgid "Implementing Unsafe Traits" msgstr "" -#: src/SUMMARY.md:178 src/exercises/day-3/safe-ffi-wrapper.md:1 +#: src/SUMMARY.md:188 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:191 src/SUMMARY.md:261 src/bare-metal/android.md:1 msgid "Android" msgstr "" -#: src/SUMMARY.md:186 src/android/setup.md:1 +#: src/SUMMARY.md:196 src/android/setup.md:1 msgid "Setup" msgstr "" -#: src/SUMMARY.md:187 src/android/build-rules.md:1 +#: src/SUMMARY.md:197 src/android/build-rules.md:1 msgid "Build Rules" msgstr "" -#: src/SUMMARY.md:188 +#: src/SUMMARY.md:198 msgid "Binary" msgstr "" -#: src/SUMMARY.md:189 +#: src/SUMMARY.md:199 msgid "Library" msgstr "" -#: src/SUMMARY.md:190 src/android/aidl.md:1 +#: src/SUMMARY.md:200 src/android/aidl.md:1 msgid "AIDL" msgstr "" -#: src/SUMMARY.md:191 +#: src/SUMMARY.md:201 msgid "Interface" msgstr "" -#: src/SUMMARY.md:192 +#: src/SUMMARY.md:202 msgid "Implementation" msgstr "" -#: src/SUMMARY.md:193 +#: src/SUMMARY.md:203 msgid "Server" msgstr "" -#: src/SUMMARY.md:194 src/android/aidl/deploy.md:1 +#: src/SUMMARY.md:204 src/android/aidl/deploy.md:1 msgid "Deploy" msgstr "" -#: src/SUMMARY.md:195 +#: src/SUMMARY.md:205 msgid "Client" msgstr "" -#: src/SUMMARY.md:196 src/android/aidl/changing.md:1 +#: src/SUMMARY.md:206 src/android/aidl/changing.md:1 msgid "Changing API" msgstr "" -#: src/SUMMARY.md:197 src/SUMMARY.md:240 src/android/logging.md:1 +#: src/SUMMARY.md:207 src/SUMMARY.md:251 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:208 src/android/interoperability.md:1 msgid "Interoperability" msgstr "" -#: src/SUMMARY.md:199 +#: src/SUMMARY.md:209 msgid "With C" msgstr "" -#: src/SUMMARY.md:200 +#: src/SUMMARY.md:210 msgid "Calling C with Bindgen" msgstr "" -#: src/SUMMARY.md:201 +#: src/SUMMARY.md:211 msgid "Calling Rust from C" msgstr "" -#: src/SUMMARY.md:202 src/android/interoperability/cpp.md:1 +#: src/SUMMARY.md:212 src/android/interoperability/cpp.md:1 msgid "With C++" msgstr "" -#: src/SUMMARY.md:203 +#: src/SUMMARY.md:213 msgid "With Java" msgstr "" -#: src/SUMMARY.md:207 +#: src/SUMMARY.md:217 msgid "Bare Metal: Morning" msgstr "" -#: src/SUMMARY.md:212 +#: src/SUMMARY.md:222 msgid "no_std" msgstr "" -#: src/SUMMARY.md:213 +#: src/SUMMARY.md:223 msgid "A Minimal Example" msgstr "" -#: src/SUMMARY.md:214 +#: src/SUMMARY.md:224 msgid "alloc" msgstr "" -#: src/SUMMARY.md:215 src/bare-metal/microcontrollers.md:1 +#: src/SUMMARY.md:225 src/bare-metal/microcontrollers.md:1 msgid "Microcontrollers" msgstr "" -#: src/SUMMARY.md:216 src/bare-metal/microcontrollers/mmio.md:1 +#: src/SUMMARY.md:226 src/bare-metal/microcontrollers/mmio.md:1 msgid "Raw MMIO" msgstr "" -#: src/SUMMARY.md:217 +#: src/SUMMARY.md:227 msgid "PACs" msgstr "" -#: src/SUMMARY.md:218 +#: src/SUMMARY.md:228 msgid "HAL Crates" msgstr "" -#: src/SUMMARY.md:219 +#: src/SUMMARY.md:229 msgid "Board Support Crates" msgstr "" -#: src/SUMMARY.md:220 +#: src/SUMMARY.md:230 msgid "The Type State Pattern" msgstr "" -#: src/SUMMARY.md:221 +#: src/SUMMARY.md:231 msgid "embedded-hal" msgstr "" -#: src/SUMMARY.md:222 +#: src/SUMMARY.md:232 msgid "probe-rs, cargo-embed" msgstr "" -#: src/SUMMARY.md:223 src/bare-metal/microcontrollers/debugging.md:1 +#: src/SUMMARY.md:233 src/bare-metal/microcontrollers/debugging.md:1 msgid "Debugging" msgstr "" -#: src/SUMMARY.md:224 src/SUMMARY.md:242 +#: src/SUMMARY.md:234 src/SUMMARY.md:254 msgid "Other Projects" msgstr "" -#: src/SUMMARY.md:226 src/exercises/bare-metal/compass.md:1 +#: src/SUMMARY.md:236 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:238 msgid "Bare Metal: Afternoon" msgstr "" -#: src/SUMMARY.md:230 +#: src/SUMMARY.md:240 msgid "Application Processors" msgstr "" -#: src/SUMMARY.md:231 +#: src/SUMMARY.md:241 src/bare-metal/aps/entry-point.md:1 +msgid "Getting Ready to Rust" +msgstr "" + +#: src/SUMMARY.md:242 msgid "Inline Assembly" msgstr "" -#: src/SUMMARY.md:232 +#: src/SUMMARY.md:243 msgid "MMIO" msgstr "" -#: src/SUMMARY.md:233 +#: src/SUMMARY.md:244 msgid "Let's Write a UART Driver" msgstr "" -#: src/SUMMARY.md:234 +#: src/SUMMARY.md:245 msgid "More Traits" msgstr "" -#: src/SUMMARY.md:235 +#: src/SUMMARY.md:246 msgid "A Better UART Driver" msgstr "" -#: src/SUMMARY.md:236 src/bare-metal/aps/better-uart/bitflags.md:1 +#: src/SUMMARY.md:247 src/bare-metal/aps/better-uart/bitflags.md:1 msgid "Bitflags" msgstr "" -#: src/SUMMARY.md:237 +#: src/SUMMARY.md:248 msgid "Multiple Registers" msgstr "" -#: src/SUMMARY.md:238 src/bare-metal/aps/better-uart/driver.md:1 +#: src/SUMMARY.md:249 src/bare-metal/aps/better-uart/driver.md:1 msgid "Driver" msgstr "" -#: src/SUMMARY.md:239 src/SUMMARY.md:241 +#: src/SUMMARY.md:250 src/SUMMARY.md:252 msgid "Using It" msgstr "" -#: src/SUMMARY.md:243 +#: src/SUMMARY.md:253 src/bare-metal/aps/exceptions.md:1 +msgid "Exceptions" +msgstr "" + +#: src/SUMMARY.md:255 msgid "Useful Crates" msgstr "" -#: src/SUMMARY.md:244 +#: src/SUMMARY.md:256 msgid "zerocopy" msgstr "" -#: src/SUMMARY.md:245 +#: src/SUMMARY.md:257 msgid "aarch64-paging" msgstr "" -#: src/SUMMARY.md:246 +#: src/SUMMARY.md:258 msgid "buddy_system_allocator" msgstr "" -#: src/SUMMARY.md:247 +#: src/SUMMARY.md:259 msgid "tinyvec" msgstr "" -#: src/SUMMARY.md:248 +#: src/SUMMARY.md:260 msgid "spin" msgstr "" -#: src/SUMMARY.md:250 src/bare-metal/android/vmbase.md:1 +#: src/SUMMARY.md:262 src/bare-metal/android/vmbase.md:1 msgid "vmbase" msgstr "" -#: src/SUMMARY.md:252 +#: src/SUMMARY.md:264 msgid "RTC Driver" msgstr "" -#: src/SUMMARY.md:255 +#: src/SUMMARY.md:267 msgid "Concurrency: Morning" msgstr "" -#: src/SUMMARY.md:260 src/concurrency/threads.md:1 +#: src/SUMMARY.md:272 src/concurrency/threads.md:1 msgid "Threads" msgstr "" -#: src/SUMMARY.md:261 src/concurrency/scoped-threads.md:1 +#: src/SUMMARY.md:273 src/concurrency/scoped-threads.md:1 msgid "Scoped Threads" msgstr "" -#: src/SUMMARY.md:262 src/concurrency/channels.md:1 +#: src/SUMMARY.md:274 src/concurrency/channels.md:1 msgid "Channels" msgstr "" -#: src/SUMMARY.md:263 src/concurrency/channels/unbounded.md:1 +#: src/SUMMARY.md:275 src/concurrency/channels/unbounded.md:1 msgid "Unbounded Channels" msgstr "" -#: src/SUMMARY.md:264 src/concurrency/channels/bounded.md:1 +#: src/SUMMARY.md:276 src/concurrency/channels/bounded.md:1 msgid "Bounded Channels" msgstr "" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:277 msgid "Send and Sync" msgstr "" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:277 msgid "Send" msgstr "" -#: src/SUMMARY.md:265 +#: src/SUMMARY.md:277 msgid "Sync" msgstr "" -#: src/SUMMARY.md:268 src/concurrency/send-sync/examples.md:1 +#: src/SUMMARY.md:280 src/concurrency/send-sync/examples.md:1 msgid "Examples" msgstr "" -#: src/SUMMARY.md:269 src/concurrency/shared_state.md:1 +#: src/SUMMARY.md:281 src/concurrency/shared_state.md:1 msgid "Shared State" msgstr "" -#: src/SUMMARY.md:270 +#: src/SUMMARY.md:282 msgid "Arc" msgstr "" -#: src/SUMMARY.md:271 +#: src/SUMMARY.md:283 msgid "Mutex" msgstr "" -#: src/SUMMARY.md:274 src/SUMMARY.md:294 +#: src/SUMMARY.md:286 src/SUMMARY.md:307 #: src/exercises/concurrency/dining-philosophers.md:1 #: src/exercises/concurrency/solutions-morning.md:3 msgid "Dining Philosophers" msgstr "" -#: src/SUMMARY.md:275 src/exercises/concurrency/link-checker.md:1 +#: src/SUMMARY.md:287 src/exercises/concurrency/link-checker.md:1 msgid "Multi-threaded Link Checker" msgstr "" -#: src/SUMMARY.md:277 +#: src/SUMMARY.md:289 msgid "Concurrency: Afternoon" msgstr "" -#: src/SUMMARY.md:279 +#: src/SUMMARY.md:291 msgid "Async Basics" msgstr "" -#: src/SUMMARY.md:280 +#: src/SUMMARY.md:292 msgid "async/await" msgstr "" -#: src/SUMMARY.md:281 src/async/futures.md:1 +#: src/SUMMARY.md:293 src/async/futures.md:1 msgid "Futures" msgstr "" -#: src/SUMMARY.md:282 src/async/runtimes.md:1 +#: src/SUMMARY.md:294 src/async/runtimes.md:1 msgid "Runtimes" msgstr "" -#: src/SUMMARY.md:283 src/async/runtimes/tokio.md:1 +#: src/SUMMARY.md:295 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:296 src/exercises/concurrency/link-checker.md:126 +#: src/async/tasks.md:1 src/exercises/concurrency/chat-app.md:140 msgid "Tasks" msgstr "" -#: src/SUMMARY.md:285 src/async/channels.md:1 +#: src/SUMMARY.md:297 src/async/channels.md:1 msgid "Async Channels" msgstr "" -#: src/SUMMARY.md:287 src/async/control-flow/join.md:1 +#: src/SUMMARY.md:299 src/async/control-flow/join.md:1 msgid "Join" msgstr "" -#: src/SUMMARY.md:288 src/async/control-flow/select.md:1 +#: src/SUMMARY.md:300 src/async/control-flow/select.md:1 msgid "Select" msgstr "" -#: src/SUMMARY.md:289 +#: src/SUMMARY.md:301 msgid "Pitfalls" msgstr "" -#: src/SUMMARY.md:290 +#: src/SUMMARY.md:302 msgid "Blocking the Executor" msgstr "" -#: src/SUMMARY.md:291 src/async/pitfalls/pin.md:1 +#: src/SUMMARY.md:303 src/async/pitfalls/pin.md:1 msgid "Pin" msgstr "" -#: src/SUMMARY.md:292 src/async/pitfalls/async-traits.md:1 +#: src/SUMMARY.md:304 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:305 src/async/pitfalls/cancellation.md:1 +#, fuzzy +msgid "Cancellation" +msgstr "Встановлення" + +#: src/SUMMARY.md:308 src/exercises/concurrency/chat-app.md:1 +#: src/exercises/concurrency/solutions-afternoon.md:119 msgid "Broadcast Chat Application" msgstr "" -#: src/SUMMARY.md:298 +#: src/SUMMARY.md:311 msgid "Final Words" msgstr "" -#: src/SUMMARY.md:302 src/thanks.md:1 +#: src/SUMMARY.md:315 src/thanks.md:1 msgid "Thanks!" msgstr "" -#: src/SUMMARY.md:303 +#: src/SUMMARY.md:316 msgid "Other Resources" msgstr "" -#: src/SUMMARY.md:304 src/credits.md:1 +#: src/SUMMARY.md:317 src/credits.md:1 msgid "Credits" msgstr "" -#: src/SUMMARY.md:307 src/exercises/solutions.md:1 +#: src/SUMMARY.md:320 src/exercises/solutions.md:1 msgid "Solutions" msgstr "" -#: src/SUMMARY.md:312 +#: src/SUMMARY.md:325 msgid "Day 1 Morning" msgstr "" -#: src/SUMMARY.md:313 +#: src/SUMMARY.md:326 msgid "Day 1 Afternoon" msgstr "" -#: src/SUMMARY.md:314 +#: src/SUMMARY.md:327 msgid "Day 2 Morning" msgstr "" -#: src/SUMMARY.md:315 +#: src/SUMMARY.md:328 msgid "Day 2 Afternoon" msgstr "" -#: src/SUMMARY.md:316 +#: src/SUMMARY.md:329 msgid "Day 3 Morning" msgstr "" -#: src/SUMMARY.md:317 +#: src/SUMMARY.md:330 msgid "Day 3 Afternoon" msgstr "" -#: src/SUMMARY.md:318 +#: src/SUMMARY.md:331 msgid "Bare Metal Rust Morning" msgstr "" -#: src/SUMMARY.md:319 src/exercises/bare-metal/solutions-afternoon.md:1 +#: src/SUMMARY.md:332 src/exercises/bare-metal/solutions-afternoon.md:1 msgid "Bare Metal Rust Afternoon" msgstr "" -#: src/SUMMARY.md:320 +#: src/SUMMARY.md:333 msgid "Concurrency Morning" msgstr "" -#: src/SUMMARY.md:321 +#: src/SUMMARY.md:334 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 "" - -#: 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 "" - -#: src/welcome.md:4 -msgid "GitHub contributors" -msgstr "" - -#: 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 "" - -#: src/welcome.md:5 -msgid "" -"[![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:7 +#: src/index.md:7 +#, fuzzy 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." +"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 "" "Це трьохденний курс по мові Rust, розроблений командою Android. Курс охоплює " "весь спектр Rust, від базового синтаксиса до складних тем, таких як " "узагальнення (generics) и обробка помилок. Останній день курсу теж охоплює " "особливості програмування під Android." -#: src/welcome.md:11 +#: src/index.md:11 msgid "" "The goal of the course is to teach you Rust. We assume you don't know " "anything about Rust and hope to:" @@ -1074,27 +1065,31 @@ msgstr "" "Ціль курсу --- навчити вас мові Rust. Ми припускаємо, що ви нічого не знаєте " "про Rust и надіємося:" -#: src/welcome.md:14 +#: src/index.md:14 msgid "Give you a comprehensive understanding of the Rust syntax and language." msgstr "Дати повне уявлення про синтаксис та семантику Rust." -#: src/welcome.md:15 +#: src/index.md:15 msgid "Enable you to modify existing programs and write new programs in Rust." msgstr "Навчити працювати з існуючим кодом та писати нові програми на Rust." -#: src/welcome.md:16 +#: src/index.md:16 msgid "Show you common Rust idioms." msgstr "Показати розповсюджені ідіоми мови Rust." -#: src/welcome.md:18 +#: src/index.md:18 +msgid "We call the first three course days Rust Fundamentals." +msgstr "" + +#: src/index.md:20 +#, fuzzy msgid "" -"The first three days show you the fundamentals of Rust. Following this, " -"you're invited to dive into one or more specialized topics:" +"Building on this, you're invited to dive into one or more specialized topics:" msgstr "" "Перші три дні познайомлять вас із основами Rust. Після цього вам " "пропонується заглибитися в одну або кілька спеціалізованих тем:" -#: src/welcome.md:21 +#: src/index.md:22 msgid "" "[Android](android.md): a half-day course on using Rust for Android platform " "development (AOSP). This includes interoperability with C, C++, and Java." @@ -1103,9 +1098,10 @@ msgstr "" "для розробки на платформі Android (AOSP). Сюди входить взаємодія з C, C++ та " "Java." -#: src/welcome.md:23 +#: src/index.md:24 +#, fuzzy 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 "" @@ -1113,9 +1109,10 @@ msgstr "" "низькорівневої (embedded) розробки, що охоплює як мікроконтролери, так і " "звичайні процесори." -#: src/welcome.md:26 +#: src/index.md:27 +#, fuzzy 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)." @@ -1125,11 +1122,11 @@ msgstr "" "багатозадачність з використанням потоків і м'ютексів), так і async/await " "конкурентність (кооперативна багатозадачність з використанням футур)." -#: src/welcome.md:32 +#: src/index.md:33 msgid "Non-Goals" msgstr "За рамками курсу" -#: src/welcome.md:34 +#: src/index.md:35 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:" @@ -1137,9 +1134,10 @@ msgstr "" "Rust --- це об'ємна мова, і ми не зможемо охопити її за кілька днів. Теми, " "що виходять за межі курсу:" -#: src/welcome.md:37 +#: src/index.md:38 +#, fuzzy 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 "" @@ -1147,14 +1145,15 @@ msgstr "" "org/book/ch19-06-macros.html) и [Rust by Example](https://doc.rust-lang.org/" "rust-by-example/macros.html)." -#: src/welcome.md:41 +#: src/index.md:42 msgid "Assumptions" msgstr "Пропозиції" -#: src/welcome.md:43 +#: src/index.md:44 +#, fuzzy 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 --- це статично " @@ -1162,15 +1161,16 @@ msgstr "" "щоб краще пояснити чи підкреслити різницю у підходах до написання коду на " "Rust." -#: src/welcome.md:47 +#: src/index.md:48 +#, fuzzy 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, ви зможете успішно пройти цей курс." -#: src/welcome.md:52 +#: src/index.md:53 msgid "" "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 " @@ -1301,16 +1301,20 @@ msgid "The course is fast paced and covers a lot of ground:" msgstr "Курс проходить у швидкому темпі та охоплює багато питань:" #: src/running-the-course/course-structure.md:7 -msgid "Day 1: Basic Rust, ownership and the borrow checker." -msgstr "День 1: Базовий Rust, володіння та аналізатор запозичень" +msgid "Day 1: Basic Rust, syntax, control flow, creating and consuming values." +msgstr "" #: src/running-the-course/course-structure.md:8 -msgid "Day 2: Compound data types, pattern matching, the standard library." +#, fuzzy +msgid "" +"Day 2: Memory management, ownership, compound data types, and the standard " +"library." msgstr "" "День 2: Складові типи даних, зіставлення із зразком, стандартна бібліотека." #: src/running-the-course/course-structure.md:9 -msgid "Day 3: Traits and generics, error handling, testing, unsafe Rust." +#, fuzzy +msgid "Day 3: Generics, traits, error handling, testing, and unsafe Rust." msgstr "" "День 3: Трейти та узагальнення, обробка помилок, тестування, небезпечний " "Rust." @@ -1327,11 +1331,16 @@ msgstr "" "На додаток до 3-денного курсу з основ Rust, ми розглянемо ще кілька " "спеціалізованих тем:" +#: src/running-the-course/course-structure.md:16 +msgid "Rust in Android" +msgstr "" + #: src/running-the-course/course-structure.md:18 +#, fuzzy 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 Deep Dive](../android.md) --- це напівденний курс з використання " "Rust для розробки на Android платформі. Сюди входить взаємодія з C, C++ та " @@ -1363,14 +1372,15 @@ msgstr "" "запускає, і переконайтеся, що вони працюють, коли ви запускаєте їх вручну." #: src/running-the-course/course-structure.md:34 -msgid "Bare-Metal" +msgid "Bare-Metal Rust" msgstr "" #: src/running-the-course/course-structure.md:36 +#, fuzzy 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 Deep Dive](../bare-metal.md): заняття на повний день з " "використання Rust для низькорівневої (embedded) розробки. Розглядаються як " @@ -1389,13 +1399,15 @@ msgstr "" "md)." #: src/running-the-course/course-structure.md:45 -msgid "Concurrency" +#, fuzzy +msgid "Concurrency in Rust" msgstr "Конкурентність" #: src/running-the-course/course-structure.md:47 +#, fuzzy 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 Deep Dive](../concurrency.md) це цілий день занять з класичної, " "а теж `async`/`await` конкурентності." @@ -1475,21 +1487,23 @@ msgid "" msgstr "Курс був перекладений іншими мовами групою чудових волонтерів:" #: src/running-the-course/translations.md:6 +#, fuzzy msgid "" "[Brazilian Portuguese](https://google.github.io/comprehensive-rust/pt-BR/) " -"by [@rastringer](https://github.com/rastringer) and [@hugojacob](https://" -"github.com/hugojacob)." +"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 +#, fuzzy msgid "" "[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).\\* \\[Ukrainian\\]\\[ua\\] by " -"\\[@git-user-cpp\\]." +"[@jooyunghan](https://github.com/jooyunghan)." msgstr "" "[Корейска](https://google.github.io/comprehensive-rust/ko/) від [@keispace]" "(https://github.com/keispace), [@jiyongp](https://github.com/jiyongp) і " @@ -1504,6 +1518,58 @@ msgstr "" "між мовами." #: src/running-the-course/translations.md:11 +#, fuzzy +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 "" + +#: src/running-the-course/translations.md:16 +#, fuzzy +msgid "" +"[Bengali](https://google.github.io/comprehensive-rust/bn/) by [@raselmandol]" +"(https://github.com/raselmandol)." +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:17 +#, fuzzy +msgid "" +"[French](https://google.github.io/comprehensive-rust/fr/) by [@KookaS]" +"(https://github.com/KookaS) and [@vcaen](https://github.com/vcaen)." +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:18 +#, fuzzy +msgid "" +"[German](https://google.github.io/comprehensive-rust/de/) by [@Throvn]" +"(https://github.com/Throvn) and [@ronaldfw](https://github.com/ronaldfw)." +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:19 +#, fuzzy +msgid "" +"[Japanese](https://google.github.io/comprehensive-rust/ja/) by [@CoinEZ-JPN]" +"(https://github.com/CoinEZ) and [@momotaro1105](https://github.com/" +"momotaro1105)." +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:21 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 " @@ -1535,64 +1601,51 @@ msgid "Installation" msgstr "Встановлення" #: src/cargo.md:10 -msgid "Rustup (Recommended)" -msgstr "Rustup (рекомендуеться)" - -#: 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." +msgid "**Please follow the instructions on .**" msgstr "" -"Ви можете дотримуватися інструкцій для встановлення Cargo, компілятора Rust " -"і інших стандартних інструментів екосистеми за допомогою інструмента [rustup]" -"(https://rust-analyzer.github.io/), який підтримується Rust Foundation." -#: src/cargo.md:14 +#: src/cargo.md:12 +#, fuzzy 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." +"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/switch toolchains, setup cross compilation, etc." msgstr "" "Поряд з Cargo та rustc, Rustup буде встановлений як утиліта командної Рядки, " "які можна використовувати для встановлення/перемикання наборів інструментів, " "налаштування крос-компіляції і т.д." #: src/cargo.md:16 -msgid "Package Managers" -msgstr "Менеджери пакетів" - -#: src/cargo.md:18 -msgid "Debian" -msgstr "" - -#: src/cargo.md:20 +#, fuzzy msgid "" -"On Debian/Ubuntu, you can install Cargo, the Rust source and the [Rust " -"formatter](https://github.com/rust-lang/rustfmt) with" +"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 "" "У Debian/Ubuntu ви можете встановити Cargo, вихідний код Rust та [Rust " "formatter](https://github.com/rust-lang/rustfmt) за допомогою" -#: src/cargo.md:22 +#: src/cargo.md:18 msgid "" "```shell\n" -"$ sudo apt install cargo rust-src rustfmt\n" +"sudo apt install cargo rust-src rustfmt\n" "```" msgstr "" -#: src/cargo.md:26 +#: src/cargo.md:22 +#, fuzzy 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)." +"We suggest using [VS Code](https://code.visualstudio.com/) to edit the code " +"(but any LSP compatible editor works with rust-analyzer[3](https://rust-" +"analyzer.github.io/))." msgstr "" "Це дозволить \\[rust-analyzer\\]\\[1\\] переходити до визначень у вихідному " "коді. Ми пропонуємо використовувати [VS Code](https://code.visualstudio." "com/) для редагування коду (підтримується будь-який сумісний із LSP " "редактор)." -#: src/cargo.md:29 +#: src/cargo.md:24 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 " @@ -1624,11 +1677,12 @@ msgstr "" "інші проміжні формати." #: src/cargo/rust-ecosystem.md:8 +#, fuzzy 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 знає, як " "завантажити залежності, розміщені на , і передати їх " @@ -1686,19 +1740,25 @@ msgstr "" #: 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 "" + +#: 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." -#: 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 "Редакціями дозволено вносити зворотно-несумісні зміни до мови." -#: 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." @@ -1706,7 +1766,7 @@ msgstr "" "Щоб уникнути збоїв, редакцію для свого пакета можна явно вказати у файлі " "`Cargo.toml`." -#: 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." @@ -1714,7 +1774,7 @@ 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)." @@ -1722,7 +1782,7 @@ msgstr "" "Варто згадати, що використання компілятора безпосередньо, а не через " "`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 " @@ -1731,20 +1791,20 @@ 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 "" "[workspaces](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 "Управління залежностями для розробки (dev) та часу виконання (runtime)" -#: 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)" @@ -1752,7 +1812,7 @@ msgstr "" "[build scripting](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)" @@ -1760,7 +1820,7 @@ msgstr "" "[global installation](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))." @@ -1768,7 +1828,7 @@ 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 "" @@ -1877,26 +1937,32 @@ msgstr "" #: src/cargo/running-locally.md:15 msgid "" +"You can use any later version too since Rust maintains backwards " +"compatibility." +msgstr "" + +#: 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 "" "Після цього виконайте такі кроки, щоб зібрати виконуваний файл на основі " "одного з прикладів у цьому курсі:" -#: 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 "" "Натисніть кнопку ”Copy to clipboard” на прикладі коду, який потрібно " "скопіювати." -#: 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 +#: src/cargo/running-locally.md:24 msgid "" "```shell\n" "$ cargo new exercise\n" @@ -1908,14 +1974,14 @@ msgstr "" " 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 +#: src/cargo/running-locally.md:31 msgid "" "```shell\n" "$ cd exercise\n" @@ -1935,7 +2001,7 @@ msgstr "" "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" @@ -1944,7 +2010,7 @@ msgstr "" "використовуючи приклад коду з попередньої сторінки, зробіть `src/main.rs` " "схожим на" -#: src/cargo/running-locally.md:41 +#: src/cargo/running-locally.md:43 msgid "" "```rust\n" "fn main() {\n" @@ -1958,13 +2024,13 @@ 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 +#: src/cargo/running-locally.md:51 msgid "" "```shell\n" "$ cargo run\n" @@ -1982,7 +2048,7 @@ msgstr "" "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/" @@ -1995,7 +2061,7 @@ msgstr "" "складання. Використовуйте `cargo build --release` для створення " "оптимізованого фінального складання в `target/release/`." -#: 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 " @@ -2005,7 +2071,7 @@ msgstr "" "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 " @@ -2021,7 +2087,7 @@ 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 "" @@ -2033,13 +2099,12 @@ msgstr "" #: src/welcome-day-1.md:9 msgid "" -"Memory management: stack vs heap, manual memory management, scope-based " -"memory management, and garbage collection." +"Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and " +"`continue`." msgstr "" #: src/welcome-day-1.md:12 -msgid "" -"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." +msgid "Pattern matching: destructuring enums, structs, and arrays." msgstr "" #: src/welcome-day-1.md:16 @@ -2060,9 +2125,10 @@ 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 "" #: src/welcome-day-1.md:24 @@ -2169,7 +2235,8 @@ 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." +msgid "" +"Can be scaled down to very constrained devices such as microcontrollers." msgstr "" #: src/welcome-day-1/what-is-rust.md:26 @@ -2222,15 +2289,14 @@ msgstr "" #: 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 "" #: 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 "" #: src/hello-world.md:31 @@ -2251,6 +2317,14 @@ msgid "" "html)." msgstr "" +#: 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 "" + #: src/hello-world/small-example.md:3 msgid "Here is a small example program in Rust:" msgstr "" @@ -2311,6 +2385,12 @@ msgid "" "that the students become familiar with searching in the standard library." msgstr "" +#: 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 "" + #: src/why-rust.md:3 msgid "Some unique selling points of Rust:" msgstr "" @@ -2392,7 +2472,7 @@ msgstr "" #: 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 "" @@ -2431,18 +2511,20 @@ msgid "Array access is bounds checked." msgstr "" #: src/why-rust/runtime.md:6 -msgid "Integer overflow is defined." +msgid "Integer overflow is defined (panic or wrap-around)." msgstr "" #: 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 "" -#: 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 " @@ -2451,7 +2533,7 @@ msgid "" msgstr "" #: src/why-rust/modern.md:3 -msgid "Rust is built with all the experience gained in the last 40 years." +msgid "Rust is built with all the experience gained in the last decades." msgstr "" #: src/why-rust/modern.md:5 @@ -2604,7 +2686,7 @@ msgid "`i8`, `i16`, `i32`, `i64`, `i128`, `isize`" msgstr "" #: src/basic-syntax/scalar-types.md:5 -msgid "`-10`, `0`, `1_000`, `123i64`" +msgid "`-10`, `0`, `1_000`, `123_i64`" msgstr "" #: src/basic-syntax/scalar-types.md:6 @@ -2616,7 +2698,7 @@ msgid "`u8`, `u16`, `u32`, `u64`, `u128`, `usize`" msgstr "" #: src/basic-syntax/scalar-types.md:6 -msgid "`0`, `123`, `10u16`" +msgid "`0`, `123`, `10_u16`" msgstr "" #: src/basic-syntax/scalar-types.md:7 @@ -2628,7 +2710,7 @@ msgid "`f32`, `f64`" msgstr "" #: src/basic-syntax/scalar-types.md:7 -msgid "`3.14`, `-10.0e20`, `2f32`" +msgid "`3.14`, `-10.0e20`, `2_f32`" msgstr "" #: src/basic-syntax/scalar-types.md:8 @@ -2680,11 +2762,11 @@ msgid "`isize` and `usize` are the width of a pointer," msgstr "" #: src/basic-syntax/scalar-types.md:16 -msgid "`char` is 32 bit wide," +msgid "`char` is 32 bits wide," msgstr "" #: src/basic-syntax/scalar-types.md:17 -msgid "`bool` is 8 bit wide." +msgid "`bool` is 8 bits wide." msgstr "" #: src/basic-syntax/scalar-types.md:21 @@ -2722,6 +2804,13 @@ msgid "" "```" msgstr "" +#: 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 "" + #: src/basic-syntax/compound-types.md:5 msgid "Arrays" msgstr "" @@ -2782,10 +2871,10 @@ 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 "" #: src/basic-syntax/compound-types.md:38 @@ -2924,66 +3013,69 @@ msgstr "" 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 "" -#: src/basic-syntax/slices.md:16 -msgid "Question: What happens if you modify `a[3]`?" +#: src/basic-syntax/slices.md:17 +msgid "Question: What happens if you modify `a[3]` right before printing `s`?" msgstr "" -#: src/basic-syntax/slices.md:20 +#: src/basic-syntax/slices.md:21 msgid "" "We create a slice by borrowing `a` and specifying the starting and ending " "indexes in brackets." msgstr "" -#: src/basic-syntax/slices.md:22 +#: 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 " "identical." msgstr "" -#: src/basic-syntax/slices.md:24 +#: 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 "" -#: src/basic-syntax/slices.md:26 +#: src/basic-syntax/slices.md:27 msgid "" "To easily create a slice of the full array, we can therefore use `&a[..]`." msgstr "" -#: src/basic-syntax/slices.md:28 +#: 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 "" -#: src/basic-syntax/slices.md:30 +#: 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 "" -#: 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 "" #: src/basic-syntax/string-slices.md:1 @@ -3179,6 +3271,13 @@ 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 "" + #: src/basic-syntax/methods.md:3 msgid "" "Methods are functions associated with a type. The `self` argument of a " @@ -3219,7 +3318,7 @@ 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 "" #: src/basic-syntax/methods.md:36 @@ -3233,8 +3332,16 @@ msgstr "" #: src/basic-syntax/methods.md:42 msgid "" -"Add a `Rectangle::new_square(width: u32)` constructor to illustrate that " -"constructors can take arbitrary parameters." +"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 "" + +#: src/basic-syntax/methods.md:45 +msgid "" +"Add a `Rectangle::square(width: u32)` constructor to illustrate that such " +"static methods can take arbitrary parameters." msgstr "" #: src/basic-syntax/functions-interlude.md:1 @@ -3331,14 +3438,12 @@ msgid "" "their state if you navigate away from the page." 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." +"After looking at the exercises, you can look at the [solutions](solutions-" +"morning.md) provided." msgstr "" #: src/exercises/day-1/implicit-conversions.md:3 @@ -3451,7 +3556,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" @@ -3545,1369 +3650,1499 @@ msgid "" "[Solution](solutions-morning.md#arrays-and-for-loops) section." msgstr "" -#: 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:" +"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 "" -#: 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 "" -#: src/basic-syntax/variables.md:17 +#: src/exercises/day-1/for-loops.md:102 msgid "" -"Due to type inference the `i32` is optional. We will gradually show the " -"types less and less as the course progresses." +"An implicit array copy would have occured. Since `i32` is a copy type, then " +"`[i32; 3]` is also a copy type." msgstr "" -#: src/basic-syntax/variables.md:18 +#: src/control-flow.md:3 msgid "" -"Note that since `println!` is a macro, `x` is not moved, even using the " -"function like syntax of `println!(\"x: {}\", x)`" +"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 "" -#: src/basic-syntax/type-inference.md:3 -msgid "Rust will look at how the variable is _used_ to determine the type:" +#: 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 "" -#: src/basic-syntax/type-inference.md:5 +#: src/control-flow/blocks.md:7 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 x = 10;\n" -" let y = 20;\n" -"\n" -" takes_u32(x);\n" -" takes_i8(y);\n" -" // takes_u32(y);\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 "" -#: src/basic-syntax/type-inference.md:26 -msgid "" -"This slide demonstrates how the Rust compiler infers types based on " -"constraints given by variable declarations and usages." -msgstr "" - -#: src/basic-syntax/type-inference.md:28 +#: src/control-flow/blocks.md:26 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." +"If the last expression ends with `;`, then the resulting value and type is " +"`()`." msgstr "" -#: src/basic-syntax/type-inference.md:32 +#: src/control-flow/blocks.md:28 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:" +"The same rule is used for functions: the value of the function body is the " +"return value:" msgstr "" -#: src/basic-syntax/type-inference.md:34 +#: src/control-flow/blocks.md:31 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" +"fn double(x: i32) -> i32 {\n" +" x + x\n" +"}\n" "\n" -" let vv = v.iter().collect::>();\n" -" println!(\"vv: {vv:?}\");\n" +"fn main() {\n" +" println!(\"doubled: {}\", double(7));\n" "}\n" "```" msgstr "" -#: src/basic-syntax/type-inference.md:46 -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." +#: 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/basic-syntax/static-and-const.md:1 -msgid "Static and Constant Variables" +#: src/control-flow/blocks.md:44 +msgid "" +"The point of this slide is to show that blocks have a type and value in " +"Rust. " msgstr "" -#: src/basic-syntax/static-and-const.md:3 -msgid "Global state is managed with static and constant variables." +#: src/control-flow/blocks.md:45 +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`." msgstr "" -#: src/basic-syntax/static-and-const.md:5 -msgid "`const`" +#: src/control-flow/if-expressions.md:1 +msgid "`if` expressions" msgstr "" -#: src/basic-syntax/static-and-const.md:7 -msgid "You can declare compile-time constants:" +#: src/control-flow/if-expressions.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:" msgstr "" -#: src/basic-syntax/static-and-const.md:9 +#: src/control-flow/if-expressions.md:7 msgid "" "```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" +"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" -" digest\n" "}\n" -"\n" +"```" +msgstr "" + +#: 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 "" + +#: src/control-flow/if-expressions.md:22 +msgid "" +"```rust,editable\n" "fn main() {\n" -" let digest = compute_digest(\"Hello\");\n" -" println!(\"Digest: {digest:?}\");\n" +" let mut x = 10;\n" +" x = if x % 2 == 0 {\n" +" x / 2\n" +" } else {\n" +" 3 * x + 1\n" +" };\n" "}\n" "```" msgstr "" -#: src/basic-syntax/static-and-const.md:27 +#: src/control-flow/if-expressions.md:35 msgid "" -"According to the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" -"vs-static.html) these are inlined upon use." +"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 "" -#: src/basic-syntax/static-and-const.md:29 -msgid "`static`" +#: src/control-flow/for-expressions.md:1 +msgid "`for` loops" msgstr "" -#: src/basic-syntax/static-and-const.md:31 -msgid "You can also declare static variables:" +#: 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 "" -#: src/basic-syntax/static-and-const.md:33 +#: src/control-flow/for-expressions.md:7 msgid "" "```rust,editable\n" -"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" -"\n" "fn main() {\n" -" println!(\"{BANNER}\");\n" +" let v = vec![10, 20, 30];\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" +" }\n" "}\n" "```" msgstr "" -#: 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." +#: src/control-flow/for-expressions.md:21 +msgid "You can use `break` and `continue` here as usual." msgstr "" -#: src/basic-syntax/static-and-const.md:44 -msgid "" -"We will look at mutating static data in the [chapter on Unsafe Rust](../" -"unsafe.md)." +#: src/control-flow/for-expressions.md:25 +msgid "Index iteration is not a special syntax in Rust for just that case." msgstr "" -#: src/basic-syntax/static-and-const.md:48 -msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." +#: src/control-flow/for-expressions.md:26 +msgid "`(0..10)` is a range that implements an `Iterator` trait. " msgstr "" -#: src/basic-syntax/static-and-const.md:49 +#: src/control-flow/for-expressions.md:27 msgid "" -"`static`, on the other hand, is much more similar to a `const` or mutable " -"global variable in C++." +"`step_by` is a method that returns another `Iterator` that skips every other " +"element. " msgstr "" -#: src/basic-syntax/static-and-const.md:50 +#: src/control-flow/for-expressions.md:28 msgid "" -"It isn't super common that one would need a runtime evaluated constant, but " -"it is helpful and safer than using a static." +"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 "" -#: src/basic-syntax/scopes-shadowing.md:3 +#: src/control-flow/while-expressions.md:1 +msgid "`while` loops" +msgstr "" + +#: src/control-flow/while-expressions.md:3 msgid "" -"You can shadow variables, both those from outer scopes and variables from " -"the same scope:" +"The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-" +"expr.html#predicate-loops) works very similar to other languages:" msgstr "" -#: src/basic-syntax/scopes-shadowing.md:6 +#: src/control-flow/while-expressions.md:6 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" +" 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" -"\n" -" println!(\"after: {a}\");\n" +" println!(\"Final x: {x}\");\n" "}\n" "```" msgstr "" -#: src/basic-syntax/scopes-shadowing.md:25 -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. " +#: src/control-flow/break-continue.md:1 +msgid "`break` and `continue`" msgstr "" -#: src/basic-syntax/scopes-shadowing.md:26 -msgid "A shadowing variable can have a different type. " +#: src/control-flow/break-continue.md:3 +msgid "" +"If you want to exit a loop early, use [`break`](https://doc.rust-lang.org/" +"reference/expressions/loop-expr.html#break-expressions)," msgstr "" -#: src/basic-syntax/scopes-shadowing.md:27 +#: src/control-flow/break-continue.md:4 msgid "" -"Shadowing looks obscure at first, but is convenient for holding on to values " -"after `.unwrap()`." +"If you want to immediately start the next iteration use [`continue`](https://" +"doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions)." msgstr "" -#: src/basic-syntax/scopes-shadowing.md:28 +#: src/control-flow/break-continue.md:7 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." +"Both `continue` and `break` can optionally take a label argument which is " +"used to break out of nested loops:" msgstr "" -#: src/basic-syntax/scopes-shadowing.md:30 +#: src/control-flow/break-continue.md:10 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 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 "" -#: 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 "" - -#: src/memory-management.md:6 +#: src/control-flow/break-continue.md:28 msgid "" -"Full safety via automatic memory management at runtime: Java, Python, Go, " -"Haskell, ..." +"In this case we break the outer loop after 3 iterations of the inner loop." msgstr "" -#: src/memory-management.md:8 -msgid "Rust offers a new mix:" +#: src/control-flow/loop-expressions.md:1 +msgid "`loop` expressions" msgstr "" -#: src/memory-management.md:10 +#: src/control-flow/loop-expressions.md:3 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." +"Finally, there is a [`loop` keyword](https://doc.rust-lang.org/reference/" +"expressions/loop-expr.html#infinite-loops) which creates an endless loop." msgstr "" -#: src/memory-management.md:15 -msgid "First, let's refresh how memory management works." +#: src/control-flow/loop-expressions.md:6 +msgid "Here you must either `break` or `return` to stop the loop:" msgstr "" -#: src/memory-management/stack-vs-heap.md:1 -msgid "The Stack vs The Heap" +#: src/control-flow/loop-expressions.md:8 +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" +"```" msgstr "" -#: src/memory-management/stack-vs-heap.md:3 -msgid "Stack: Continuous area of memory for local variables." +#: src/control-flow/loop-expressions.md:27 +msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." msgstr "" -#: src/memory-management/stack-vs-heap.md:4 -msgid "Values have fixed sizes known at compile time." +#: src/control-flow/loop-expressions.md:28 +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)." msgstr "" -#: src/memory-management/stack-vs-heap.md:5 -msgid "Extremely fast: just move a stack pointer." +#: src/basic-syntax/variables.md:3 +msgid "" +"Rust provides type safety via static typing. Variable bindings are immutable " +"by default:" msgstr "" -#: src/memory-management/stack-vs-heap.md:6 -msgid "Easy to manage: follows function calls." +#: src/basic-syntax/variables.md:6 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" let x: i32 = 10;\n" +" println!(\"x: {x}\");\n" +" // x = 20;\n" +" // println!(\"x: {x}\");\n" +"}\n" +"```" msgstr "" -#: src/memory-management/stack-vs-heap.md:7 -msgid "Great memory locality." +#: 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 "" -#: src/memory-management/stack-vs-heap.md:9 -msgid "Heap: Storage of values outside of function calls." +#: src/basic-syntax/type-inference.md:3 +msgid "Rust will look at how the variable is _used_ to determine the type:" msgstr "" -#: src/memory-management/stack-vs-heap.md:10 -msgid "Values have dynamic sizes determined at runtime." +#: 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 x = 10;\n" +" let y = 20;\n" +"\n" +" takes_u32(x);\n" +" takes_i8(y);\n" +" // takes_u32(y);\n" +"}\n" +"```" msgstr "" -#: src/memory-management/stack-vs-heap.md:11 -msgid "Slightly slower than the stack: some book-keeping needed." +#: src/basic-syntax/type-inference.md:26 +msgid "" +"This slide demonstrates how the Rust compiler infers types based on " +"constraints given by variable declarations and usages." msgstr "" -#: src/memory-management/stack-vs-heap.md:12 -msgid "No guarantee of memory locality." +#: 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/stack.md:3 +#: src/basic-syntax/type-inference.md:32 msgid "" -"Creating a `String` puts fixed-sized data on the stack and dynamically sized " -"data on the heap:" +"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/stack.md:6 +#: src/basic-syntax/type-inference.md:34 msgid "" "```rust,editable\n" "fn main() {\n" -" let s1 = String::from(\"Hello\");\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 "" -#: src/memory-management/stack.md:12 +#: src/basic-syntax/type-inference.md:46 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" -"```" +"[`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 "" -#: 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." +#: src/basic-syntax/static-and-const.md:1 +msgid "Static and Constant Variables" msgstr "" -#: src/memory-management/stack.md:30 +#: src/basic-syntax/static-and-const.md:3 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)" +"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/stack.md:32 +#: src/basic-syntax/static-and-const.md:6 +msgid "`const`" +msgstr "" + +#: src/basic-syntax/static-and-const.md:8 msgid "" -"We can inspect the memory layout with `unsafe` code. However, you should " -"point out that this is rightfully unsafe!" +"Constant variables are evaluated at compile time and their values are " +"inlined wherever they are used:" msgstr "" -#: src/memory-management/stack.md:34 +#: src/basic-syntax/static-and-const.md:11 msgid "" "```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" +"\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 digest = compute_digest(\"Hello\");\n" +" println!(\"Digest: {digest:?}\");\n" "}\n" "```" msgstr "" -#: src/memory-management/manual.md:3 -msgid "You allocate and deallocate heap memory yourself." +#: src/basic-syntax/static-and-const.md:29 +msgid "" +"According to the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" +"vs-static.html) these are inlined upon use." msgstr "" -#: src/memory-management/manual.md:5 +#: src/basic-syntax/static-and-const.md:31 msgid "" -"If not done with care, this can lead to crashes, bugs, security " -"vulnerabilities, and memory leaks." +"Only functions marked `const` can be called at compile time to generate " +"`const` values. `const` functions can however be called at runtime." msgstr "" -#: src/memory-management/manual.md:7 -msgid "C Example" +#: src/basic-syntax/static-and-const.md:33 +msgid "`static`" msgstr "" -#: src/memory-management/manual.md:9 -msgid "You must call `free` on every pointer you allocate with `malloc`:" +#: src/basic-syntax/static-and-const.md:35 +msgid "" +"Static variables will live during the whole execution of the program, and " +"therefore will not move:" msgstr "" -#: src/memory-management/manual.md:11 +#: src/basic-syntax/static-and-const.md:37 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" +"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" +"\n" +"fn main() {\n" +" println!(\"{BANNER}\");\n" "}\n" "```" msgstr "" -#: src/memory-management/manual.md:21 +#: src/basic-syntax/static-and-const.md:45 msgid "" -"Memory is leaked if the function returns early between `malloc` and `free`: " -"the pointer is lost and we cannot deallocate the memory." +"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 "" -#: src/memory-management/scope-based.md:3 +#: src/basic-syntax/static-and-const.md:49 msgid "" -"Constructors and destructors let you hook into the lifetime of an object." +"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 "" -#: 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." +#: src/basic-syntax/static-and-const.md:57 +msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." msgstr "" -#: src/memory-management/scope-based.md:9 +#: src/basic-syntax/static-and-const.md:58 msgid "" -"This is often called _resource acquisition is initialization_ (RAII) and " -"gives you smart pointers." +"`static`, on the other hand, is much more similar to a `const` or mutable " +"global variable in C++." msgstr "" -#: src/memory-management/scope-based.md:12 -msgid "C++ Example" +#: src/basic-syntax/static-and-const.md:59 +msgid "" +"`static` provides object identity: an address in memory and state as " +"required by types with interior mutability such as `Mutex`." msgstr "" -#: src/memory-management/scope-based.md:14 +#: src/basic-syntax/static-and-const.md:60 msgid "" -"```c++\n" -"void say_hello(std::unique_ptr person) {\n" -" std::cout << \"Hello \" << person->name << std::endl;\n" -"}\n" -"```" +"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/scope-based.md:20 -msgid "" -"The `std::unique_ptr` object is allocated on the stack, and points to memory " -"allocated on the heap." +#: src/basic-syntax/static-and-const.md:61 +msgid "`thread_local` data can be created with the macro `std::thread_local`." msgstr "" -#: src/memory-management/scope-based.md:22 -msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." +#: src/basic-syntax/static-and-const.md:63 +msgid "Properties table:" msgstr "" -#: src/memory-management/scope-based.md:23 -msgid "The destructor frees the `Person` object it points to." +#: src/basic-syntax/static-and-const.md:65 +msgid "Property" msgstr "" -#: src/memory-management/scope-based.md:25 -msgid "" -"Special move constructors are used when passing ownership to a function:" +#: src/basic-syntax/static-and-const.md:65 +msgid "Static" msgstr "" -#: src/memory-management/scope-based.md:27 -msgid "" -"```c++\n" -"std::unique_ptr person = find_person(\"Carla\");\n" -"say_hello(std::move(person));\n" -"```" +#: src/basic-syntax/static-and-const.md:65 +msgid "Constant" msgstr "" -#: src/memory-management/garbage-collection.md:1 -msgid "Automatic Memory Management" +#: src/basic-syntax/static-and-const.md:67 +msgid "Has an address in memory" msgstr "" -#: src/memory-management/garbage-collection.md:3 -msgid "" -"An alternative to manual and scope-based memory management is automatic " -"memory management:" +#: 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/garbage-collection.md:6 -msgid "The programmer never allocates or deallocates memory explicitly." +#: src/basic-syntax/static-and-const.md:67 +msgid "No (inlined)" msgstr "" -#: src/memory-management/garbage-collection.md:7 -msgid "" -"A garbage collector finds unused memory and deallocates it for the " -"programmer." +#: src/basic-syntax/static-and-const.md:68 +msgid "Lives for the entire duration of the program" msgstr "" -#: src/memory-management/garbage-collection.md:9 -msgid "Java Example" +#: 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/garbage-collection.md:11 -msgid "The `person` object is not deallocated after `sayHello` returns:" +#: src/basic-syntax/static-and-const.md:69 +msgid "Can be mutable" msgstr "" -#: src/memory-management/garbage-collection.md:13 -msgid "" -"```java\n" -"void sayHello(Person person) {\n" -" System.out.println(\"Hello \" + person.getName());\n" -"}\n" -"```" +#: src/basic-syntax/static-and-const.md:69 +msgid "Yes (unsafe)" msgstr "" -#: src/memory-management/rust.md:1 -msgid "Memory Management in Rust" +#: src/basic-syntax/static-and-const.md:70 +msgid "Evaluated at compile time" msgstr "" -#: src/memory-management/rust.md:3 -msgid "Memory management in Rust is a mix:" +#: src/basic-syntax/static-and-const.md:70 +msgid "Yes (initialised at compile time)" msgstr "" -#: src/memory-management/rust.md:5 -msgid "Safe and correct like Java, but without a garbage collector." +#: src/basic-syntax/static-and-const.md:71 +msgid "Inlined wherever it is used" msgstr "" -#: src/memory-management/rust.md:6 +#: src/basic-syntax/scopes-shadowing.md:3 msgid "" -"Depending on which abstraction (or combination of abstractions) you choose, " -"can be a single unique pointer, reference counted, or atomically reference " -"counted." +"You can shadow variables, both those from outer scopes and variables from " +"the same scope:" msgstr "" -#: src/memory-management/rust.md:7 -msgid "Scope-based like C++, but the compiler enforces full adherence." +#: src/basic-syntax/scopes-shadowing.md:6 +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" +"}\n" +"```" msgstr "" -#: src/memory-management/rust.md:8 +#: src/basic-syntax/scopes-shadowing.md:25 msgid "" -"A Rust user can choose the right abstraction for the situation, some even " -"have no cost at runtime like C." +"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 "" -#: src/memory-management/rust.md:10 -msgid "It achieves this by modeling _ownership_ explicitly." +#: src/basic-syntax/scopes-shadowing.md:26 +msgid "A shadowing variable can have a different type. " msgstr "" -#: src/memory-management/rust.md:14 +#: src/basic-syntax/scopes-shadowing.md:27 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." +"Shadowing looks obscure at first, but is convenient for holding on to values " +"after `.unwrap()`." msgstr "" -#: src/memory-management/rust.md:16 +#: src/basic-syntax/scopes-shadowing.md:28 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." -msgstr "" - -#: src/memory-management/comparison.md:3 -msgid "Here is a rough comparison of the memory management techniques." +"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/memory-management/comparison.md:5 -msgid "Pros of Different Memory Management Techniques" +#: src/basic-syntax/scopes-shadowing.md:30 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" let a = 1;\n" +" let b = &a;\n" +" let a = a + 1;\n" +" println!(\"{a} {b}\");\n" +"}\n" +"```" msgstr "" -#: src/memory-management/comparison.md:7 src/memory-management/comparison.md:22 -msgid "Manual like C:" +#: src/enums.md:3 +msgid "" +"The `enum` keyword allows the creation of a type which has a few different " +"variants:" msgstr "" -#: src/memory-management/comparison.md:8 src/memory-management/comparison.md:14 -#: src/memory-management/comparison.md:17 -msgid "No runtime overhead." +#: src/enums.md:6 +msgid "" +"```rust,editable\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" +"\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" +" println!(\"You got: {:?}\", flip_coin());\n" +"}\n" +"```" msgstr "" -#: src/memory-management/comparison.md:9 src/memory-management/comparison.md:26 -msgid "Automatic like Java:" +#: src/enums.md:36 +msgid "Enumerations allow you to collect a set of values under one type" msgstr "" -#: src/memory-management/comparison.md:10 -msgid "Fully automatic." +#: 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 "" -#: src/memory-management/comparison.md:11 -#: src/memory-management/comparison.md:18 -msgid "Safe and correct." +#: src/enums.md:38 +msgid "This might be a good time to compare Structs and Enums:" msgstr "" -#: src/memory-management/comparison.md:12 -#: src/memory-management/comparison.md:29 -msgid "Scope-based like C++:" +#: src/enums.md:39 +msgid "" +"In both, you can have a simple version without fields (unit struct) or one " +"with different types of fields (variant payloads). " msgstr "" -#: src/memory-management/comparison.md:13 -msgid "Partially automatic." +#: src/enums.md:40 +msgid "In both, associated functions are defined within an `impl` block." msgstr "" -#: src/memory-management/comparison.md:15 -msgid "Compiler-enforced scope-based like Rust:" +#: src/enums.md:41 +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. " msgstr "" -#: src/memory-management/comparison.md:16 -msgid "Enforced by compiler." +#: 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 "" -#: src/memory-management/comparison.md:20 -msgid "Cons of Different Memory Management Techniques" -msgstr "" - -#: src/memory-management/comparison.md:23 -msgid "Use-after-free." +#: src/enums/variant-payloads.md:6 +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" +"}\n" +"```" msgstr "" -#: src/memory-management/comparison.md:24 -msgid "Double-frees." +#: 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 "" -#: src/memory-management/comparison.md:25 -msgid "Memory leaks." +#: 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 "" -#: src/memory-management/comparison.md:27 -msgid "Garbage collection pauses." +#: 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 "" -#: src/memory-management/comparison.md:28 -msgid "Destructor delays." +#: src/enums/variant-payloads.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 "" -#: src/memory-management/comparison.md:30 -msgid "Complex, opt-in by programmer." +#: 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 "" -#: src/memory-management/comparison.md:31 -msgid "Potential for use-after-free." +#: src/enums/variant-payloads.md:40 +msgid "`match` inspects a hidden discriminant field in the `enum`." msgstr "" -#: src/memory-management/comparison.md:32 -msgid "Compiler-enforced and scope-based like Rust:" +#: src/enums/variant-payloads.md:41 +msgid "" +"It is possible to retrieve the discriminant by calling `std::mem::" +"discriminant()`" msgstr "" -#: src/memory-management/comparison.md:33 -msgid "Some upfront complexity." +#: 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 "" -#: src/memory-management/comparison.md:34 -msgid "Can reject valid programs." +#: 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 "" -#: src/ownership.md:3 +#: src/enums/sizes.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:" +"Rust enums are packed tightly, taking constraints due to alignment into " +"account:" msgstr "" -#: src/ownership.md:6 +#: src/enums/sizes.md:5 msgid "" -"```rust,editable,compile_fail\n" -"struct Point(i32, i32);\n" +"```rust,editable\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" -" {\n" -" let p = Point(3, 4);\n" -" println!(\"x: {}\", p.0);\n" -" }\n" -" println!(\"y: {}\", p.1);\n" +" dbg_size::();\n" "}\n" "```" msgstr "" -#: src/ownership.md:18 +#: src/enums/sizes.md:24 msgid "" -"At the end of the scope, the variable is _dropped_ and the data is freed." -msgstr "" - -#: src/ownership.md:19 -msgid "A destructor can run here to free up resources." +"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." +"html)." msgstr "" -#: src/ownership.md:20 -msgid "We say that the variable _owns_ the value." +#: src/enums/sizes.md:30 +msgid "" +"Internally Rust is using a field (discriminant) to keep track of the enum " +"variant." msgstr "" -#: src/ownership/move-semantics.md:3 -msgid "An assignment will transfer ownership between variables:" +#: src/enums/sizes.md:32 +msgid "" +"You can control the discriminant if needed (e.g., for compatibility with C):" msgstr "" -#: src/ownership/move-semantics.md:5 +#: src/enums/sizes.md:34 msgid "" "```rust,editable\n" +"#[repr(u32)]\n" +"enum Bar {\n" +" A, // 0\n" +" B = 10000,\n" +" C, // 10001\n" +"}\n" +"\n" "fn main() {\n" -" let s1: String = String::from(\"Hello!\");\n" -" let s2: String = s1;\n" -" println!(\"s2: {s2}\");\n" -" // println!(\"s1: {s1}\");\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/move-semantics.md:14 -msgid "The assignment of `s1` to `s2` transfers ownership." -msgstr "" - -#: src/ownership/move-semantics.md:15 -msgid "The data was _moved_ from `s1` and `s1` is no longer accessible." -msgstr "" - -#: src/ownership/move-semantics.md:16 -msgid "When `s1` goes out of scope, nothing happens: it has no ownership." +#: src/enums/sizes.md:49 +msgid "" +"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " +"bytes." msgstr "" -#: src/ownership/move-semantics.md:17 -msgid "When `s2` goes out of scope, the string data is freed." +#: src/enums/sizes.md:53 +msgid "Try out other types such as" msgstr "" -#: src/ownership/move-semantics.md:18 -msgid "There is always _exactly_ one variable binding which owns a value." +#: src/enums/sizes.md:55 +msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," msgstr "" -#: src/ownership/move-semantics.md:22 +#: src/enums/sizes.md:56 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!)." +"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " +"see below)," msgstr "" -#: src/ownership/move-semantics.md:24 -msgid "In Rust, clones are explicit (by using `clone`)." +#: src/enums/sizes.md:57 +msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," msgstr "" -#: src/ownership/moved-strings-rust.md:3 +#: src/enums/sizes.md:58 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let s1: String = String::from(\"Rust\");\n" -" let s2: String = s1;\n" -"}\n" -"```" +"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " +"optimization, see below)." msgstr "" -#: src/ownership/moved-strings-rust.md:10 -msgid "The heap data from `s1` is reused for `s2`." +#: src/enums/sizes.md:60 +msgid "" +"Niche optimization: Rust will merge unused bit patterns for the enum " +"discriminant." msgstr "" -#: src/ownership/moved-strings-rust.md:11 -msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." +#: 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 "" -#: src/ownership/moved-strings-rust.md:13 -msgid "Before move to `s2`:" +#: 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/ownership/moved-strings-rust.md:15 +#: src/enums/sizes.md:70 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" +"```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" "```" msgstr "" -#: src/ownership/moved-strings-rust.md:30 -msgid "After move to `s2`:" +#: 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 "" -#: src/ownership/moved-strings-rust.md:32 +#: src/enums/sizes.md:107 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" -"```" -msgstr "" - -#: src/ownership/double-free-modern-cpp.md:3 -msgid "Modern C++ solves this differently:" -msgstr "" - -#: src/ownership/double-free-modern-cpp.md:5 -msgid "" -"```c++\n" -"std::string s1 = \"Cpp\";\n" -"std::string s2 = s1; // Duplicate the data in s1.\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" +"// 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" +" // 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/double-free-modern-cpp.md:10 +#: src/control-flow/novel.md:3 msgid "" -"The heap data from `s1` is duplicated and `s2` gets its own independent copy." -msgstr "" - -#: src/ownership/double-free-modern-cpp.md:11 -msgid "When `s1` and `s2` go out of scope, they each free their own memory." -msgstr "" - -#: src/ownership/double-free-modern-cpp.md:13 -msgid "Before copy-assignment:" +"Rust has a few control flow constructs which differ from other languages. " +"They are used for pattern matching:" msgstr "" -#: src/ownership/double-free-modern-cpp.md:16 -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" -"```" +#: src/control-flow/novel.md:6 src/control-flow/if-let-expressions.md:1 +msgid "`if let` expressions" msgstr "" -#: src/ownership/double-free-modern-cpp.md:30 -msgid "After copy-assignment:" +#: src/control-flow/novel.md:7 +msgid "`while let` expressions" msgstr "" -#: src/ownership/double-free-modern-cpp.md:32 -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" -"```" +#: src/control-flow/novel.md:8 src/control-flow/match-expressions.md:1 +msgid "`match` expressions" msgstr "" -#: src/ownership/moves-function-calls.md:3 +#: src/control-flow/if-let-expressions.md:3 msgid "" -"When you pass a value to a function, the value is assigned to the function " -"parameter. This transfers ownership:" +"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 "" -#: src/ownership/moves-function-calls.md:6 +#: src/control-flow/if-let-expressions.md:7 msgid "" "```rust,editable\n" -"fn say_hello(name: String) {\n" -" println!(\"Hello {name}\")\n" -"}\n" -"\n" "fn main() {\n" -" let name = String::from(\"Alice\");\n" -" say_hello(name);\n" -" // say_hello(name);\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 "" -#: src/ownership/moves-function-calls.md:20 -msgid "" -"With the first call to `say_hello`, `main` gives up ownership of `name`. " -"Afterwards, `name` cannot be used anymore within `main`." -msgstr "" - -#: src/ownership/moves-function-calls.md:21 +#: src/control-flow/if-let-expressions.md:18 +#: src/control-flow/while-let-expressions.md:21 +#: src/control-flow/match-expressions.md:23 msgid "" -"The heap memory allocated for `name` will be freed at the end of the " -"`say_hello` function." +"See [pattern matching](../pattern-matching.md) for more details on patterns " +"in Rust." msgstr "" -#: src/ownership/moves-function-calls.md:22 +#: src/control-flow/if-let-expressions.md:23 msgid "" -"`main` can retain ownership if it passes `name` as a reference (`&name`) and " -"if `say_hello` accepts a reference as a parameter." +"Unlike `match`, `if let` does not have to cover all branches. This can make " +"it more concise than `match`." msgstr "" -#: src/ownership/moves-function-calls.md:23 -msgid "" -"Alternatively, `main` can pass a clone of `name` in the first call (`name." -"clone()`)." +#: src/control-flow/if-let-expressions.md:24 +msgid "A common usage is handling `Some` values when working with `Option`." msgstr "" -#: src/ownership/moves-function-calls.md:24 +#: src/control-flow/if-let-expressions.md:25 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." +"Unlike `match`, `if let` does not support guard clauses for pattern matching." msgstr "" -#: src/ownership/copy-clone.md:3 +#: src/control-flow/if-let-expressions.md:26 msgid "" -"While move semantics are the default, certain types are copied by default:" +"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 "" -#: src/ownership/copy-clone.md:5 +#: src/control-flow/if-let-expressions.md:28 msgid "" "```rust,editable\n" "fn main() {\n" -" let x = 42;\n" -" let y = x;\n" -" println!(\"x: {x}\");\n" -" println!(\"y: {y}\");\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 "" -#: src/ownership/copy-clone.md:14 -msgid "These types implement the `Copy` trait." +#: src/control-flow/while-let-expressions.md:1 +msgid "`while let` loops" msgstr "" -#: src/ownership/copy-clone.md:16 -msgid "You can opt-in your own types to use copy semantics:" +#: src/control-flow/while-let-expressions.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:" msgstr "" -#: src/ownership/copy-clone.md:18 +#: src/control-flow/while-let-expressions.md:6 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 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" "```" msgstr "" -#: src/ownership/copy-clone.md:30 -msgid "After the assignment, both `p1` and `p2` own their own data." -msgstr "" - -#: src/ownership/copy-clone.md:31 -msgid "We can also use `p1.clone()` to explicitly copy the data." -msgstr "" - -#: src/ownership/copy-clone.md:35 -msgid "Copying and cloning are not the same thing:" -msgstr "" - -#: src/ownership/copy-clone.md:37 +#: src/control-flow/while-let-expressions.md:17 msgid "" -"Copying refers to bitwise copies of memory regions and does not work on " -"arbitrary objects." +"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 "" -#: src/ownership/copy-clone.md:38 +#: src/control-flow/while-let-expressions.md:26 msgid "" -"Copying does not allow for custom logic (unlike copy constructors in C++)." +"Point out that the `while let` loop will keep going as long as the value " +"matches the pattern." msgstr "" -#: src/ownership/copy-clone.md:39 +#: src/control-flow/while-let-expressions.md:27 msgid "" -"Cloning is a more general operation and also allows for custom behavior by " -"implementing the `Clone` trait." +"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 "" -#: src/ownership/copy-clone.md:40 -msgid "Copying does not work on types that implement the `Drop` trait." +#: src/control-flow/match-expressions.md:3 +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:" msgstr "" -#: 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 "" -"Add a `String` field to `struct Point`. It will not compile because `String` " -"is not a `Copy` type." -msgstr "" - -#: src/ownership/copy-clone.md:45 -msgid "" -"Remove `Copy` from the `derive` attribute. The compiler error is now in the " -"`println!` for `p1`." -msgstr "" - -#: src/ownership/copy-clone.md:46 -msgid "Show that it works if you clone `p1` instead." -msgstr "" - -#: src/ownership/copy-clone.md:48 -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." -msgstr "" - -#: src/ownership/borrowing.md:3 -msgid "" -"Instead of transferring ownership when calling a function, you can let a " -"function _borrow_ the value:" -msgstr "" - -#: src/ownership/borrowing.md:6 +#: 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" -" 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" +" 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 "" -#: src/ownership/borrowing.md:22 -msgid "The `add` function _borrows_ two points and returns a new point." +#: 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 "" -#: src/ownership/borrowing.md:23 -msgid "The caller retains ownership of the inputs." +#: src/control-flow/match-expressions.md:28 +msgid "Save the match expression to a variable and print it out." msgstr "" -#: src/ownership/borrowing.md:27 -msgid "Notes on stack returns:" +#: src/control-flow/match-expressions.md:29 +msgid "Remove `.as_deref()` and explain the error." msgstr "" -#: src/ownership/borrowing.md:28 +#: src/control-flow/match-expressions.md:30 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:" +"`std::env::args().next()` returns an `Option`, but we cannot match " +"against `String`." msgstr "" -#: src/ownership/borrowing.md:30 +#: src/control-flow/match-expressions.md:31 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" -"}\n" -"```" -msgstr "" - -#: src/ownership/borrowing.md:48 -msgid "The Rust compiler can do return value optimization (RVO)." +"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " +"this turns `Option` into `Option<&str>`." msgstr "" -#: src/ownership/borrowing.md:49 +#: src/control-flow/match-expressions.md:32 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." -msgstr "" - -#: src/ownership/shared-unique-borrows.md:3 -msgid "Rust puts constraints on the ways you can borrow values:" +"We can now use pattern matching to match against the `&str` inside `Option`." msgstr "" -#: src/ownership/shared-unique-borrows.md:5 -msgid "You can have one or more `&T` values at any given time, _or_" +#: 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 "" -#: src/ownership/shared-unique-borrows.md:6 -msgid "You can have exactly one `&mut T` value." +#: src/pattern-matching.md:6 +msgid "The patterns can be simple values, similarly to `switch` in C and C++:" msgstr "" -#: src/ownership/shared-unique-borrows.md:8 +#: 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 "" -#: 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 "" - -#: src/ownership/shared-unique-borrows.md:26 -msgid "" -"Move the `println!` statement for `b` before the scope that introduces `c` " -"to make the code compile." +#: src/pattern-matching.md:21 +msgid "The `_` pattern is a wildcard pattern which matches any value." msgstr "" -#: src/ownership/shared-unique-borrows.md:27 +#: src/pattern-matching.md:26 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\"." +"You might point out how some specific characters are being used when in a " +"pattern" msgstr "" -#: src/ownership/lifetimes.md:3 -msgid "A borrowed value has a _lifetime_:" +#: src/pattern-matching.md:27 +msgid "`|` as an `or`" msgstr "" -#: src/ownership/lifetimes.md:5 -msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." +#: src/pattern-matching.md:28 +msgid "`..` can expand as much as it needs to be" msgstr "" -#: src/ownership/lifetimes.md:6 -msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." +#: src/pattern-matching.md:29 +msgid "`1..=5` represents an inclusive range" msgstr "" -#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:23 -msgid "" -"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " -"lifetime `a`\"." +#: src/pattern-matching.md:30 +msgid "`_` is a wild card" msgstr "" -#: src/ownership/lifetimes.md:9 +#: src/pattern-matching.md:31 msgid "" -"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " -"yourself." +"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/ownership/lifetimes.md:11 -msgid "" -"Lifetime annotations create constraints; the compiler verifies that there is " -"a valid solution." +#: 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 "" -#: 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:" +"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 "" -#: 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" +"enum Result {\n" +" Ok(i32),\n" +" Err(String),\n" +"}\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." +#: 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 "" -#: src/ownership/lifetimes-function-calls.md:22 -msgid "Lifetimes start with `'` and `'a` is a typical default name." +#: src/pattern-matching/destructuring-enums.md:36 +msgid "" +"The `if`/`else` expression is returning an enum that is later unpacked with " +"a `match`." msgstr "" -#: src/ownership/lifetimes-function-calls.md:25 +#: src/pattern-matching/destructuring-enums.md:37 msgid "" -"The _at least_ part is important when parameters are in different scopes." +"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:31 -msgid "" -"Move the declaration of `p2` and `p3` into a a new scope (`{ ... }`), " -"resulting in the following code:" +#: src/pattern-matching/destructuring-structs.md:3 +msgid "You can also destructure `structs`:" msgstr "" -#: src/ownership/lifetimes-function-calls.md:32 +#: 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" -"}\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 "" -#: src/ownership/lifetimes-function-calls.md:50 -msgid "Note how this does not compile since `p3` outlives `p2`." -msgstr "" - -#: 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." +#: src/pattern-matching/destructuring-structs.md:23 +msgid "Change the literal values in `foo` to match with the other patterns." msgstr "" -#: src/ownership/lifetimes-function-calls.md:53 -msgid "Another way to explain it:" +#: src/pattern-matching/destructuring-structs.md:24 +msgid "Add a new field to `Foo` and make changes to the pattern as needed." msgstr "" -#: 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." +"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 "" -#: 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)." +"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:" +"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 "" -#: src/ownership/lifetimes-data-structures.md:25 +#: 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 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." +"Point out that how `..` will expand to account for different number of " +"elements." msgstr "" -#: src/ownership/lifetimes-data-structures.md:26 +#: src/pattern-matching/destructuring-arrays.md:44 +msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" +msgstr "" + +#: src/pattern-matching/match-guards.md:3 msgid "" -"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " -"the borrow checker throws an error." +"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 "" -#: 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." +#: 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 "" -#: 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 "" + +#: src/pattern-matching/match-guards.md:26 +msgid "You can use the variables defined in the pattern in your if expression." +msgstr "" + +#: src/pattern-matching/match-guards.md:27 +msgid "" +"The condition defined in the guard applies to every expression in a pattern " +"with an `|`." msgstr "" #: src/exercises/day-1/afternoon.md:1 @@ -4919,3212 +5154,3179 @@ msgid "We will look at two things:" msgstr "" #: src/exercises/day-1/afternoon.md:5 -msgid "A small book library," +msgid "The Luhn algorithm," msgstr "" #: src/exercises/day-1/afternoon.md:7 -msgid "Iterators and ownership (hard)." +msgid "An exercise on pattern matching." msgstr "" -#: src/exercises/day-1/book-library.md:3 +#: 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 "" -"We will learn much more about structs and the `Vec` type tomorrow. For " -"now, you just need to know part of its API:" +"After looking at the exercises, you can look at the [solutions](solutions-" +"afternoon.md) provided." msgstr "" -#: src/exercises/day-1/book-library.md:6 +#: src/exercises/day-1/luhn.md:3 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" -"```" +"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 "" + +#: 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:18 +#: src/exercises/day-1/luhn.md:9 msgid "" -"Use this to create a library application. Copy the code below to and update the types to make it compile:" +"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 "" -#: src/exercises/day-1/book-library.md:21 +#: src/exercises/day-1/luhn.md:12 msgid "" -"```rust,should_panic\n" -"struct Library {\n" -" books: Vec,\n" -"}\n" +"After doubling a digit, sum the digits. So doubling `7` becomes `14` which " +"becomes `5`." +msgstr "" + +#: src/exercises/day-1/luhn.md:15 +msgid "Sum all the undoubled and doubled digits." +msgstr "" + +#: src/exercises/day-1/luhn.md:17 +msgid "The credit card number is valid if the sum ends with `0`." +msgstr "" + +#: 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 "" +"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 "" + +#: src/exercises/day-1/luhn.md:25 +msgid "" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" "\n" -"struct Book {\n" -" title: String,\n" -" year: u16,\n" +"pub fn luhn(cc_number: &str) -> bool {\n" +" unimplemented!()\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_non_digit_cc_number() {\n" +" assert!(!luhn(\"foo\"));\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_empty_cc_number() {\n" +" assert!(!luhn(\"\"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +"}\n" "\n" -" //fn len(self) -> usize {\n" -" // todo!(\"Return the length of `self.books`\")\n" -" //}\n" +"#[test]\n" +"fn test_single_digit_cc_number() {\n" +" assert!(!luhn(\"0\"));\n" +"}\n" "\n" -" //fn is_empty(self) -> bool {\n" -" // todo!(\"Return `true` if `self.books` is empty\")\n" -" //}\n" +"#[test]\n" +"fn test_two_digit_cc_number() {\n" +" assert!(luhn(\" 0 0 \"));\n" +"}\n" "\n" -" //fn add_book(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 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" +"#[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" -"// 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" +"#[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" "```" 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 -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." +#: src/welcome-day-2.md:1 +msgid "Welcome to Day 2" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 -msgid "`Iterator`" +#: src/welcome-day-2.md:3 +msgid "Now that we have seen a fair amount of Rust, we will continue with:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:10 +#: src/welcome-day-2.md:5 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:" +"Memory management: stack vs heap, manual memory management, scope-based " +"memory management, and garbage collection." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:13 +#: src/welcome-day-2.md:8 msgid "" -"```rust\n" -"pub trait Iterator {\n" -" type Item;\n" -" fn next(&mut self) -> Option;\n" -"}\n" -"```" +"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:20 -msgid "You use this trait like this:" +#: src/welcome-day-2.md:10 +msgid "Structs and methods." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:22 +#: src/welcome-day-2.md:12 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" -"```" +"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " +"`Rc` and `Arc`." 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:15 +msgid "Modules: visibility, paths, and filesystem hierarchy." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:36 -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" -"```" +#: src/memory-management.md:3 +msgid "Traditionally, languages have fallen into two broad categories:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:46 -msgid "Why is this type used?" +#: src/memory-management.md:5 +msgid "Full control via manual memory management: C, C++, Pascal, ..." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:48 -msgid "`IntoIterator`" +#: src/memory-management.md:6 +msgid "" +"Full safety via automatic memory management at runtime: Java, Python, Go, " +"Haskell, ..." 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:" +#: src/memory-management.md:8 +msgid "Rust offers a new mix:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:53 +#: src/memory-management.md:10 msgid "" -"```rust\n" -"pub trait IntoIterator {\n" -" type Item;\n" -" type IntoIter: Iterator;\n" -"\n" -" fn into_iter(self) -> Self::IntoIter;\n" -"}\n" -"```" +"Full control _and_ safety via compile time enforcement of correct memory " +"management." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:62 -msgid "" -"The syntax here means that every implementation of `IntoIterator` must " -"declare two types:" +#: src/memory-management.md:13 +msgid "It does this with an explicit ownership concept." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:65 -msgid "`Item`: the type we iterate over, such as `i8`," +#: src/memory-management.md:15 +msgid "First, let's refresh how memory management works." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:66 -msgid "`IntoIter`: the `Iterator` type returned by the `into_iter` method." +#: src/memory-management/stack-vs-heap.md:1 +msgid "The Stack vs The Heap" 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`" +#: src/memory-management/stack-vs-heap.md:3 +msgid "Stack: Continuous area of memory for local variables." 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:4 +msgid "Values have fixed sizes known at compile time." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:73 -msgid "" -"```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" -"```" +#: src/memory-management/stack-vs-heap.md:5 +msgid "Extremely fast: just move a stack pointer." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:83 -msgid "`for` Loops" +#: src/memory-management/stack-vs-heap.md:6 +msgid "Easy to manage: follows function calls." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:85 -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:" +#: src/memory-management/stack-vs-heap.md:7 +msgid "Great memory locality." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:89 -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" -" }\n" -"}\n" -"```" +#: src/memory-management/stack-vs-heap.md:9 +msgid "Heap: Storage of values outside of function calls." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:103 -msgid "What is the type of `word` in each loop?" +#: src/memory-management/stack-vs-heap.md:10 +msgid "Values have dynamic sizes determined at runtime." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:105 -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." +#: src/memory-management/stack-vs-heap.md:11 +msgid "Slightly slower than the stack: some book-keeping needed." msgstr "" -#: src/welcome-day-2.md:1 -msgid "Welcome to Day 2" +#: src/memory-management/stack-vs-heap.md:12 +msgid "No guarantee of memory locality." msgstr "" -#: src/welcome-day-2.md:3 -msgid "Now that we have seen a fair amount of Rust, we will continue with:" +#: src/memory-management/stack.md:1 +msgid "Stack and Heap Example" msgstr "" -#: src/welcome-day-2.md:5 -msgid "Structs, enums, methods." +#: src/memory-management/stack.md:3 +msgid "" +"Creating a `String` puts fixed-sized metadata on the stack and dynamically " +"sized data, the actual string, on the heap:" msgstr "" -#: src/welcome-day-2.md:7 -msgid "Pattern matching: destructuring enums, structs, and arrays." +#: src/memory-management/stack.md:6 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" let s1 = String::from(\"Hello\");\n" +"}\n" +"```" msgstr "" -#: src/welcome-day-2.md:9 +#: src/memory-management/stack.md:12 msgid "" -"Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and " -"`continue`." +"```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 "" -#: src/welcome-day-2.md:12 +#: src/memory-management/stack.md:28 msgid "" -"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " -"`Rc` and `Arc`." +"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 "" -#: src/welcome-day-2.md:15 -msgid "Modules: visibility, paths, and filesystem hierarchy." +#: 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 "" -#: src/structs.md:3 -msgid "Like C and C++, Rust has support for custom structs:" +#: 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 "" -#: src/structs.md:5 +#: src/memory-management/stack.md:34 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" +" 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 "" -#: 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:" +#: src/memory-management/manual.md:3 +msgid "You allocate and deallocate heap memory yourself." msgstr "" -#: src/structs.md:33 -msgid "Structs work like in C or C++." +#: src/memory-management/manual.md:5 +msgid "" +"If not done with care, this can lead to crashes, bugs, security " +"vulnerabilities, and memory leaks." msgstr "" -#: src/structs.md:34 -msgid "Like in C++, and unlike in C, no typedef is needed to define a type." +#: src/memory-management/manual.md:7 +msgid "C Example" msgstr "" -#: src/structs.md:35 -msgid "Unlike in C++, there is no inheritance between structs." +#: src/memory-management/manual.md:9 +msgid "You must call `free` on every pointer you allocate with `malloc`:" msgstr "" -#: src/structs.md:36 +#: src/memory-management/manual.md:11 msgid "" -"Methods are defined in an `impl` block, which we will see in following " -"slides." +"```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 "" -#: src/structs.md:37 +#: src/memory-management/manual.md:21 msgid "" -"This may be a good time to let people know there are different types of " -"structs. " +"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 "" -#: src/structs.md:38 +#: src/memory-management/scope-based.md:3 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. " +"Constructors and destructors let you hook into the lifetime of an object." msgstr "" -#: src/structs.md:39 +#: src/memory-management/scope-based.md:5 msgid "" -"The next slide will introduce Tuple structs, used when the field names are " -"not important." +"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:40 +#: src/memory-management/scope-based.md:9 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." +"This is often called _resource acquisition is initialization_ (RAII) and " +"gives you smart pointers." msgstr "" -#: src/structs/tuple-structs.md:3 -msgid "If the field names are unimportant, you can use a tuple struct:" +#: src/memory-management/scope-based.md:12 +msgid "C++ Example" msgstr "" -#: src/structs/tuple-structs.md:5 +#: src/memory-management/scope-based.md:14 msgid "" -"```rust,editable\n" -"struct Point(i32, i32);\n" -"\n" -"fn main() {\n" -" let p = Point(17, 23);\n" -" println!(\"({}, {})\", p.0, p.1);\n" +"```c++\n" +"void say_hello(std::unique_ptr person) {\n" +" std::cout << \"Hello \" << person->name << std::endl;\n" "}\n" "```" msgstr "" -#: src/structs/tuple-structs.md:14 -msgid "This is often used for single-field wrappers (called newtypes):" +#: 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 "" -#: src/structs/tuple-structs.md:16 +#: src/memory-management/scope-based.md:22 +msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." +msgstr "" + +#: src/memory-management/scope-based.md:23 +msgid "The destructor frees the `Person` object it points to." +msgstr "" + +#: src/memory-management/scope-based.md:25 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" -"\n" -"fn main() {\n" -" let force = compute_thruster_force();\n" -" set_thruster_force(force);\n" -"}\n" -"\n" -"```" +"Special move constructors are used when passing ownership to a function:" msgstr "" -#: src/structs/tuple-structs.md:37 +#: src/memory-management/scope-based.md:27 msgid "" -"Newtypes are a great way to encode additional information about the value in " -"a primitive type, for example:" +"```c++\n" +"std::unique_ptr person = find_person(\"Carla\");\n" +"say_hello(std::move(person));\n" +"```" msgstr "" -#: src/structs/tuple-structs.md:38 -msgid "The number is measured in some units: `Newtons` in the example above." +#: src/memory-management/garbage-collection.md:1 +msgid "Automatic Memory Management" msgstr "" -#: src/structs/tuple-structs.md:39 +#: src/memory-management/garbage-collection.md:3 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)\\`." +"An alternative to manual and scope-based memory management is automatic " +"memory management:" msgstr "" -#: src/structs/tuple-structs.md:40 -msgid "" -"Demonstrate how to add a `f64` value to a `Newtons` type by accessing the " -"single field in the newtype." +#: src/memory-management/garbage-collection.md:6 +msgid "The programmer never allocates or deallocates memory explicitly." msgstr "" -#: src/structs/tuple-structs.md:41 +#: src/memory-management/garbage-collection.md:7 msgid "" -"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " -"for instance using booleans as integers." +"A garbage collector finds unused memory and deallocates it for the " +"programmer." msgstr "" -#: src/structs/tuple-structs.md:42 -msgid "Operator overloading is discussed on Day 3 (generics). " +#: src/memory-management/garbage-collection.md:9 +msgid "Java Example" msgstr "" -#: 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:" +#: src/memory-management/garbage-collection.md:11 +msgid "The `person` object is not deallocated after `sayHello` returns:" msgstr "" -#: src/structs/field-shorthand.md:6 +#: src/memory-management/garbage-collection.md:13 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" +"```java\n" +"void sayHello(Person person) {\n" +" System.out.println(\"Hello \" + person.getName());\n" "}\n" "```" msgstr "" -#: src/structs/field-shorthand.md:27 -msgid "" -"The `new` function could be written using `Self` as a type, as it is " -"interchangeable with the struct type name" +#: src/memory-management/rust.md:1 +msgid "Memory Management in Rust" msgstr "" -#: src/structs/field-shorthand.md:29 -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" -"}\n" -"```" +#: src/memory-management/rust.md:3 +msgid "Memory management in Rust is a mix:" msgstr "" -#: 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." +#: src/memory-management/rust.md:5 +msgid "Safe and correct like Java, but without a garbage collector." msgstr "" -#: src/structs/field-shorthand.md:43 +#: src/memory-management/rust.md:6 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" -"```" +"Depending on which abstraction (or combination of abstractions) you choose, " +"can be a single unique pointer, reference counted, or atomically reference " +"counted." msgstr "" -#: src/structs/field-shorthand.md:68 -msgid "Methods are defined in the `impl` block." +#: src/memory-management/rust.md:7 +msgid "Scope-based like C++, but the compiler enforces full adherence." msgstr "" -#: src/structs/field-shorthand.md:69 +#: src/memory-management/rust.md:8 msgid "" -"Use struct update syntax to define a new structure using `peter`. Note that " -"the variable `peter` will no longer be accessible afterwards." +"A Rust user can choose the right abstraction for the situation, some even " +"have no cost at runtime like C." msgstr "" -#: src/structs/field-shorthand.md:70 -msgid "" -"Use `{:#?}` when printing structs to request the `Debug` representation." +#: src/memory-management/rust.md:10 +msgid "Rust achieves this by modeling _ownership_ explicitly." msgstr "" -#: src/enums.md:3 +#: src/memory-management/rust.md:14 msgid "" -"The `enum` keyword allows the creation of a type which has a few different " -"variants:" +"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 "" -#: src/enums.md:6 +#: src/memory-management/rust.md:16 msgid "" -"```rust,editable\n" -"fn generate_random_number() -> i32 {\n" -" 4 // Chosen by fair dice roll. Guaranteed to be random.\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" -"}\n" -"\n" -"fn main() {\n" -" println!(\"You got: {:?}\", flip_coin());\n" -"}\n" -"```" +"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 "" -#: src/enums.md:35 -msgid "Enumerations allow you to collect a set of values under one type" +#: src/memory-management/comparison.md:3 +msgid "Here is a rough comparison of the memory management techniques." msgstr "" -#: src/enums.md:36 -msgid "" -"This page offers an enum type `CoinFlip` with two variants `Heads` and " -"`Tail`. You might note the namespace when using variants." +#: src/memory-management/comparison.md:5 +msgid "Pros of Different Memory Management Techniques" msgstr "" -#: src/enums.md:37 -msgid "This might be a good time to compare Structs and Enums:" +#: src/memory-management/comparison.md:7 src/memory-management/comparison.md:22 +msgid "Manual like C:" msgstr "" -#: src/enums.md:38 -msgid "" -"In both, you can have a simple version without fields (unit struct) or one " -"with different types of fields (variant payloads). " +#: src/memory-management/comparison.md:8 src/memory-management/comparison.md:14 +#: src/memory-management/comparison.md:17 +msgid "No runtime overhead." msgstr "" -#: src/enums.md:39 -msgid "In both, associated functions are defined within an `impl` block." +#: src/memory-management/comparison.md:9 src/memory-management/comparison.md:26 +msgid "Automatic like Java:" msgstr "" -#: src/enums.md:40 -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. " +#: src/memory-management/comparison.md:10 +msgid "Fully automatic." msgstr "" -#: 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:" +#: src/memory-management/comparison.md:11 +#: src/memory-management/comparison.md:18 +msgid "Safe and correct." msgstr "" -#: src/enums/variant-payloads.md:6 -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" -"}\n" -"```" +#: src/memory-management/comparison.md:12 +#: src/memory-management/comparison.md:29 +msgid "Scope-based like C++:" msgstr "" -#: 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 `=>`." +#: src/memory-management/comparison.md:13 +msgid "Partially automatic." msgstr "" -#: 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++." +#: src/memory-management/comparison.md:15 +msgid "Compiler-enforced scope-based like Rust:" 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." +#: src/memory-management/comparison.md:16 +msgid "Enforced by compiler." msgstr "" -#: src/enums/variant-payloads.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. " +#: src/memory-management/comparison.md:20 +msgid "Cons of Different Memory Management Techniques" 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. " +#: src/memory-management/comparison.md:23 +msgid "Use-after-free." msgstr "" -#: src/enums/variant-payloads.md:40 -msgid "`match` inspects a hidden discriminant field in the `enum`." +#: src/memory-management/comparison.md:24 +msgid "Double-frees." msgstr "" -#: src/enums/variant-payloads.md:41 -msgid "" -"It is possible to retrieve the discriminant by calling `std::mem::" -"discriminant()`" +#: src/memory-management/comparison.md:25 +msgid "Memory leaks." msgstr "" -#: 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." +#: src/memory-management/comparison.md:27 +msgid "Garbage collection pauses." msgstr "" -#: 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." +#: src/memory-management/comparison.md:28 +msgid "Destructor delays." msgstr "" -#: src/enums/sizes.md:3 -msgid "" -"Rust enums are packed tightly, taking constraints due to alignment into " -"account:" +#: src/memory-management/comparison.md:30 +msgid "Complex, opt-in by programmer (on C++)." msgstr "" -#: src/enums/sizes.md:5 -msgid "" -"```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" -"\n" -"enum Foo {\n" -" A,\n" -" B,\n" -"}\n" -"\n" -"fn main() {\n" -" dbg_size!(Foo);\n" -"}\n" -"```" +#: src/memory-management/comparison.md:31 +msgid "Circular references can lead to memory leaks" msgstr "" -#: src/enums/sizes.md:25 -msgid "" -"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." -"html)." +#: src/memory-management/comparison.md:32 +msgid "Potential runtime overhead" msgstr "" -#: src/enums/sizes.md:31 -msgid "" -"Internally Rust is using a field (discriminant) to keep track of the enum " -"variant." +#: src/memory-management/comparison.md:33 +msgid "Compiler-enforced and scope-based like Rust:" msgstr "" -#: src/enums/sizes.md:33 +#: src/memory-management/comparison.md:34 +msgid "Some upfront complexity." +msgstr "" + +#: src/memory-management/comparison.md:35 +msgid "Can reject valid programs." +msgstr "" + +#: src/ownership.md:3 msgid "" -"You can control the discriminant if needed (e.g., for compatibility with C):" +"All variable bindings have a _scope_ where they are valid and it is an error " +"to use a variable outside its scope:" msgstr "" -#: src/enums/sizes.md:35 +#: src/ownership.md:6 msgid "" -"```rust,editable\n" -"#[repr(u32)]\n" -"enum Bar {\n" -" A, // 0\n" -" B = 10000,\n" -" C, // 10001\n" -"}\n" +"```rust,editable,compile_fail\n" +"struct Point(i32, i32);\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" +" {\n" +" let p = Point(3, 4);\n" +" println!(\"x: {}\", p.0);\n" +" }\n" +" println!(\"y: {}\", p.1);\n" "}\n" "```" msgstr "" -#: src/enums/sizes.md:50 +#: src/ownership.md:18 msgid "" -"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " -"bytes." -msgstr "" - -#: src/enums/sizes.md:54 -msgid "Try out other types such as" +"At the end of the scope, the variable is _dropped_ and the data is freed." msgstr "" -#: src/enums/sizes.md:56 -msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," +#: src/ownership.md:19 +msgid "A destructor can run here to free up resources." msgstr "" -#: src/enums/sizes.md:57 -msgid "" -"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " -"see below)," +#: src/ownership.md:20 +msgid "We say that the variable _owns_ the value." msgstr "" -#: src/enums/sizes.md:58 -msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," +#: src/ownership/move-semantics.md:3 +msgid "An assignment will transfer _ownership_ between variables:" msgstr "" -#: src/enums/sizes.md:59 +#: src/ownership/move-semantics.md:5 msgid "" -"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " -"optimization, see below)." +"```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 "" -#: src/enums/sizes.md:61 -msgid "" -"Niche optimization: Rust will merge use unused bit patterns for the enum " -"discriminant." +#: src/ownership/move-semantics.md:14 +msgid "The assignment of `s1` to `s2` transfers ownership." msgstr "" -#: src/enums/sizes.md:64 -msgid "" -"Null pointer optimization: For [some types](https://doc.rust-lang.org/std/" -"option/#representation), Rust guarantees that `size_of::()` equals " -"`size_of::>()`." +#: src/ownership/move-semantics.md:15 +msgid "When `s1` goes out of scope, nothing happens: it does not own anything." msgstr "" -#: src/enums/sizes.md:68 -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." +#: src/ownership/move-semantics.md:16 +msgid "When `s2` goes out of scope, the string data is freed." msgstr "" -#: src/enums/sizes.md:71 -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" -" // 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" -"```" +#: src/ownership/move-semantics.md:17 +msgid "There is always _exactly_ one variable binding which owns a value." msgstr "" -#: src/enums/sizes.md:106 +#: src/ownership/move-semantics.md:21 msgid "" -"More complex example if you want to discuss what happens when we chain more " -"than 256 `Option`s together." +"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 "" -#: src/enums/sizes.md:108 +#: src/ownership/move-semantics.md:23 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" -" // 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" -"```" +"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/methods.md:3 +#: src/ownership/move-semantics.md:25 msgid "" -"Rust allows you to associate functions with your new types. You do this with " -"an `impl` block:" +"Simple values (such as integers) can be marked `Copy` (see later slides)." msgstr "" -#: src/methods.md:6 +#: src/ownership/move-semantics.md:27 +msgid "In Rust, clones are explicit (by using `clone`)." +msgstr "" + +#: src/ownership/moved-strings-rust.md:3 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"\n" -"impl Person {\n" -" fn say_hello(&self) {\n" -" println!(\"Hello, my name is {}\", self.name);\n" -" }\n" -"}\n" -"\n" "fn main() {\n" -" let peter = Person {\n" -" name: String::from(\"Peter\"),\n" -" age: 27,\n" -" };\n" -" peter.say_hello();\n" +" let s1: String = String::from(\"Rust\");\n" +" let s2: String = s1;\n" "}\n" "```" msgstr "" -#: src/methods.md:31 -msgid "It can be helpful to introduce methods by comparing them to functions." -msgstr "" - -#: src/methods.md:32 -msgid "" -"Methods are called on an instance of a type (such as a struct or enum), the " -"first parameter represents the instance as `self`." +#: src/ownership/moved-strings-rust.md:10 +msgid "The heap data from `s1` is reused for `s2`." msgstr "" -#: src/methods.md:33 -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." +#: src/ownership/moved-strings-rust.md:11 +msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." msgstr "" -#: src/methods.md:34 -msgid "Point out the use of the keyword `self`, a method receiver. " +#: src/ownership/moved-strings-rust.md:13 +msgid "Before move to `s2`:" msgstr "" -#: src/methods.md:35 +#: src/ownership/moved-strings-rust.md:15 msgid "" -"Show that it is an abbreviated term for `self:&Self` and perhaps show how " -"the struct name could also be used. " +"```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 "" -#: 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." +#: src/ownership/moved-strings-rust.md:30 +msgid "After move to `s2`:" msgstr "" -#: src/methods.md:37 +#: src/ownership/moved-strings-rust.md:32 msgid "" -"Note how `self` is used like other structs and dot notation can be used to " -"refer to individual 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 "" -#: 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." +#: src/ownership/double-free-modern-cpp.md:1 +msgid "Extra Work in Modern C++" msgstr "" -#: src/methods.md:39 -msgid "We describe the distinction between method receivers next." +#: src/ownership/double-free-modern-cpp.md:3 +msgid "Modern C++ solves this differently:" msgstr "" -#: src/methods/receiver.md:3 +#: src/ownership/double-free-modern-cpp.md:5 msgid "" -"The `&self` above indicates that the method borrows the object immutably. " -"There are other possible receivers for a method:" +"```c++\n" +"std::string s1 = \"Cpp\";\n" +"std::string s2 = s1; // Duplicate the data in s1.\n" +"```" msgstr "" -#: src/methods/receiver.md:6 +#: src/ownership/double-free-modern-cpp.md:10 msgid "" -"`&self`: borrows the object from the caller using a shared and immutable " -"reference. The object can be used again afterwards." +"The heap data from `s1` is duplicated and `s2` gets its own independent copy." msgstr "" -#: src/methods/receiver.md:8 -msgid "" -"`&mut self`: borrows the object from the caller using a unique and mutable " -"reference. The object can be used again afterwards." +#: src/ownership/double-free-modern-cpp.md:11 +msgid "When `s1` and `s2` go out of scope, they each free their own memory." msgstr "" -#: src/methods/receiver.md:10 -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." +#: src/ownership/double-free-modern-cpp.md:13 +msgid "Before copy-assignment:" msgstr "" -#: src/methods/receiver.md:14 -msgid "`mut self`: same as above, but the method can mutate the object. " +#: src/ownership/double-free-modern-cpp.md:16 +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 "" -#: src/methods/receiver.md:15 -msgid "" -"No receiver: this becomes a static method on the struct. Typically used to " -"create constructors which are called `new` by convention." +#: src/ownership/double-free-modern-cpp.md:30 +msgid "After copy-assignment:" msgstr "" -#: src/methods/receiver.md:18 +#: src/ownership/double-free-modern-cpp.md:32 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`." +"```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 "" -#: src/methods/receiver.md:24 +#: src/ownership/moves-function-calls.md:3 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." +"When you pass a value to a function, the value is assigned to the function " +"parameter. This transfers ownership:" msgstr "" -#: src/methods/example.md:3 +#: src/ownership/moves-function-calls.md:6 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" +"fn say_hello(name: String) {\n" +" println!(\"Hello {name}\")\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 name = String::from(\"Alice\");\n" +" say_hello(name);\n" +" // say_hello(name);\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/ownership/moves-function-calls.md:20 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`." +"With the first call to `say_hello`, `main` gives up ownership of `name`. " +"Afterwards, `name` cannot be used anymore within `main`." msgstr "" -#: src/methods/example.md:49 +#: src/ownership/moves-function-calls.md:21 msgid "" -"You can showcase the error that appears when trying to call `finish` twice." +"The heap memory allocated for `name` will be freed at the end of the " +"`say_hello` function." msgstr "" -#: src/methods/example.md:50 +#: src/ownership/moves-function-calls.md:22 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." +"`main` can retain ownership if it passes `name` as a reference (`&name`) and " +"if `say_hello` accepts a reference as a parameter." msgstr "" -#: src/methods/example.md:51 +#: src/ownership/moves-function-calls.md:23 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. " +"Alternatively, `main` can pass a clone of `name` in the first call (`name." +"clone()`)." msgstr "" -#: src/pattern-matching.md:3 +#: src/ownership/moves-function-calls.md:24 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." +"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 "" -#: src/pattern-matching.md:6 -msgid "The patterns can be simple values, similarly to `switch` in C and C++:" +#: src/ownership/copy-clone.md:3 +msgid "" +"While move semantics are the default, certain types are copied by default:" msgstr "" -#: src/pattern-matching.md:8 +#: src/ownership/copy-clone.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let input = 'x';\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" -" }\n" +" let x = 42;\n" +" let y = x;\n" +" println!(\"x: {x}\");\n" +" println!(\"y: {y}\");\n" "}\n" "```" msgstr "" -#: src/pattern-matching.md:21 -msgid "The `_` pattern is a wildcard pattern which matches any value." +#: src/ownership/copy-clone.md:14 +msgid "These types implement the `Copy` trait." msgstr "" -#: src/pattern-matching.md:26 -msgid "" -"You might point out how some specific characters are being used when in a " -"pattern" +#: src/ownership/copy-clone.md:16 +msgid "You can opt-in your own types to use copy semantics:" msgstr "" -#: src/pattern-matching.md:27 -msgid "`|` as an `or`" +#: src/ownership/copy-clone.md:18 +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" +"}\n" +"```" msgstr "" -#: src/pattern-matching.md:28 -msgid "`..` can expand as much as it needs to be" +#: src/ownership/copy-clone.md:30 +msgid "After the assignment, both `p1` and `p2` own their own data." msgstr "" -#: src/pattern-matching.md:29 -msgid "`1..=5` represents an inclusive range" +#: src/ownership/copy-clone.md:31 +msgid "We can also use `p1.clone()` to explicitly copy the data." msgstr "" -#: src/pattern-matching.md:30 -msgid "`_` is a wild card" +#: src/ownership/copy-clone.md:35 +msgid "Copying and cloning are not the same thing:" msgstr "" -#: src/pattern-matching.md:31 +#: src/ownership/copy-clone.md:37 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`." +"Copying refers to bitwise copies of memory regions and does not work on " +"arbitrary objects." msgstr "" -#: src/pattern-matching.md:32 -msgid "You can demonstrate matching on a reference." +#: src/ownership/copy-clone.md:38 +msgid "" +"Copying does not allow for custom logic (unlike copy constructors in C++)." msgstr "" -#: src/pattern-matching.md:33 +#: src/ownership/copy-clone.md:39 msgid "" -"This might be a good time to bring up the concept of irrefutable patterns, " -"as the term can show up in error messages." +"Cloning is a more general operation and also allows for custom behavior by " +"implementing the `Clone` trait." msgstr "" -#: src/pattern-matching/destructuring-enums.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:" +#: src/ownership/copy-clone.md:40 +msgid "Copying does not work on types that implement the `Drop` trait." msgstr "" -#: src/pattern-matching/destructuring-enums.md:6 -msgid "" -"```rust,editable\n" -"enum Result {\n" -" Ok(i32),\n" -" Err(String),\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" -" }\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" -"}\n" -"```" +#: src/ownership/copy-clone.md:42 src/ownership/lifetimes-function-calls.md:29 +msgid "In the above example, try the following:" msgstr "" -#: src/pattern-matching/destructuring-enums.md:29 +#: src/ownership/copy-clone.md:44 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." +"Add a `String` field to `struct Point`. It will not compile because `String` " +"is not a `Copy` type." msgstr "" -#: src/pattern-matching/destructuring-enums.md:36 +#: src/ownership/copy-clone.md:45 msgid "" -"The `if`/`else` expression is returning an enum that is later unpacked with " -"a `match`." +"Remove `Copy` from the `derive` attribute. The compiler error is now in the " +"`println!` for `p1`." msgstr "" -#: src/pattern-matching/destructuring-enums.md:37 +#: src/ownership/copy-clone.md:46 +msgid "Show that it works if you clone `p1` instead." +msgstr "" + +#: src/ownership/copy-clone.md:48 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." +"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 "" -#: src/pattern-matching/destructuring-structs.md:3 -msgid "You can also destructure `structs`:" +#: src/ownership/borrowing.md:3 +msgid "" +"Instead of transferring ownership when calling a function, you can let a " +"function _borrow_ the value:" msgstr "" -#: src/pattern-matching/destructuring-structs.md:5 +#: src/ownership/borrowing.md:6 msgid "" "```rust,editable\n" -"struct Foo {\n" -" x: (u32, u32),\n" -" y: u32,\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" -"#[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" +" 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 "" -#: src/pattern-matching/destructuring-structs.md:23 -msgid "Change the literal values in `foo` to match with the other patterns." -msgstr "" - -#: src/pattern-matching/destructuring-structs.md:24 -msgid "Add a new field to `Foo` and make changes to the pattern as needed." -msgstr "" - -#: src/pattern-matching/destructuring-structs.md:25 -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." +#: src/ownership/borrowing.md:22 +msgid "The `add` function _borrows_ two points and returns a new point." msgstr "" -#: src/pattern-matching/destructuring-arrays.md:3 -msgid "" -"You can destructure arrays, tuples, and slices by matching on their elements:" +#: src/ownership/borrowing.md:23 +msgid "The caller retains ownership of the inputs." msgstr "" -#: src/pattern-matching/destructuring-arrays.md:5 -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" -"```" +#: src/ownership/borrowing.md:27 +msgid "Notes on stack returns:" msgstr "" -#: src/pattern-matching/destructuring-arrays.md:21 +#: src/ownership/borrowing.md:28 msgid "" -"Destructuring of slices of unknown length also works with patterns of fixed " -"length." +"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 "" -#: src/pattern-matching/destructuring-arrays.md:24 +#: src/ownership/borrowing.md:30 msgid "" "```rust,editable\n" -"fn main() {\n" -" inspect(&[0, -2, 3]);\n" -" inspect(&[0, -2, 3, 4]);\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" -"#[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" +"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 "" -#: src/pattern-matching/destructuring-arrays.md:41 -msgid "Create a new pattern using `_` to represent an element. " +#: src/ownership/borrowing.md:48 +msgid "The Rust compiler can do return value optimization (RVO)." msgstr "" -#: src/pattern-matching/destructuring-arrays.md:42 -msgid "Add more values to the array." +#: src/ownership/borrowing.md:49 +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 perform a simple and efficient `memcpy` " +"copy." msgstr "" -#: src/pattern-matching/destructuring-arrays.md:43 -msgid "" -"Point out that how `..` will expand to account for different number of " -"elements." +#: src/ownership/shared-unique-borrows.md:3 +msgid "Rust puts constraints on the ways you can borrow values:" msgstr "" -#: src/pattern-matching/destructuring-arrays.md:44 -msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" +#: src/ownership/shared-unique-borrows.md:5 +msgid "You can have one or more `&T` values at any given time, _or_" 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/ownership/shared-unique-borrows.md:6 +msgid "You can have exactly one `&mut T` value." msgstr "" -#: src/pattern-matching/match-guards.md:6 +#: src/ownership/shared-unique-borrows.md:8 msgid "" -"```rust,editable\n" -"#[rustfmt::skip]\n" +"```rust,editable,compile_fail\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" +" 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 "" -#: src/pattern-matching/match-guards.md:23 +#: src/ownership/shared-unique-borrows.md:25 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." +"The above code does not compile because `a` is borrowed as mutable (through " +"`c`) and as immutable (through `b`) at the same time." msgstr "" -#: src/pattern-matching/match-guards.md:24 +#: src/ownership/shared-unique-borrows.md:26 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." +"Move the `println!` statement for `b` before the scope that introduces `c` " +"to make the code compile." msgstr "" -#: src/pattern-matching/match-guards.md:26 -msgid "You can use the variables defined in the pattern in your if expression." +#: 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 "" -#: src/pattern-matching/match-guards.md:27 -msgid "" -"The condition defined in the guard applies to every expression in a pattern " -"with an `|`." +#: src/ownership/lifetimes.md:3 +msgid "A borrowed value has a _lifetime_:" msgstr "" -#: src/exercises/day-2/morning.md:1 -msgid "Day 2: Morning Exercises" +#: src/ownership/lifetimes.md:5 +msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." msgstr "" -#: src/exercises/day-2/morning.md:3 -msgid "We will look at implementing methods in two contexts:" +#: src/ownership/lifetimes.md:6 +msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." msgstr "" -#: src/exercises/day-2/morning.md:5 -msgid "Simple struct which tracks health statistics." +#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:23 +msgid "" +"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " +"lifetime `a`\"." msgstr "" -#: src/exercises/day-2/morning.md:7 -msgid "Multiple structs and enums for a drawing library." +#: src/ownership/lifetimes.md:9 +msgid "" +"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " +"yourself." msgstr "" -#: src/exercises/day-2/health-statistics.md:3 +#: src/ownership/lifetimes.md:11 msgid "" -"You're working on implementing a health-monitoring system. As part of that, " -"you need to keep track of users' health statistics." +"Lifetime annotations create constraints; the compiler verifies that there is " +"a valid solution." msgstr "" -#: src/exercises/day-2/health-statistics.md:6 +#: src/ownership/lifetimes.md:13 msgid "" -"You'll start with some stubbed functions in an `impl` block as well as a " -"`User` struct definition. Your goal is to implement the stubbed out methods " -"on the `User` `struct` defined in the `impl` block." +"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 "" -#: src/exercises/day-2/health-statistics.md:10 +#: src/ownership/lifetimes-function-calls.md:3 msgid "" -"Copy the code below to and fill in the missing " -"methods:" +"In addition to borrowing its arguments, a function can return a borrowed " +"value:" msgstr "" -#: src/exercises/day-2/health-statistics.md:13 +#: src/ownership/lifetimes-function-calls.md:5 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" -" name: String,\n" -" age: u32,\n" -" weight: f32,\n" -"}\n" -"\n" -"impl User {\n" -" pub fn new(name: String, age: u32, weight: f32) -> Self {\n" -" unimplemented!()\n" -" }\n" -"\n" -" pub fn name(&self) -> &str {\n" -" unimplemented!()\n" -" }\n" -"\n" -" pub fn age(&self) -> u32 {\n" -" unimplemented!()\n" -" }\n" -"\n" -" pub fn weight(&self) -> f32 {\n" -" unimplemented!()\n" -" }\n" -"\n" -" pub fn set_age(&mut self, new_age: u32) {\n" -" unimplemented!()\n" -" }\n" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -" pub fn set_weight(&mut self, new_weight: f32) {\n" -" unimplemented!()\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 bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" -" println!(\"I'm {} and my age is {}\", bob.name(), bob.age());\n" -"}\n" -"\n" -"#[test]\n" -"fn test_weight() {\n" -" let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" -" assert_eq!(bob.weight(), 155.2);\n" -"}\n" -"\n" -"#[test]\n" -"fn test_set_age() {\n" -" let mut bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" -" assert_eq!(bob.age(), 32);\n" -" bob.set_age(33);\n" -" assert_eq!(bob.age(), 33);\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 "" -#: src/exercises/day-2/points-polygons.md:1 -msgid "Polygon Struct" +#: src/ownership/lifetimes-function-calls.md:21 +msgid "`'a` is a generic parameter, it is inferred by the compiler." msgstr "" -#: src/exercises/day-2/points-polygons.md:3 -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:" +#: src/ownership/lifetimes-function-calls.md:22 +msgid "Lifetimes start with `'` and `'a` is a typical default name." msgstr "" -#: src/exercises/day-2/points-polygons.md:7 +#: src/ownership/lifetimes-function-calls.md:25 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" +"The _at least_ part is important when parameters are in different scopes." +msgstr "" + +#: 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 "" + +#: src/ownership/lifetimes-function-calls.md:32 +msgid "" +"```rust,ignore\n" +"#[derive(Debug)]\n" +"struct Point(i32, i32);\n" "\n" -"pub enum Shape {\n" -" Polygon(Polygon),\n" -" Circle(Circle),\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" -"#[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" +"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" -"\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" "```" msgstr "" -#: src/exercises/day-2/points-polygons.md:117 +#: src/ownership/lifetimes-function-calls.md:50 +msgid "Note how this does not compile since `p3` outlives `p2`." +msgstr "" + +#: src/ownership/lifetimes-function-calls.md:52 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." +"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 "" -#: src/exercises/day-2/points-polygons.md:120 -msgid "Other interesting parts of the exercise:" +#: src/ownership/lifetimes-function-calls.md:53 +msgid "Another way to explain it:" msgstr "" -#: src/exercises/day-2/points-polygons.md:122 +#: src/ownership/lifetimes-function-calls.md:54 msgid "" -"Derive a `Copy` trait for some structs, as in tests the methods sometimes " -"don't borrow their arguments." +"Two references to two values are borrowed by a function and the function " +"returns another reference." msgstr "" -#: src/exercises/day-2/points-polygons.md:123 +#: src/ownership/lifetimes-function-calls.md:56 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." +"It must have come from one of those two inputs (or from a global variable)." msgstr "" -#: src/control-flow.md:3 +#: src/ownership/lifetimes-function-calls.md:57 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." +"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/control-flow/blocks.md:3 +#: src/ownership/lifetimes-data-structures.md:3 msgid "" -"A block in Rust has a value and a type: the value is the last expression of " -"the block:" +"If a data type stores borrowed data, it must be annotated with a lifetime:" msgstr "" -#: src/control-flow/blocks.md:6 +#: src/ownership/lifetimes-data-structures.md:5 msgid "" "```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 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" +" 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 "" -#: src/control-flow/blocks.md:25 +#: src/ownership/lifetimes-data-structures.md:25 msgid "" -"The same rule is used for functions: the value of the function body is the " -"return value:" +"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 "" -#: src/control-flow/blocks.md:28 +#: src/ownership/lifetimes-data-structures.md:26 msgid "" -"```rust,editable\n" -"fn double(x: i32) -> i32 {\n" -" x + x\n" -"}\n" -"\n" -"fn main() {\n" -" println!(\"doubled: {}\", double(7));\n" -"}\n" -"```" +"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " +"the borrow checker throws an error." msgstr "" -#: src/control-flow/blocks.md:38 +#: src/ownership/lifetimes-data-structures.md:27 msgid "" -"However if the last expression ends with `;`, then the resulting value and " -"type is `()`." +"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/control-flow/blocks.md:43 -msgid "" -"The point of this slide is to show that blocks have a type and value in " -"Rust. " +#: src/ownership/lifetimes-data-structures.md:28 +msgid "When possible, make data structures own their data directly." msgstr "" -#: src/control-flow/blocks.md:44 +#: src/ownership/lifetimes-data-structures.md:29 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`." +"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/control-flow/if-expressions.md:1 -msgid "`if` expressions" +#: src/exercises/day-2/morning.md:1 +msgid "Day 2: Morning Exercises" msgstr "" -#: src/control-flow/if-expressions.md:3 +#: src/exercises/day-2/morning.md:3 +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 "" + +#: src/exercises/day-2/morning.md:7 +msgid "Multiple structs and enums for a drawing library." +msgstr "" + +#: src/exercises/day-2/book-library.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:" +"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/control-flow/if-expressions.md:7 +#: src/exercises/day-2/book-library.md:6 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" +" 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/control-flow/if-expressions.md:18 +#: src/exercises/day-2/book-library.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:" +"Use this to model a library's book collection. Copy the code below to " +" and update the types to make it compile:" msgstr "" -#: src/control-flow/if-expressions.md:22 +#: src/exercises/day-2/book-library.md:21 msgid "" -"```rust,editable\n" +"```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. 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" +"\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(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 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" +"// 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 mut x = 10;\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" +" let library = Library::new();\n" +"\n" +" //println!(\"The library is empty: 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() -> {}\", " +"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/control-flow/if-expressions.md:35 +#: src/exercises/day-2/book-library.md:102 +msgid "[Solution](solutions-afternoon.md#designing-a-library)" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:3 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." +"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 "" -#: src/control-flow/if-let-expressions.md:1 -msgid "`if let` expressions" +#: src/exercises/day-2/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 +msgid "`Iterator`" msgstr "" -#: src/control-flow/if-let-expressions.md:3 +#: src/exercises/day-2/iterators-and-ownership.md:10 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:" +"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/control-flow/if-let-expressions.md:7 +#: src/exercises/day-2/iterators-and-ownership.md:13 +msgid "" +"```rust\n" +"pub trait Iterator {\n" +" type Item;\n" +" fn next(&mut self) -> Option;\n" +"}\n" +"```" +msgstr "" + +#: 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" "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" +" 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/control-flow/if-let-expressions.md:18 -#: src/control-flow/while-let-expressions.md:21 -#: src/control-flow/match-expressions.md:23 -msgid "" -"See [pattern matching](../pattern-matching.md) for more details on patterns " -"in Rust." +#: src/exercises/day-2/iterators-and-ownership.md:34 +msgid "What is the type returned by the iterator? Test your answer here:" msgstr "" -#: src/control-flow/if-let-expressions.md:23 +#: src/exercises/day-2/iterators-and-ownership.md:36 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." +"```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/control-flow/if-let-expressions.md:24 -msgid "A common usage is handling `Some` values when working with `Option`." +#: src/exercises/day-2/iterators-and-ownership.md:46 +msgid "Why is this type used?" msgstr "" -#: src/control-flow/if-let-expressions.md:25 -msgid "" -"Unlike `match`, `if let` does not support guard clauses for pattern matching." +#: src/exercises/day-2/iterators-and-ownership.md:48 +msgid "`IntoIterator`" msgstr "" -#: src/control-flow/if-let-expressions.md:26 +#: src/exercises/day-2/iterators-and-ownership.md:50 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):" +"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/control-flow/if-let-expressions.md:28 +#: src/exercises/day-2/iterators-and-ownership.md:53 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" +"```rust\n" +"pub trait IntoIterator {\n" +" type Item;\n" +" type IntoIter: Iterator;\n" +"\n" +" fn into_iter(self) -> Self::IntoIter;\n" "}\n" "```" msgstr "" -#: src/control-flow/while-expressions.md:1 -msgid "`while` loops" +#: src/exercises/day-2/iterators-and-ownership.md:62 +msgid "" +"The syntax here means that every implementation of `IntoIterator` must " +"declare two types:" msgstr "" -#: src/control-flow/while-expressions.md:3 +#: 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 "" -"The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-" -"expr.html#predicate-loops) works very similar to other languages:" +"Note that `IntoIter` and `Item` are linked: the iterator must have the same " +"`Item` type, which means that it returns `Option`" msgstr "" -#: src/control-flow/while-expressions.md:6 +#: 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 "" -"```rust,editable\n" +"```rust,editable,compile_fail\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" +" 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/control-flow/while-let-expressions.md:1 -msgid "`while let` loops" +#: src/exercises/day-2/iterators-and-ownership.md:83 +msgid "`for` Loops" msgstr "" -#: src/control-flow/while-let-expressions.md:3 +#: src/exercises/day-2/iterators-and-ownership.md:85 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:" +"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 "" -#: src/control-flow/while-let-expressions.md:6 +#: src/exercises/day-2/iterators-and-ownership.md:89 msgid "" "```rust,editable\n" "fn main() {\n" -" let v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" +" let v: Vec = vec![String::from(\"foo\"), String::" +"from(\"bar\")];\n" "\n" -" while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\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/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." +#: src/exercises/day-2/iterators-and-ownership.md:103 +msgid "What is the type of `word` in each loop?" msgstr "" -#: src/control-flow/while-let-expressions.md:26 +#: src/exercises/day-2/iterators-and-ownership.md:105 msgid "" -"Point out that the `while let` loop will keep going as long as the value " -"matches the pattern." +"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 "" -#: 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." +#: src/structs.md:3 +msgid "Like C and C++, Rust has support for custom structs:" msgstr "" -#: src/control-flow/for-expressions.md:1 -msgid "`for` loops" -msgstr "" - -#: 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-expression.md). It will " -"automatically call `into_iter()` on the expression and then iterate over it:" -msgstr "" - -#: src/control-flow/for-expressions.md:7 +#: src/structs.md:5 msgid "" "```rust,editable\n" -"fn main() {\n" -" let v = vec![10, 20, 30];\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" "\n" -" for x in v {\n" -" println!(\"x: {x}\");\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" -" for i in (0..10).step_by(2) {\n" -" println!(\"i: {i}\");\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/control-flow/for-expressions.md:21 -msgid "You can use `break` and `continue` here as usual." +#: src/structs.md:33 +msgid "Structs work like in C or C++." msgstr "" -#: src/control-flow/for-expressions.md:25 -msgid "Index iteration is not a special syntax in Rust for just that case." +#: src/structs.md:34 +msgid "Like in C++, and unlike in C, no typedef is needed to define a type." msgstr "" -#: src/control-flow/for-expressions.md:26 -msgid "`(0..10)` is a range that implements an `Iterator` trait. " +#: src/structs.md:35 +msgid "Unlike in C++, there is no inheritance between structs." msgstr "" -#: src/control-flow/for-expressions.md:27 +#: src/structs.md:36 msgid "" -"`step_by` is a method that returns another `Iterator` that skips every other " -"element. " +"Methods are defined in an `impl` block, which we will see in following " +"slides." msgstr "" -#: src/control-flow/for-expressions.md:28 +#: src/structs.md:37 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()`." +"This may be a good time to let people know there are different types of " +"structs. " msgstr "" -#: src/control-flow/loop-expressions.md:1 -msgid "`loop` expressions" +#: src/structs.md:38 +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. " msgstr "" -#: src/control-flow/loop-expressions.md:3 +#: src/structs.md:39 msgid "" -"Finally, there is a [`loop` keyword](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#infinite-loops) which creates an endless loop." +"The next slide will introduce Tuple structs, used when the field names are " +"not important." msgstr "" -#: src/control-flow/loop-expressions.md:6 -msgid "Here you must either `break` or `return` to stop the loop:" +#: src/structs.md:40 +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." msgstr "" -#: src/control-flow/loop-expressions.md:8 +#: 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" +"struct Point(i32, i32);\n" +"\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" +" let p = Point(17, 23);\n" +" println!(\"({}, {})\", p.0, p.1);\n" "}\n" "```" msgstr "" -#: src/control-flow/loop-expressions.md:27 -msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." -msgstr "" - -#: src/control-flow/loop-expressions.md:28 -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)." -msgstr "" - -#: src/control-flow/match-expressions.md:1 -msgid "`match` expressions" -msgstr "" - -#: src/control-flow/match-expressions.md:3 -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:" +#: src/structs/tuple-structs.md:14 +msgid "This is often used for single-field wrappers (called newtypes):" msgstr "" -#: src/control-flow/match-expressions.md:7 +#: src/structs/tuple-structs.md:16 msgid "" -"```rust,editable\n" +"```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" +"\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 force = compute_thruster_force();\n" +" set_thruster_force(force);\n" "}\n" +"\n" "```" msgstr "" -#: src/control-flow/match-expressions.md:20 +#: src/structs/tuple-structs.md:37 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 `()`." +"Newtypes are a great way to encode additional information about the value in " +"a primitive type, for example:" msgstr "" -#: src/control-flow/match-expressions.md:28 -msgid "Save the match expression to a variable and print it out." +#: src/structs/tuple-structs.md:38 +msgid "The number is measured in some units: `Newtons` in the example above." msgstr "" -#: src/control-flow/match-expressions.md:29 -msgid "Remove `.as_deref()` and explain the error." +#: 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 "" -#: src/control-flow/match-expressions.md:30 +#: src/structs/tuple-structs.md:40 msgid "" -"`std::env::args().next()` returns an `Option`, but we cannot match " -"against `String`." +"Demonstrate how to add a `f64` value to a `Newtons` type by accessing the " +"single field in the newtype." msgstr "" -#: src/control-flow/match-expressions.md:31 +#: src/structs/tuple-structs.md:41 msgid "" -"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " -"this turns `Option` into `Option<&str>`." +"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " +"for instance using booleans as integers." msgstr "" -#: src/control-flow/match-expressions.md:32 -msgid "" -"We can now use pattern matching to match against the `&str` inside `Option`." +#: src/structs/tuple-structs.md:42 +msgid "Operator overloading is discussed on Day 3 (generics)." msgstr "" -#: src/control-flow/break-continue.md:1 -msgid "`break` and `continue`" +#: 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 "" -#: src/control-flow/break-continue.md:3 +#: src/structs/field-shorthand.md:3 msgid "" -"If you want to exit a loop early, use [`break`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#break-expressions)," +"If you already have variables with the right names, then you can create the " +"struct using a shorthand:" msgstr "" -#: src/control-flow/break-continue.md:4 +#: src/structs/field-shorthand.md:6 msgid "" -"If you want to immediately start the next iteration use [`continue`](https://" -"doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions)." +"```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" +"```" msgstr "" -#: src/control-flow/break-continue.md:7 +#: src/structs/field-shorthand.md:27 msgid "" -"Both `continue` and `break` can optionally take a label argument which is " -"used to break out of nested loops:" +"The `new` function could be written using `Self` as a type, as it is " +"interchangeable with the struct type name" msgstr "" -#: src/control-flow/break-continue.md:10 +#: src/structs/field-shorthand.md:29 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" +"#[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" "}\n" "```" msgstr "" -#: src/control-flow/break-continue.md:28 +#: src/structs/field-shorthand.md:41 msgid "" -"In this case we break the outer loop after 3 iterations of the inner loop." +"Implement the `Default` trait for the struct. Define some fields and use the " +"default values for the other fields." msgstr "" -#: src/std.md:3 +#: src/structs/field-shorthand.md:43 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 "" - -#: src/std.md:7 -msgid "The common vocabulary types include:" +"```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 "" -#: src/std.md:9 +#: src/structs/field-shorthand.md:68 +msgid "Methods are defined in the `impl` block." +msgstr "" + +#: src/structs/field-shorthand.md:69 msgid "" -"[`Option` and `Result`](std/option-result.md) types: used for optional " -"values and [error handling](error-handling.md)." +"Use struct update syntax to define a new structure using `peter`. Note that " +"the variable `peter` will no longer be accessible afterwards." msgstr "" -#: src/std.md:12 -msgid "[`String`](std/string.md): the default string type used for owned data." +#: src/structs/field-shorthand.md:70 +msgid "" +"Use `{:#?}` when printing structs to request the `Debug` representation." msgstr "" -#: src/std.md:14 -msgid "[`Vec`](std/vec.md): a standard extensible vector." +#: src/methods.md:3 +msgid "" +"Rust allows you to associate functions with your new types. You do this with " +"an `impl` block:" msgstr "" -#: src/std.md:16 +#: src/methods.md:6 msgid "" -"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " -"algorithm." +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" +"\n" +"impl Person {\n" +" fn say_hello(&self) {\n" +" println!(\"Hello, my name is {}\", self.name);\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let peter = Person {\n" +" name: String::from(\"Peter\"),\n" +" age: 27,\n" +" };\n" +" peter.say_hello();\n" +"}\n" +"```" msgstr "" -#: src/std.md:19 -msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." +#: src/methods.md:31 +msgid "It can be helpful to introduce methods by comparing them to functions." msgstr "" -#: src/std.md:21 +#: src/methods.md:32 msgid "" -"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " -"data." +"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/std.md:25 +#: src/methods.md:33 msgid "" -"In fact, Rust contains several layers of the Standard Library: `core`, " -"`alloc` and `std`. " +"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/std.md:26 +#: src/methods.md:34 +msgid "Point out the use of the keyword `self`, a method receiver." +msgstr "" + +#: src/methods.md:35 msgid "" -"`core` includes the most basic types and functions that don't depend on " -"`libc`, allocator or even the presence of an operating system. " +"Show that it is an abbreviated term for `self: Self` and perhaps show how " +"the struct name could also be used." msgstr "" -#: src/std.md:28 +#: src/methods.md:36 msgid "" -"`alloc` includes types which require a global heap allocator, such as `Vec`, " -"`Box` and `Arc`." +"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/std.md:29 +#: src/methods.md:37 msgid "" -"Embedded Rust applications often only use `core`, and sometimes `alloc`." +"Note how `self` is used like other structs and dot notation can be used to " +"refer to individual fields." msgstr "" -#: src/std/option-result.md:1 -msgid "`Option` and `Result`" +#: 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." msgstr "" -#: src/std/option-result.md:3 -msgid "The types represent optional data:" +#: src/methods.md:39 +msgid "We describe the distinction between method receivers next." msgstr "" -#: src/std/option-result.md:5 +#: src/methods/receiver.md:3 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" -"```" +"The `&self` above indicates that the method borrows the object immutably. " +"There are other possible receivers for a method:" msgstr "" -#: src/std/option-result.md:18 -msgid "`Option` and `Result` are widely used not just in the standard library." +#: 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." msgstr "" -#: src/std/option-result.md:19 -msgid "`Option<&T>` has zero space overhead compared to `&T`." +#: src/methods/receiver.md:8 +msgid "" +"`&mut self`: borrows the object from the caller using a unique and mutable " +"reference. The object can be used again afterwards." msgstr "" -#: src/std/option-result.md:20 +#: src/methods/receiver.md:10 msgid "" -"`Result` is the standard type to implement error handling as we will see on " -"Day 3." +"`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/std/option-result.md:21 -msgid "`binary_search` returns `Result`." +#: src/methods/receiver.md:14 +msgid "`mut self`: same as above, but the method can mutate the object. " msgstr "" -#: src/std/option-result.md:22 -msgid "If found, `Result::Ok` holds the index where the element is found." +#: src/methods/receiver.md:15 +msgid "" +"No receiver: this becomes a static method on the struct. Typically used to " +"create constructors which are called `new` by convention." msgstr "" -#: src/std/option-result.md:23 +#: src/methods/receiver.md:18 msgid "" -"Otherwise, `Result::Err` contains the index where such an element should be " -"inserted." +"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/std/string.md:3 +#: src/methods/receiver.md:24 msgid "" -"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " -"standard heap-allocated growable UTF-8 string buffer:" +"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/std/string.md:5 +#: src/methods/example.md:3 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" +"#[derive(Debug)]\n" +"struct Race {\n" +" name: String,\n" +" laps: Vec,\n" +"}\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" +"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" -" let s3 = String::from(\"🇨🇭\");\n" -" println!(\"s3: len = {}, number of chars = {}\", s3.len(),\n" -" s3.chars().count());\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/std/string.md:22 -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`." +#: src/methods/example.md:47 +msgid "All four methods here use a different method receiver." msgstr "" -#: src/std/string.md:30 +#: src/methods/example.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." +"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/std/string.md:31 +#: src/methods/example.md:49 msgid "" -"`String::len` returns the size of the `String` in bytes (which can be " -"different from its length in characters)." +"You can showcase the error that appears when trying to call `finish` twice." msgstr "" -#: src/std/string.md:32 +#: src/methods/example.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)." +"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/std/string.md:33 +#: src/methods/example.md:51 msgid "" -"When people refer to strings they could either be talking about `&str` or " -"`String`." -msgstr "" - -#: src/std/string.md:34 -msgid "" -"When a type implements `Deref`, the compiler will let you " -"transparently call methods from `T`." -msgstr "" - -#: src/std/string.md:35 -msgid "" -"`String` implements `Deref` which transparently gives it " -"access to `str`'s methods." -msgstr "" - -#: src/std/string.md:36 -msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;." +"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/std/string.md:37 -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." +#: src/exercises/day-2/afternoon.md:1 +msgid "Day 2: Afternoon Exercises" msgstr "" -#: src/std/string.md:38 -msgid "Compare the different ways to index a `String`:" +#: src/exercises/day-2/afternoon.md:3 +msgid "The exercises for this afternoon will focus on strings and iterators." msgstr "" -#: src/std/string.md:39 +#: src/exercises/day-2/health-statistics.md:3 msgid "" -"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " -"out-of-bounds." +"You're working on implementing a health-monitoring system. As part of that, " +"you need to keep track of users' health statistics." msgstr "" -#: src/std/string.md:40 +#: src/exercises/day-2/health-statistics.md:6 msgid "" -"To a substring by using `s3[0..4]`, where that slice is on character " -"boundaries or not." -msgstr "" - -#: src/std/vec.md:1 -msgid "`Vec`" +"You'll start with some stubbed functions in an `impl` block as well as a " +"`User` struct definition. Your goal is to implement the stubbed out methods " +"on the `User` `struct` defined in the `impl` block." msgstr "" -#: src/std/vec.md:3 +#: src/exercises/day-2/health-statistics.md:10 msgid "" -"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " -"resizable heap-allocated buffer:" +"Copy the code below to and fill in the missing " +"methods:" msgstr "" -#: src/std/vec.md:5 +#: src/exercises/day-2/health-statistics.md:13 msgid "" -"```rust,editable\n" +"```rust,should_panic\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" +"\n" +"pub struct User {\n" +" name: String,\n" +" age: u32,\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, height: f32) -> Self {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn name(&self) -> &str {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn age(&self) -> u32 {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn height(&self) -> f32 {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn doctor_visits(&self) -> u32 {\n" +" unimplemented!()\n" +" }\n" +"\n" +" pub fn set_age(&mut self, new_age: u32) {\n" +" unimplemented!()\n" +" }\n" +"\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" +"\n" "fn main() {\n" -" let mut v1 = Vec::new();\n" -" v1.push(42);\n" -" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n" +" let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" +" println!(\"I'm {} and my age is {}\", bob.name(), bob.age());\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" +"#[test]\n" +"fn test_height() {\n" +" let bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" +" assert_eq!(bob.height(), 155.2);\n" +"}\n" "\n" -" // Canonical macro to initialize a vector with elements.\n" -" let mut v3 = vec![0, 0, 1, 2, 3, 4];\n" +"#[test]\n" +"fn test_set_age() {\n" +" let mut bob = User::new(String::from(\"Bob\"), 32, 155.2);\n" +" assert_eq!(bob.age(), 32);\n" +" bob.set_age(33);\n" +" assert_eq!(bob.age(), 33);\n" +"}\n" "\n" -" // Retain only the even elements.\n" -" v3.retain(|x| x % 2 == 0);\n" -" println!(\"{v3:?}\");\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" -" // Remove consecutive duplicates.\n" -" v3.dedup();\n" -" println!(\"{v3:?}\");\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 "" -#: src/std/vec.md:29 +#: src/std.md:3 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`." +"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 "" -#: src/std/vec.md:37 -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." +#: src/std.md:7 +msgid "The common vocabulary types include:" msgstr "" -#: src/std/vec.md:40 +#: src/std.md:9 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." +"[`Option` and `Result`](std/option-result.md) types: used for optional " +"values and [error handling](error-handling.md)." 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/std.md:12 +msgid "[`String`](std/string.md): the default string type used for owned data." 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/std.md:14 +msgid "[`Vec`](std/vec.md): a standard extensible vector." msgstr "" -#: src/std/vec.md:46 +#: src/std.md:16 msgid "" -"Show iterating over a vector and mutating the value: `for e in &mut v { *e " -"+= 50; }`" +"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " +"algorithm." msgstr "" -#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 -msgid "`HashMap`" +#: src/std.md:19 +msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." msgstr "" -#: src/std/hashmap.md:3 -msgid "Standard hash map with protection against HashDoS attacks:" +#: src/std.md:21 +msgid "" +"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " +"data." msgstr "" -#: src/std/hashmap.md:5 +#: src/std.md:25 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" -"```" +"In fact, Rust contains several layers of the Standard Library: `core`, " +"`alloc` and `std`. " msgstr "" -#: src/std/hashmap.md:38 +#: src/std.md:26 msgid "" -"`HashMap` is not defined in the prelude and needs to be brought into scope." +"`core` includes the most basic types and functions that don't depend on " +"`libc`, allocator or even the presence of an operating system. " msgstr "" -#: src/std/hashmap.md:39 +#: src/std.md:28 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." +"`alloc` includes types which require a global heap allocator, such as `Vec`, " +"`Box` and `Arc`." msgstr "" -#: src/std/hashmap.md:41 +#: src/std.md:29 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" -"```" +"Embedded Rust applications often only use `core`, and sometimes `alloc`." msgstr "" -#: src/std/hashmap.md:49 -msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." +#: src/std/option-result.md:1 +msgid "`Option` and `Result`" msgstr "" -#: src/std/hashmap.md:50 -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:" +#: src/std/option-result.md:3 +msgid "The types represent optional data:" msgstr "" -#: src/std/hashmap.md:52 +#: src/std/option-result.md:5 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" +"```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" "```" msgstr "" -#: src/std/hashmap.md:59 -msgid "" -"Alternatively HashMap can be built from any `Iterator` which yields key-" -"value tuples." +#: src/std/option-result.md:18 +msgid "`Option` and `Result` are widely used not just in the standard library." 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/std/option-result.md:19 +msgid "`Option<&T>` has zero space overhead compared to `&T`." msgstr "" -#: src/std/hashmap.md:62 +#: src/std/option-result.md:20 msgid "" -"Try removing `to_string()` from the example above and see if it still " -"compiles. Where do you think we might run into issues?" +"`Result` is the standard type to implement error handling as we will see on " +"Day 3." msgstr "" -#: src/std/box.md:1 -msgid "`Box`" +#: src/std/option-result.md:21 +msgid "`binary_search` returns `Result`." msgstr "" -#: 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:" +#: src/std/option-result.md:22 +msgid "If found, `Result::Ok` holds the index where the element is found." msgstr "" -#: src/std/box.md:5 +#: src/std/option-result.md:23 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let five = Box::new(5);\n" -" println!(\"five: {}\", *five);\n" -"}\n" -"```" +"Otherwise, `Result::Err` contains the index where such an element should be " +"inserted." msgstr "" -#: src/std/box.md:13 +#: src/std/string.md:3 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - -. .- - - - - - -.\n" -": : : :\n" -": five : : :\n" -": +-----+ : : +-----+ :\n" -": | o---|---+-----+-->| 5 | :\n" -": +-----+ : : +-----+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - -' `- - - - - - -'\n" -"```" +"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " +"standard heap-allocated growable UTF-8 string buffer:" msgstr "" -#: src/std/box.md:26 +#: src/std/string.md:5 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)." +"```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" +"\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 "" -#: src/std/box.md:34 +#: src/std/string.md:22 msgid "" -"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " -"not null. " +"`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 "" -#: src/std/box.md:35 +#: src/std/string.md:30 msgid "" -"In the above example, you can even leave out the `*` in the `println!` " -"statement thanks to `Deref`. " -msgstr "" - -#: src/std/box.md:36 -msgid "A `Box` can be useful when you:" +"`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 "" -#: src/std/box.md:37 +#: src/std/string.md:31 msgid "" -"have a type whose size that can't be known at compile time, but the Rust " -"compiler wants to know an exact size." +"`String::len` returns the size of the `String` in bytes (which can be " +"different from its length in characters)." msgstr "" -#: src/std/box.md:38 +#: src/std/string.md:32 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." -msgstr "" - -#: src/std/box-recursive.md:1 -msgid "Box with Recursive Data Structures" +"`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 "" -#: src/std/box-recursive.md:3 +#: src/std/string.md:33 msgid "" -"Recursive data types or data types with dynamic sizes need to use a `Box`:" +"When people refer to strings they could either be talking about `&str` or " +"`String`." msgstr "" -#: src/std/box-recursive.md:5 src/std/box-niche.md:3 +#: src/std/string.md:34 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" -"}\n" -"```" +"When a type implements `Deref`, the compiler will let you " +"transparently call methods from `T`." msgstr "" -#: src/std/box-recursive.md:18 +#: src/std/string.md:35 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " -"- -.\n" -": : : :\n" -": " -"list : : :\n" -": +------+----+----+ : : +------+----+----+ +------+----+----" -"+ :\n" -": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // " -"| :\n" -": +------+----+----+ : : +------+----+----+ +------+----+----" -"+ :\n" -": : : :\n" -": : : :\n" -"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " -"- -'\n" -"```" +"`String` implements `Deref` which transparently gives it " +"access to `str`'s methods." 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/std/string.md:36 +msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;." msgstr "" -#: src/std/box-recursive.md:36 +#: src/std/string.md:37 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." +"`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 "" -#: 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." +#: src/std/string.md:38 +msgid "Compare the different ways to index a `String`:" msgstr "" -#: src/std/box-niche.md:16 +#: src/std/string.md:39 msgid "" -"A `Box` cannot be empty, so the pointer is always valid and non-`null`. This " -"allows the compiler to optimize the memory layout:" +"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " +"out-of-bounds." msgstr "" -#: src/std/box-niche.md:19 +#: src/std/string.md:40 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " -"-.\n" -": : : :\n" -": " -"list : : :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " -"| :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " -"-'\n" -"```" +"To a substring by using `s3[0..4]`, where that slice is on character " +"boundaries or not." msgstr "" -#: src/std/rc.md:1 -msgid "`Rc`" +#: src/std/vec.md:1 +msgid "`Vec`" msgstr "" -#: src/std/rc.md:3 +#: src/std/vec.md:3 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:" +"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " +"resizable heap-allocated buffer:" msgstr "" -#: src/std/rc.md:6 +#: src/std/vec.md:5 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 mut v1 = Vec::new();\n" +" v1.push(42);\n" +" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n" "\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\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" +" // 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" "```" msgstr "" -#: src/std/rc.md:18 +#: src/std/vec.md:29 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 "" - -#: src/std/rc.md:20 -msgid "" -"See [`Arc`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) if you are " -"in a multi-threaded context." +"`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 "" -#: src/std/rc.md:21 +#: src/std/vec.md:37 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." +"`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 "" -#: src/std/rc.md:31 +#: src/std/vec.md:40 msgid "" -"`Rc`'s count ensures that its contained value is valid for as long as there " -"are references." -msgstr "" - -#: src/std/rc.md:32 -msgid "Like C++'s `std::shared_ptr`." +"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/std/rc.md:33 +#: src/std/vec.md:42 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." +"`vec![...]` is a canonical macro to use instead of `Vec::new()` and it " +"supports adding initial elements to the vector." msgstr "" -#: src/std/rc.md:34 +#: src/std/vec.md:44 msgid "" -"`make_mut` actually clones the inner value if necessary (\"clone-on-write\") " -"and returns a mutable reference." +"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/rc.md:35 -msgid "Use `Rc::strong_count` to check the reference count." +#: 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/rc.md:36 -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." +#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 +msgid "`HashMap`" msgstr "" -#: src/std/rc.md:37 -msgid "" -"`Rc::downgrade` gives you a _weakly reference-counted_ object to create " -"cycles that will be dropped properly (likely in combination with `RefCell`)." +#: src/std/hashmap.md:3 +msgid "Standard hash map with protection against HashDoS attacks:" msgstr "" -#: src/std/rc.md:41 +#: src/std/hashmap.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" -"}\n" +"use std::collections::HashMap;\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" +" 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" -" println!(\"graph: {root:#?}\");\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" "```" msgstr "" -#: src/modules.md:3 -msgid "We have seen how `impl` blocks let us namespace functions to a type." +#: src/std/hashmap.md:38 +msgid "" +"`HashMap` is not defined in the prelude and needs to be brought into scope." msgstr "" -#: src/modules.md:5 -msgid "Similarly, `mod` lets us namespace types and functions:" +#: 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." msgstr "" -#: src/modules.md:7 +#: src/std/hashmap.md:41 msgid "" -"```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" +"```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 "" -#: src/modules.md:28 +#: src/std/hashmap.md:49 +msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." +msgstr "" + +#: src/std/hashmap.md:50 msgid "" -"Packages provide functionality and include a `Cargo.toml` file that " -"describes how to build a bundle of 1+ crates." +"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 "" -#: src/modules.md:29 +#: src/std/hashmap.md:52 msgid "" -"Crates are a tree of modules, where a binary crate creates an executable and " -"a library crate compiles to a library." +"```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 "" -#: src/modules.md:30 -msgid "Modules define organization, scope, and are the focus of this section." +#: src/std/hashmap.md:59 +msgid "" +"Alternatively HashMap can be built from any `Iterator` which yields key-" +"value tuples." msgstr "" -#: src/modules/visibility.md:3 -msgid "Modules are a privacy boundary:" +#: 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." msgstr "" -#: src/modules/visibility.md:5 -msgid "Module items are private by default (hides implementation details)." +#: 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/modules/visibility.md:6 -msgid "Parent and sibling items are always visible." +#: src/std/hashmap.md:64 +msgid "" +"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 "" -#: src/modules/visibility.md:7 +#: src/std/box.md:1 +msgid "`Box`" +msgstr "" + +#: src/std/box.md:3 msgid "" -"In other words, if an item is visible in module `foo`, it's visible in all " -"the descendants of `foo`." +"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned " +"pointer to data on the heap:" msgstr "" -#: src/modules/visibility.md:10 +#: src/std/box.md:5 msgid "" "```rust,editable\n" -"mod outer {\n" -" fn private() {\n" -" println!(\"outer::private\");\n" -" }\n" -"\n" -" pub fn public() {\n" -" println!(\"outer::public\");\n" -" }\n" -"\n" -" mod inner {\n" -" fn private() {\n" -" println!(\"outer::inner::private\");\n" -" }\n" -"\n" -" pub fn public() {\n" -" println!(\"outer::inner::public\");\n" -" super::private();\n" -" }\n" -" }\n" -"}\n" -"\n" "fn main() {\n" -" outer::public();\n" +" let five = Box::new(5);\n" +" println!(\"five: {}\", *five);\n" "}\n" "```" msgstr "" -#: src/modules/visibility.md:39 -msgid "Use the `pub` keyword to make modules public." +#: src/std/box.md:13 +msgid "" +"```bob\n" +" Stack Heap\n" +".- - - - - - -. .- - - - - - -.\n" +": : : :\n" +": five : : :\n" +": +-----+ : : +-----+ :\n" +": | o---|---+-----+-->| 5 | :\n" +": +-----+ : : +-----+ :\n" +": : : :\n" +": : : :\n" +"`- - - - - - -' `- - - - - - -'\n" +"```" msgstr "" -#: src/modules/visibility.md:41 +#: src/std/box.md:26 msgid "" -"Additionally, there are advanced `pub(...)` specifiers to restrict the scope " -"of public visibility." +"`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 "" -#: src/modules/visibility.md:43 +#: src/std/box.md:34 msgid "" -"See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-" -"privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)." +"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " +"not null. " msgstr "" -#: src/modules/visibility.md:44 -msgid "Configuring `pub(crate)` visibility is a common pattern." +#: src/std/box.md:35 +msgid "" +"In the above example, you can even leave out the `*` in the `println!` " +"statement thanks to `Deref`. " msgstr "" -#: src/modules/visibility.md:45 -msgid "Less commonly, you can give visibility to a specific path." +#: src/std/box.md:36 +msgid "A `Box` can be useful when you:" msgstr "" -#: src/modules/visibility.md:46 +#: src/std/box.md:37 msgid "" -"In any case, visibility must be granted to an ancestor module (and all of " -"its descendants)." +"have a type whose size that can't be known at compile time, but the Rust " +"compiler wants to know an exact size." msgstr "" -#: src/modules/paths.md:3 -msgid "Paths are resolved as follows:" +#: src/std/box.md:38 +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." 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," +#: src/std/box-recursive.md:1 +msgid "Box with Recursive Data Structures" msgstr "" -#: src/modules/paths.md:7 -msgid "`super::foo` refers to `foo` in the parent module." +#: src/std/box-recursive.md:3 +msgid "" +"Recursive data types or data types with dynamic sizes need to use a `Box`:" msgstr "" -#: src/modules/paths.md:9 -msgid "As an absolute path:" +#: src/std/box-recursive.md:5 src/std/box-niche.md:3 +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" +"}\n" +"```" msgstr "" -#: src/modules/paths.md:10 -msgid "`crate::foo` refers to `foo` in the root of the current crate," +#: src/std/box-recursive.md:18 +msgid "" +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"- -.\n" +": : : :\n" +": " +"list : : :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // " +"| :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": : : :\n" +": : : :\n" +"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"- -'\n" +"```" msgstr "" -#: src/modules/paths.md:11 -msgid "`bar::foo` refers to `foo` in the `bar` crate." +#: src/std/box-recursive.md:33 +msgid "" +"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 "" -#: src/modules/paths.md:13 +#: src/std/box-recursive.md:36 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:" +"`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 "" -#: src/modules/paths.md:16 +#: src/std/box-recursive.md:39 msgid "" -"```rust,editable\n" -"use std::collections::HashSet;\n" -"use std::mem::transmute;\n" -"```" +"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 "" -#: src/modules/filesystem.md:3 -msgid "The module content can be omitted:" +#: 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 "" -#: src/modules/filesystem.md:5 +#: src/std/box-niche.md:19 msgid "" -"```rust,editable,compile_fail\n" -"mod garden;\n" +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"-.\n" +": : : :\n" +": " +"list : : :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " +"| :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": : : :\n" +": : : :\n" +"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"-'\n" "```" msgstr "" -#: src/modules/filesystem.md:9 -msgid "The `garden` module content is found at:" +#: src/std/rc.md:1 +msgid "`Rc`" msgstr "" -#: src/modules/filesystem.md:11 -msgid "`src/garden.rs` (modern Rust 2018 style)" +#: src/std/rc.md:3 +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:" msgstr "" -#: src/modules/filesystem.md:12 -msgid "`src/garden/mod.rs` (older Rust 2015 style)" +#: src/std/rc.md:6 +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" +"\n" +" println!(\"a: {a}\");\n" +" println!(\"b: {b}\");\n" +"}\n" +"```" msgstr "" -#: src/modules/filesystem.md:14 -msgid "Similarly, a `garden::vegetables` module can be found at:" +#: src/std/rc.md:18 +msgid "" +"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 "" -#: src/modules/filesystem.md:16 -msgid "`src/garden/vegetables.rs` (modern Rust 2018 style)" +#: src/std/rc.md:19 +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." msgstr "" -#: src/modules/filesystem.md:17 -msgid "`src/garden/vegetables/mod.rs` (older Rust 2015 style)" +#: src/std/rc.md:29 +msgid "" +"`Rc`'s count ensures that its contained value is valid for as long as there " +"are references." msgstr "" -#: src/modules/filesystem.md:19 -msgid "The `crate` root is in:" +#: src/std/rc.md:30 +msgid "`Rc` in Rust is like `std::shared_ptr` in C++." msgstr "" -#: src/modules/filesystem.md:21 -msgid "`src/lib.rs` (for a library crate)" +#: src/std/rc.md:31 +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." msgstr "" -#: src/modules/filesystem.md:22 -msgid "`src/main.rs` (for a binary crate)" +#: src/std/rc.md:32 +msgid "" +"`make_mut` actually clones the inner value if necessary (\"clone-on-write\") " +"and returns a mutable reference." msgstr "" -#: src/modules/filesystem.md:24 -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." +#: src/std/rc.md:33 +msgid "Use `Rc::strong_count` to check the reference count." msgstr "" -#: src/modules/filesystem.md:27 +#: src/std/rc.md:34 msgid "" -"```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" -"```" +"`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 "" -#: src/modules/filesystem.md:44 +#: src/std/cell.md:1 +msgid "`Cell` and `RefCell`" +msgstr "" + +#: src/std/cell.md:3 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.)" +"[`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 "" -#: src/modules/filesystem.md:47 -msgid "The following is valid:" +#: src/std/cell.md:8 +msgid "" +"`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 "" -#: src/modules/filesystem.md:49 +#: src/std/cell.md:12 msgid "" -"```ignore\n" -"src/\n" -"├── main.rs\n" -"├── top_module.rs\n" -"└── top_module/\n" -" └── sub_module.rs\n" +"```rust,editable\n" +"use std::cell::RefCell;\n" +"use std::rc::Rc;\n" +"\n" +"#[derive(Debug, Default)]\n" +"struct Node {\n" +" value: i64,\n" +" children: Vec>>,\n" +"}\n" +"\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/modules/filesystem.md:57 +#: src/std/cell.md:47 msgid "" -"The main reason for the change is to prevent many files named `mod.rs`, " -"which can be hard to distinguish in IDEs." +"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 "" -#: src/modules/filesystem.md:60 +#: src/std/cell.md:48 msgid "" -"Rust will look for modules in `modulename/mod.rs` and `modulename.rs`, but " -"this can be changed with a compiler directive:" +"To do anything with a Node, you must call a `RefCell` method, usually " +"`borrow` or `borrow_mut`." msgstr "" -#: src/modules/filesystem.md:63 +#: src/std/cell.md:49 msgid "" -"```rust,ignore\n" -"#[path = \"some/path.rs\"]\n" -"mod some_module { }\n" -"```" +"Demonstrate that reference loops can be created by adding `root` to `subtree." +"children` (don't try to print it!)." msgstr "" -#: src/modules/filesystem.md:68 +#: src/std/cell.md:50 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." +"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/exercises/day-2/afternoon.md:1 -msgid "Day 2: Afternoon Exercises" +#: src/modules.md:3 +msgid "We have seen how `impl` blocks let us namespace functions to a type." msgstr "" -#: src/exercises/day-2/afternoon.md:3 -msgid "The exercises for this afternoon will focus on strings and iterators." +#: src/modules.md:5 +msgid "Similarly, `mod` lets us namespace types and functions:" msgstr "" -#: src/exercises/day-2/luhn.md:3 +#: src/modules.md:7 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:" +"```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/exercises/day-2/luhn.md:7 -msgid "Ignore all spaces. Reject number with less than two digits." +#: 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/exercises/day-2/luhn.md:9 +#: src/modules.md:29 msgid "" -"Moving from right to left, double every second digit: for the number `1234`, " -"we double `3` and `1`." +"Crates are a tree of modules, where a binary crate creates an executable and " +"a library crate compiles to a library." msgstr "" -#: src/exercises/day-2/luhn.md:12 -msgid "" -"After doubling a digit, sum the digits. So doubling `7` becomes `14` which " -"becomes `5`." +#: src/modules.md:30 +msgid "Modules define organization, scope, and are the focus of this section." msgstr "" -#: src/exercises/day-2/luhn.md:15 -msgid "Sum all the undoubled and doubled digits." +#: src/modules/visibility.md:3 +msgid "Modules are a privacy boundary:" msgstr "" -#: src/exercises/day-2/luhn.md:17 -msgid "The credit card number is valid if the sum ends with `0`." +#: src/modules/visibility.md:5 +msgid "Module items are private by default (hides implementation details)." +msgstr "" + +#: src/modules/visibility.md:6 +msgid "Parent and sibling items are always visible." msgstr "" -#: src/exercises/day-2/luhn.md:19 +#: src/modules/visibility.md:7 msgid "" -"Copy the following code to and implement the " -"function:" +"In other words, if an item is visible in module `foo`, it's visible in all " +"the descendants of `foo`." msgstr "" -#: src/exercises/day-2/luhn.md:23 +#: src/modules/visibility.md:10 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" +"```rust,editable\n" +"mod outer {\n" +" fn private() {\n" +" println!(\"outer::private\");\n" +" }\n" "\n" -"#[test]\n" -"fn test_non_digit_cc_number() {\n" -" assert!(!luhn(\"foo\"));\n" -"}\n" +" pub fn public() {\n" +" println!(\"outer::public\");\n" +" }\n" "\n" -"#[test]\n" -"fn test_empty_cc_number() {\n" -" assert!(!luhn(\"\"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -"}\n" +" mod inner {\n" +" fn private() {\n" +" println!(\"outer::inner::private\");\n" +" }\n" "\n" -"#[test]\n" -"fn test_single_digit_cc_number() {\n" -" assert!(!luhn(\"0\"));\n" +" pub fn public() {\n" +" println!(\"outer::inner::public\");\n" +" super::private();\n" +" }\n" +" }\n" "}\n" "\n" -"#[test]\n" -"fn test_two_digit_cc_number() {\n" -" assert!(luhn(\" 0 0 \"));\n" +"fn main() {\n" +" outer::public();\n" "}\n" +"```" +msgstr "" + +#: src/modules/visibility.md:39 +msgid "Use the `pub` keyword to make modules public." +msgstr "" + +#: src/modules/visibility.md:41 +msgid "" +"Additionally, there are advanced `pub(...)` specifiers to restrict the scope " +"of public visibility." +msgstr "" + +#: src/modules/visibility.md:43 +msgid "" +"See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-" +"privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)." +msgstr "" + +#: src/modules/visibility.md:44 +msgid "Configuring `pub(crate)` visibility is a common pattern." +msgstr "" + +#: src/modules/visibility.md:45 +msgid "Less commonly, you can give visibility to a specific path." +msgstr "" + +#: src/modules/visibility.md:46 +msgid "" +"In any case, visibility must be granted to an ancestor module (and all of " +"its descendants)." +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." +msgstr "" + +#: src/modules/paths.md:13 +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:" +msgstr "" + +#: src/modules/paths.md:16 +msgid "" +"```rust,editable\n" +"use std::collections::HashSet;\n" +"use std::mem::transmute;\n" +"```" +msgstr "" + +#: src/modules/filesystem.md:3 +msgid "" +"Omitting the module content will tell Rust to look for it in another file:" +msgstr "" + +#: src/modules/filesystem.md:5 +msgid "" +"```rust,editable,compile_fail\n" +"mod garden;\n" +"```" +msgstr "" + +#: src/modules/filesystem.md:9 +msgid "" +"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/modules/filesystem.md:12 +msgid "The `crate` root is in:" +msgstr "" + +#: src/modules/filesystem.md:14 +msgid "`src/lib.rs` (for a library crate)" +msgstr "" + +#: src/modules/filesystem.md:15 +msgid "`src/main.rs` (for a binary crate)" +msgstr "" + +#: src/modules/filesystem.md:17 +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." +msgstr "" + +#: src/modules/filesystem.md:20 +msgid "" +"```rust,editable,compile_fail\n" +"//! This module implements the garden, including a highly performant " +"germination\n" +"//! implementation.\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" +"// Re-export types from this module.\n" +"pub use seeds::SeedPacket;\n" +"pub use garden::Garden;\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" +"/// Sow the given seed packets.\n" +"pub fn sow(seeds: Vec) { todo!() }\n" "\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" +"/// Harvest the produce in the garden that is ready.\n" +"pub fn harvest(garden: &mut Garden) { todo!() }\n" +"```" +msgstr "" + +#: src/modules/filesystem.md:37 +msgid "" +"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/modules/filesystem.md:39 +msgid "" +"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 "" + +#: src/modules/filesystem.md:42 +msgid "Deeper nesting can use folders, even if the main module is a file:" +msgstr "" + +#: src/modules/filesystem.md:44 +msgid "" +"```ignore\n" +"src/\n" +"├── main.rs\n" +"├── top_module.rs\n" +"└── top_module/\n" +" └── sub_module.rs\n" "```" msgstr "" +#: src/modules/filesystem.md:52 +msgid "" +"The place rust will look for modules can be changed with a compiler " +"directive:" +msgstr "" + +#: src/modules/filesystem.md:54 +msgid "" +"```rust,ignore\n" +"#[path = \"some/path.rs\"]\n" +"mod some_module;\n" +"```" +msgstr "" + +#: src/modules/filesystem.md:59 +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/exercises/day-2/strings-iterators.md:3 msgid "" "In this exercise, you are implementing a routing component of a web server. " @@ -8224,8 +8426,8 @@ msgstr "" #: src/generics.md:3 msgid "" -"Rust support generics, which lets you abstract an algorithm (such as " -"sorting) over the types used in the algorithm." +"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 "" #: src/generics/data-types.md:3 @@ -8534,10 +8736,16 @@ msgid "" msgstr "" #: src/traits/deriving-traits.md:3 -msgid "You can let the compiler derive a number of traits:" +msgid "" +"Rust derive macros work by automatically generating code that implements the " +"specified traits for a data structure." msgstr "" #: src/traits/deriving-traits.md:5 +msgid "You can let the compiler derive a number of traits as follows:" +msgstr "" + +#: src/traits/deriving-traits.md:7 msgid "" "```rust,editable\n" "#[derive(Debug, Clone, PartialEq, Eq, Default)]\n" @@ -8564,9 +8772,9 @@ msgstr "" 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" +" fn equals(&self, other: &Self) -> bool;\n" +" fn not_equals(&self, other: &Self) -> bool {\n" +" !self.equals(other)\n" " }\n" "}\n" "\n" @@ -8574,7 +8782,7 @@ msgid "" "struct Centimeter(i16);\n" "\n" "impl Equals for Centimeter {\n" -" fn equal(&self, other: &Centimeter) -> bool {\n" +" fn equals(&self, other: &Centimeter) -> bool {\n" " self.0 == other.0\n" " }\n" "}\n" @@ -8582,8 +8790,8 @@ msgid "" "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" +" println!(\"{a:?} equals {b:?}: {}\", a.equals(&b));\n" +" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equals(&b));\n" "}\n" "```" msgstr "" @@ -8596,38 +8804,38 @@ msgid "" msgstr "" #: src/traits/default-methods.md:35 -msgid "Move method `not_equal` to a new trait `NotEqual`." +msgid "Move method `not_equals` to a new trait `NotEquals`." msgstr "" #: src/traits/default-methods.md:37 -msgid "Make `NotEqual` a super trait for `Equal`." +msgid "Make `Equals` a super trait for `NotEquals`." msgstr "" #: src/traits/default-methods.md:38 msgid "" "```rust,editable,compile_fail\n" -"trait NotEqual: Equals {\n" -" fn not_equal(&self, other: &Self) -> bool {\n" -" !self.equal(other)\n" +"trait NotEquals: Equals {\n" +" fn not_equals(&self, other: &Self) -> bool {\n" +" !self.equals(other)\n" " }\n" "}\n" "```" msgstr "" #: src/traits/default-methods.md:46 -msgid "Provide a blanket implementation of `NotEqual` for `Equal`." +msgid "Provide a blanket implementation of `NotEquals` for `Equals`." msgstr "" #: src/traits/default-methods.md:47 msgid "" "```rust,editable,compile_fail\n" -"trait NotEqual {\n" -" fn not_equal(&self, other: &Self) -> bool;\n" +"trait NotEquals {\n" +" fn not_equals(&self, other: &Self) -> bool;\n" "}\n" "\n" -"impl NotEqual for T where T: Equals {\n" -" fn not_equal(&self, other: &Self) -> bool {\n" -" !self.equal(other)\n" +"impl NotEquals for T where T: Equals {\n" +" fn not_equals(&self, other: &Self) -> bool {\n" +" !self.equals(other)\n" " }\n" "}\n" "```" @@ -8635,8 +8843,8 @@ msgstr "" #: src/traits/default-methods.md:58 msgid "" -"With the blanket implementation, you no longer need `NotEqual` as a super " -"trait for `Equal`." +"With the blanket implementation, you no longer need `Equals` as a super " +"trait for `NotEqual`." msgstr "" #: src/traits/trait-bounds.md:3 @@ -9089,7 +9297,7 @@ msgstr "" #: src/traits/default.md:3 msgid "" "[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " -"provides a default implementation of a trait." +"produces a default value for a type." msgstr "" #: src/traits/default.md:5 @@ -9112,12 +9320,12 @@ msgid "" "}\n" "\n" "fn main() {\n" -" let default_struct: Derived = Default::default();\n" +" let default_struct = Derived::default();\n" " println!(\"{default_struct:#?}\");\n" "\n" " let almost_default_struct = Derived {\n" " y: \"Y is set!\".into(),\n" -" ..Default::default()\n" +" ..Derived::default()\n" " };\n" " println!(\"{almost_default_struct:#?}\");\n" "\n" @@ -9135,7 +9343,7 @@ msgstr "" #: src/traits/default.md:41 msgid "" -"Derived implementation will produce an instance where all fields are set to " +"A derived implementation will produce a value where all fields are set to " "their default values." msgstr "" @@ -9159,6 +9367,13 @@ msgid "" "provides convenience methods that use it." msgstr "" +#: 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/operators.md:1 msgid "`Add`, `Mul`, ..." msgstr "" @@ -9204,13 +9419,22 @@ msgid "" msgstr "" #: src/traits/operators.md:33 -msgid "Why is `Output` an associated type? Could it be made a type parameter?" +msgid "" +"Why is `Output` an associated type? Could it be made a type parameter of the " +"method?" 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." +"Short answer: Function type parameters are controlled by the caller, but " +"associated types (like `Output`) are controlled by the implementor of a " +"trait." +msgstr "" + +#: src/traits/operators.md:37 +msgid "" +"You could implement `Add` for two different types, e.g. `impl Add<(i32, " +"i32)> for Point` would add a tuple to a `Point`." msgstr "" #: src/traits/closures.md:1 @@ -9235,41 +9459,73 @@ msgid "" "\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" +" 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/closures.md:25 +#: src/traits/closures.md:34 msgid "" -"If you have an `FnOnce`, you may only call it once. It might consume " -"captured values." +"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 "" -#: src/traits/closures.md:27 +#: src/traits/closures.md:37 msgid "" -"An `FnMut` might mutate captured values, so you can call it multiple times " -"but not concurrently." +"An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it " +"multiple times, but not concurrently." msgstr "" -#: src/traits/closures.md:29 +#: src/traits/closures.md:40 msgid "" -"An `Fn` neither consumes nor mutates captured values, or perhaps captures " -"nothing at all, so it can be called multiple times concurrently." +"If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It " +"might consume captured values." msgstr "" -#: src/traits/closures.md:32 +#: src/traits/closures.md:43 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 "" -#: src/traits/closures.md:36 -msgid "`move` closures only implement `FnOnce`." +#: src/traits/closures.md:47 +msgid "" +"The compiler also infers `Copy` (e.g. for `add_3`) and `Clone` (e.g. " +"`multiply_sum`), depending on what the closure captures." +msgstr "" + +#: src/traits/closures.md:50 +msgid "" +"By default, closures will capture by reference if they can. The `move` " +"keyword makes them capture by value." +msgstr "" + +#: src/traits/closures.md:52 +msgid "" +"```rust,editable\n" +"fn make_greeter(prefix: String) -> impl Fn(&str) {\n" +" return move |name| println!(\"{} {}\", prefix, name)\n" +"}\n" +"\n" +"fn main() {\n" +" let hi = make_greeter(\"Hi\".to_string());\n" +" hi(\"there\");\n" +"}\n" +"```" msgstr "" #: src/exercises/day-3/morning.md:1 @@ -9277,7 +9533,13 @@ msgid "Day 3: Morning Exercises" msgstr "" #: src/exercises/day-3/morning.md:3 -msgid "We will design a classical GUI library traits and trait objects." +msgid "We will design a classical GUI library using traits and trait objects." +msgstr "" + +#: src/exercises/day-3/morning.md:5 +msgid "" +"We will also look at enum dispatch with an exercise involving points and " +"polygons." msgstr "" #: src/exercises/day-3/simple-gui.md:3 @@ -9486,20 +9748,165 @@ msgid "" "```" msgstr "" -#: src/error-handling.md:3 -msgid "Error handling in Rust is done using explicit control flow:" -msgstr "" - -#: 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." +#: src/exercises/day-3/points-polygons.md:1 +msgid "Polygon Struct" msgstr "" -#: src/error-handling/panics.md:3 -msgid "Rust will trigger a panic if a fatal error happens at runtime:" +#: src/exercises/day-3/points-polygons.md:3 +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:" +msgstr "" + +#: src/exercises/day-3/points-polygons.md:7 +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-3/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 "" + +#: src/exercises/day-3/points-polygons.md:120 +msgid "Other interesting parts of the exercise:" +msgstr "" + +#: src/exercises/day-3/points-polygons.md:122 +msgid "" +"Derive a `Copy` trait for some structs, as in tests the methods sometimes " +"don't borrow their arguments." +msgstr "" + +#: src/exercises/day-3/points-polygons.md:123 +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 "" + +#: src/error-handling.md:3 +msgid "Error handling in Rust is done using explicit control flow:" +msgstr "" + +#: 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 "" + +#: src/error-handling/panics.md:3 +msgid "Rust will trigger a panic if a fatal error happens at runtime:" msgstr "" #: src/error-handling/panics.md:5 @@ -9540,25 +9947,27 @@ msgid "" "```rust,editable\n" "use std::panic;\n" "\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" +"fn main() {\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/error-handling/panic-unwind.md:19 +#: 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:21 +#: src/error-handling/panic-unwind.md:23 msgid "This does not work if `panic = 'abort'` is set in your `Cargo.toml`." msgstr "" @@ -9575,11 +9984,11 @@ msgstr "" #: src/error-handling/result.md:6 msgid "" "```rust,editable\n" -"use std::fs::File;\n" +"use std::fs;\n" "use std::io::Read;\n" "\n" "fn main() {\n" -" let file = File::open(\"diary.txt\");\n" +" let file = fs::File::open(\"diary.txt\");\n" " match file {\n" " Ok(mut file) => {\n" " let mut contents = String::new();\n" @@ -9641,14 +10050,14 @@ msgid "" msgstr "" #: src/error-handling/try-operator.md:19 -msgid "We can use this to simplify our error handing code:" +msgid "We can use this to simplify our error handling code:" msgstr "" #: src/error-handling/try-operator.md:21 msgid "" "```rust,editable\n" -"use std::fs;\n" -"use std::io::{self, Read};\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" @@ -9684,6 +10093,24 @@ msgid "" "file, file with username." msgstr "" +#: src/error-handling/try-operator.md:52 +msgid "" +"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 "" + +#: src/error-handling/try-operator.md:57 +msgid "" +"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 "" + #: src/error-handling/converting-error-types.md:3 msgid "" "The effective expansion of `?` is a little more complicated than previously " @@ -9768,11 +10195,17 @@ msgstr "" #: src/error-handling/converting-error-types-example.md:55 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." +"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/error-handling/converting-error-types-example.md:57 +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 "" #: src/error-handling/deriving-error-enums.md:3 @@ -9797,7 +10230,7 @@ msgid "" "}\n" "\n" "fn read_username(path: &str) -> Result {\n" -" let mut username = String::with_capacity(100);\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" @@ -9836,7 +10269,7 @@ msgstr "" #: src/error-handling/dynamic-errors.md:6 msgid "" "```rust,editable,compile_fail\n" -"use std::fs::{self, File};\n" +"use std::fs;\n" "use std::io::Read;\n" "use thiserror::Error;\n" "use std::error::Error;\n" @@ -9846,8 +10279,8 @@ msgid "" "struct EmptyUsernameError(String);\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" +" 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" @@ -10497,7 +10930,19 @@ msgstr "" msgid "Let us build a safe wrapper for reading directory content!" msgstr "" -#: src/exercises/day-3/afternoon.md:7 +#: src/exercises/day-3/afternoon.md:5 +msgid "" +"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/exercises/day-3/afternoon.md:8 +msgid "" +"To get started, follow the [running locally](../../cargo/running-locally.md) " +"instructions." +msgstr "" + +#: src/exercises/day-3/afternoon.md:14 msgid "" "After looking at the exercise, you can look at the [solution](solutions-" "afternoon.md) provided." @@ -10642,7 +11087,7 @@ msgid "" "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" @@ -10652,23 +11097,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" @@ -10678,7 +11125,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" @@ -10751,9 +11213,9 @@ msgstr "" #: src/android/setup.md:6 msgid "" "```shell\n" -"$ source build/envsetup.sh\n" -"$ lunch aosp_cf_x86_64_phone-userdebug\n" -"$ acloud create\n" +"source build/envsetup.sh\n" +"lunch aosp_cf_x86_64_phone-userdebug\n" +"acloud create\n" "```" msgstr "" @@ -10899,9 +11361,15 @@ 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" +"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 "" + +#: src/android/build-rules/binary.md:35 +msgid "" +"```text\n" "Hello from Rust!\n" "```" msgstr "" @@ -10989,10 +11457,16 @@ msgstr "" #: src/android/build-rules/library.md:61 msgid "" "```shell\n" -"$ m hello_rust_with_dep\n" -"$ adb push \"$ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/" +"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" +"adb shell /data/local/tmp/hello_rust_with_dep\n" +"```" +msgstr "" + +#: src/android/build-rules/library.md:67 +msgid "" +"```text\n" "Hello Bob, it is very\n" "nice to meet you!\n" "```" @@ -11183,10 +11657,10 @@ 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/" +"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" +"adb shell /data/local/tmp/birthday_server\n" "```" msgstr "" @@ -11197,19 +11671,31 @@ msgstr "" #: src/android/aidl/deploy.md:13 msgid "" "```shell\n" -"$ adb shell service check birthdayservice\n" +"adb shell service check birthdayservice\n" +"```" +msgstr "" + +#: src/android/aidl/deploy.md:17 +msgid "" +"```text\n" "Service birthdayservice: found\n" "```" msgstr "" -#: src/android/aidl/deploy.md:18 +#: src/android/aidl/deploy.md:21 msgid "You can also call the service with `service call`:" msgstr "" -#: src/android/aidl/deploy.md:20 +#: src/android/aidl/deploy.md:23 msgid "" "```shell\n" -"$ $ adb shell service call birthdayservice 1 s16 Bob i32 24\n" +"adb shell service call birthdayservice 1 s16 Bob i32 24\n" +"```" +msgstr "" + +#: src/android/aidl/deploy.md:27 +msgid "" +"```text\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" @@ -11297,10 +11783,16 @@ msgstr "" #: src/android/aidl/client.md:56 msgid "" "```shell\n" -"$ m birthday_client\n" -"$ adb push \"$ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/" +"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" +"adb shell /data/local/tmp/birthday_client Charlie 60\n" +"```" +msgstr "" + +#: src/android/aidl/client.md:62 +msgid "" +"```text\n" "Happy Birthday Charlie, congratulations with the 60 years!\n" "```" msgstr "" @@ -11384,10 +11876,10 @@ msgstr "" #: src/android/logging.md:44 msgid "" "```shell\n" -"$ m hello_rust_logs\n" -"$ adb push \"$ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/" +"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" +"adb shell /data/local/tmp/hello_rust_logs\n" "```" msgstr "" @@ -11398,7 +11890,13 @@ msgstr "" #: src/android/logging.md:52 msgid "" "```shell\n" -"$ adb logcat -s rust\n" +"adb logcat -s rust\n" +"```" +msgstr "" + +#: src/android/logging.md:56 +msgid "" +"```text\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" @@ -11618,10 +12116,10 @@ msgstr "" #: src/android/interoperability/with-c/bindgen.md:100 msgid "" "```shell\n" -"$ m print_birthday_card\n" -"$ adb push \"$ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/" +"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" +"adb shell /data/local/tmp/print_birthday_card\n" "```" msgstr "" @@ -11647,7 +12145,7 @@ msgstr "" #: src/android/interoperability/with-c/bindgen.md:122 msgid "" "```shell\n" -"$ atest libbirthday_bindgen_test\n" +"atest libbirthday_bindgen_test\n" "```" msgstr "" @@ -11756,10 +12254,10 @@ msgstr "" #: src/android/interoperability/with-c/rust.md:75 msgid "" "```shell\n" -"$ m analyze_numbers\n" -"$ adb push \"$ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/" +"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" +"adb shell /data/local/tmp/analyze_numbers\n" "```" msgstr "" @@ -11786,6 +12284,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 "" @@ -11891,9 +12425,9 @@ 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" +"m helloworld_jni\n" +"adb sync # requires adb root && adb remount\n" +"adb shell /system/bin/helloworld_jni\n" "```" msgstr "" @@ -13137,31 +13671,179 @@ msgid "" "hardware, but is designed purely for virtual machines." msgstr "" -#: src/bare-metal/aps/inline-assembly.md:1 -msgid "Inline assembly" +#: 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/inline-assembly.md:3 +#: src/bare-metal/aps/entry-point.md:5 msgid "" -"Sometimes we need to use assembly to do things that aren't possible with " -"Rust code. For example, to make an " +"```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/inline-assembly.md:4 -msgid "HVC" +#: 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/inline-assembly.md:4 -msgid " to tell the firmware to power off the system:" +#: 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/inline-assembly.md:6 +#: src/bare-metal/aps/entry-point.md:86 msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"use core::arch::asm;\n" +"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 cachable 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 "" + +#: src/bare-metal/aps/inline-assembly.md:3 +msgid "" +"Sometimes we need to use assembly to do things that aren't possible with " +"Rust code. For example, to make an " +msgstr "" + +#: src/bare-metal/aps/inline-assembly.md:4 +msgid "HVC" +msgstr "" + +#: src/bare-metal/aps/inline-assembly.md:4 +msgid " to tell the firmware to power off the system:" +msgstr "" + +#: src/bare-metal/aps/inline-assembly.md:6 +msgid "" +"```rust,editable,compile_fail\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use core::arch::asm;\n" "use core::panic::PanicInfo;\n" "\n" "mod exceptions;\n" @@ -13798,9 +14480,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" @@ -13944,6 +14626,98 @@ msgid "" "examples`." msgstr "" +#: src/bare-metal/aps/exceptions.md:3 +msgid "" +"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/aps/exceptions.md:8 +msgid "" +"```rust,editable,compile_fail\n" +"use log::error;\n" +"use smccc::psci::system_off;\n" +"use smccc::Hvc;\n" +"\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" +"#[no_mangle]\n" +"extern \"C\" fn irq_current(_elr: u64, _spsr: u64) {\n" +" error!(\"irq_current\");\n" +" system_off::().unwrap();\n" +"}\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" +"#[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 "" @@ -13972,18 +14746,45 @@ msgid "" "exception handling, page tables" msgstr "" -#: src/bare-metal/aps/other-projects.md:9 -msgid "Not all very well written, so beware." +#: 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:10 +#: 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:11 +#: 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 cachable 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 " @@ -15531,23 +16332,26 @@ 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 "" -#: 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:" msgstr "" -#: src/concurrency/scoped-threads.md:19 +#: src/concurrency/scoped-threads.md:22 msgid "" "```rust,editable\n" "use std::thread;\n" @@ -15564,13 +16368,13 @@ msgid "" "```" msgstr "" -#: 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." msgstr "" -#: 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." @@ -15649,7 +16453,8 @@ msgid "" msgstr "" #: src/concurrency/channels/bounded.md:3 -msgid "Bounded and synchronous channels make `send` block the current thread:" +msgid "" +"With bounded (synchronous) channels, `send` can block the current thread:" msgstr "" #: src/concurrency/channels/bounded.md:5 @@ -15679,6 +16484,25 @@ msgid "" "```" msgstr "" +#: 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 "" @@ -15936,7 +16760,7 @@ msgstr "" #: src/concurrency/shared_state/arc.md:31 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 "" #: src/concurrency/shared_state/arc.md:33 @@ -16011,7 +16835,9 @@ msgid "" msgstr "" #: src/concurrency/shared_state/mutex.md:35 -msgid "`Mutex` implements both `Send` and `Sync` iff `T` implements `Send`." +msgid "" +"`Mutex` implements both `Send` and `Sync` iff (if and only if) `T` " +"implements `Send`." msgstr "" #: src/concurrency/shared_state/mutex.md:36 @@ -16188,7 +17014,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" @@ -16226,9 +17052,9 @@ 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" +"cargo new link-checker\n" +"cd link-checker\n" +"cargo add --features blocking,rustls-tls reqwest\n" "```" msgstr "" @@ -16247,7 +17073,7 @@ msgstr "" #: src/exercises/concurrency/link-checker.md:22 msgid "" "```shell\n" -"$ cargo add scraper\n" +"cargo add scraper\n" "```" msgstr "" @@ -16260,7 +17086,7 @@ msgstr "" #: src/exercises/concurrency/link-checker.md:29 msgid "" "```shell\n" -"$ cargo add thiserror\n" +"cargo add thiserror\n" "```" msgstr "" @@ -16299,8 +17125,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" @@ -16308,34 +17133,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" @@ -16343,24 +17191,24 @@ 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 +#: src/exercises/concurrency/link-checker.md:122 msgid "" "```shell\n" -"$ cargo run\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 " @@ -16562,19 +17410,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 @@ -16671,12 +17519,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` " @@ -16685,7 +17531,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" @@ -16729,25 +17575,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 `?`." @@ -16755,8 +17601,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 @@ -16784,7 +17629,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" @@ -16907,12 +17752,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" @@ -16962,7 +17808,7 @@ 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 " @@ -16970,24 +17816,28 @@ msgid "" "that take 500ms seconds." 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 @@ -17011,7 +17861,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 @@ -17318,6 +18172,155 @@ msgid "" "time and adding it to the Vec." msgstr "" +#: src/async/pitfalls/cancellation.md:3 +msgid "" +"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 "" + +#: src/async/pitfalls/cancellation.md:8 +msgid "" +"```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 "" + +#: src/async/pitfalls/cancellation.md:72 +msgid "" +"The compiler doesn't help with cancellation-safety. You need to read API " +"documentation and consider what state your `async fn` holds." +msgstr "" + +#: 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 makeing `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 +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 "" + +#: src/async/pitfalls/cancellation.md:107 +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 "" + +#: src/async/pitfalls/cancellation.md:110 +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 "" + #: src/exercises/concurrency/afternoon.md:3 msgid "" "To practice your Async Rust skills, we have again two exercises for you:" @@ -17500,7 +18503,7 @@ msgstr "" #: src/exercises/concurrency/chat-app.md:41 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 "" @@ -17534,7 +18537,7 @@ msgid "" msgstr "" #: src/exercises/concurrency/chat-app.md:59 -#: src/exercises/concurrency/solutions-afternoon.md:117 +#: src/exercises/concurrency/solutions-afternoon.md:123 msgid "`src/bin/server.rs`:" msgstr "" @@ -17581,7 +18584,7 @@ msgid "" msgstr "" #: src/exercises/concurrency/chat-app.md:102 -#: src/exercises/concurrency/solutions-afternoon.md:202 +#: src/exercises/concurrency/solutions-afternoon.md:208 msgid "`src/bin/client.rs`:" msgstr "" @@ -17601,7 +18604,7 @@ msgid "" " .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" @@ -17621,7 +18624,7 @@ msgstr "" #: src/exercises/concurrency/chat-app.md:130 msgid "" "```shell\n" -"$ cargo run --bin server\n" +"cargo run --bin server\n" "```" msgstr "" @@ -17632,7 +18635,7 @@ msgstr "" #: src/exercises/concurrency/chat-app.md:136 msgid "" "```shell\n" -"$ cargo run --bin client\n" +"cargo run --bin client\n" "```" msgstr "" @@ -17823,14 +18826,15 @@ 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 "" -#: src/credits.md:10 +#: src/credits.md:12 msgid "Rust by Example" msgstr "" -#: 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 " @@ -17838,22 +18842,22 @@ msgid "" "terms." msgstr "" -#: src/credits.md:17 +#: src/credits.md:19 msgid "Rust on Exercism" msgstr "" -#: 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/` " "directory for details, including the license terms." msgstr "" -#: src/credits.md:24 +#: src/credits.md:26 msgid "CXX" msgstr "" -#: 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/` " @@ -17993,488 +18997,45 @@ msgid "" "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 "" - -#: 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 "" - -#: 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" -"\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" +#: 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-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 "" -#: src/exercises/day-2/solutions-afternoon.md:7 +#: src/exercises/day-1/solutions-afternoon.md:7 msgid "" "```rust\n" "// Copyright 2022 Google LLC\n" @@ -18564,15 +19125,236 @@ msgid "" " assert!(!luhn(\"4539 3195 0343 6476\"));\n" " assert!(!luhn(\"8273 1232 7352 0569\"));\n" "}\n" -"// ANCHOR_END: unit-tests\n" +"// ANCHOR_END: unit-tests\n" +"```" +msgstr "" + +#: src/exercises/day-1/solutions-afternoon.md:97 +msgid "Pattern matching" +msgstr "" + +#: src/exercises/day-1/solutions-afternoon.md:99 +msgid "TBD." +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 "" + +#: 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" +"// 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() -> {}\", 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() -> {}\", " +"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-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 "" -#: src/exercises/day-2/solutions-afternoon.md:101 +#: src/exercises/day-2/solutions-afternoon.md:7 msgid "" "```rust\n" "// Copyright 2022 Google LLC\n" @@ -18592,23 +19374,30 @@ msgid "" "// 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" @@ -18726,111 +19515,358 @@ msgid "" " 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" +"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" +"// 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" +" }\n" +"\n" +" let inner_width = self.inner_width();\n" +"\n" +" // TODO: after learning about error handling, you can change\n" +" // draw_into to return Result<(), std::fmt::Error>. Then use\n" +" // the ?-operator here instead of .unwrap().\n" +" writeln!(buffer, \"+-{:- 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" +"\n" +" writeln!(buffer, \"+{:- usize {\n" +" // ANCHOR_END: Label-width\n" +" self.label\n" +" .lines()\n" +" .map(|line| line.chars().count())\n" +" .max()\n" +" .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." +"\")));\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" +"// ANCHOR_END: main\n" +"```" +msgstr "" + +#: src/exercises/day-3/solutions-morning.md:177 +msgid "([back to exercise](points-polygons.md))" +msgstr "" + +#: src/exercises/day-3/solutions-morning.md:179 +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" +" // 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" +"// 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" -" fn add_widget(&mut self, widget: Box) {\n" -" self.widgets.push(widget);\n" +"impl From for Shape {\n" +" fn from(circle: Circle) -> Self {\n" +" Shape::Circle(circle)\n" " }\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" +"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_END: setup\n" +"// ANCHOR: unit-tests\n" +"#[cfg(test)]\n" +"mod tests {\n" +" use super::*;\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" +" fn round_two_digits(x: f64) -> f64 {\n" +" (x * 100.0).round() / 100.0\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" -" }\n" -"\n" -" let inner_width = self.inner_width();\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" -" // TODO: after learning about error handling, you can change\n" -" // draw_into to return Result<(), std::fmt::Error>. Then use\n" -" // the ?-operator here instead of .unwrap().\n" -" writeln!(buffer, \"+-{:- usize {\n" -" // ANCHOR_END: Button-width\n" -" self.label.width() + 8 // add a bit of padding\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" -" // 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" +" #[test]\n" +" fn test_polygon_left_most_point() {\n" +" let p1 = Point::new(12, 13);\n" +" let p2 = Point::new(16, 16);\n" "\n" -" writeln!(buffer, \"+{:- usize {\n" -" // ANCHOR_END: Label-width\n" -" self.label\n" -" .lines()\n" -" .map(|line| line.chars().count())\n" -" .max()\n" -" .unwrap_or(0)\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" -" // 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" +" #[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" +"// ANCHOR_END: unit-tests\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." -"\")));\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" -"// ANCHOR_END: main\n" +"fn main() {}\n" "```" msgstr "" @@ -18863,7 +19899,7 @@ msgid "" "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" @@ -18873,23 +19909,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" @@ -18899,7 +19937,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" @@ -19448,8 +20501,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" @@ -19560,8 +20613,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" @@ -19593,6 +20646,209 @@ msgid "" "```" msgstr "" +#: src/exercises/concurrency/solutions-morning.md:104 +msgid "Link Checker" +msgstr "" + +#: src/exercises/concurrency/solutions-morning.md:106 +msgid "([back to exercise](link-checker.md))" +msgstr "" + +#: src/exercises/concurrency/solutions-morning.md:108 +msgid "" +"```rust,compile_fail\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" +"use std::{sync::Arc, sync::Mutex, sync::mpsc, thread};\n" +"\n" +"// ANCHOR: setup\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" +"// ANCHOR_END: setup\n" +"\n" +"// ANCHOR: visit_page\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" +"// ANCHOR_END: visit_page\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 "" @@ -19681,14 +20937,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" @@ -19715,11 +20976,11 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:115 +#: src/exercises/concurrency/solutions-afternoon.md:121 msgid "([back to exercise](chat-app.md))" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:119 +#: src/exercises/concurrency/solutions-afternoon.md:125 msgid "" "```rust,compile_fail\n" "// Copyright 2023 Google LLC\n" @@ -19806,7 +21067,7 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:204 +#: src/exercises/concurrency/solutions-afternoon.md:210 msgid "" "```rust,compile_fail\n" "// Copyright 2023 Google LLC\n" @@ -19837,12 +21098,11 @@ msgid "" " .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" @@ -19852,10 +21112,10 @@ msgid "" " 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" @@ -19866,3 +21126,22 @@ msgid "" "}\n" "```" msgstr "" + +#~ msgid "Day 1: Basic Rust, ownership and the borrow checker." +#~ msgstr "День 1: Базовий Rust, володіння та аналізатор запозичень" + +#~ 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 "" +#~ "Ви можете дотримуватися інструкцій для встановлення Cargo, компілятора " +#~ "Rust і інших стандартних інструментів екосистеми за допомогою інструмента " +#~ "[rustup](https://rust-analyzer.github.io/), який підтримується Rust " +#~ "Foundation." + +#~ msgid "Package Managers" +#~ msgstr "Менеджери пакетів"