From 629a5c322c283ba28b480563be7efefd046cbb71 Mon Sep 17 00:00:00 2001 From: Amin Sharifi Date: Wed, 25 Oct 2023 17:41:54 +0330 Subject: [PATCH] fa: Adding Persian translations for sidebar items (#1427) Part of #671 **Summary of changes:** - Persian version of book messages synced to original book - Translate sidebar menu * I just translate and double check until `# src/SUMMARY.md:333` part* --- po/fa.po | 12027 ++++++++++++++++++++++++----------------------------- 1 file changed, 5480 insertions(+), 6547 deletions(-) diff --git a/po/fa.po b/po/fa.po index 1d21c6e60a4..a7a3bd78124 100644 --- a/po/fa.po +++ b/po/fa.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Comprehensive Rust 🦀\n" -"POT-Creation-Date: 2023-08-23\n" +"POT-Creation-Date: 2023-10-25T14:05:24+03:30\n" "PO-Revision-Date: 2023-08-08 21:41+0330\n" "Last-Translator: danny \n" "Language-Team: Persian\n" @@ -14,7 +14,7 @@ msgstr "" #: src/SUMMARY.md:4 src/index.md:1 msgid "Welcome to Comprehensive Rust 🦀" -msgstr "Welcome to Comprehensive Rust 🦀" +msgstr "به Comprehensive Rust خوش آمدید 🦀" #: src/SUMMARY.md:5 src/running-the-course.md:1 msgid "Running the Course" @@ -52,8 +52,8 @@ msgstr "اجرای cargo روی ماشین local" msgid "Day 1: Morning" msgstr "روز اول: صبح" -#: src/SUMMARY.md:19 src/SUMMARY.md:76 src/SUMMARY.md:129 src/SUMMARY.md:186 -#: src/SUMMARY.md:212 src/SUMMARY.md:262 +#: src/SUMMARY.md:19 src/SUMMARY.md:80 src/SUMMARY.md:135 src/SUMMARY.md:193 +#: src/SUMMARY.md:219 src/SUMMARY.md:269 msgid "Welcome" msgstr "خوش آمدید" @@ -63,7 +63,7 @@ msgstr "زبان Rust چیست؟" #: src/SUMMARY.md:21 src/hello-world.md:1 msgid "Hello World!" -msgstr "Hello World!" +msgstr "سلام دنیا" #: src/SUMMARY.md:22 src/hello-world/small-example.md:1 msgid "Small Example" @@ -73,66 +73,71 @@ msgstr "یک مثال ساده" msgid "Why Rust?" msgstr "چرا Rust؟" -#: src/SUMMARY.md:24 src/why-rust/compile-time.md:1 +#: src/SUMMARY.md:24 src/why-rust/an-example-in-c.md:1 src/credits.md:32 +#, fuzzy +msgid "An Example in C" +msgstr "یک مثال در زبان C" + +#: src/SUMMARY.md:25 src/why-rust/compile-time.md:1 msgid "Compile Time Guarantees" -msgstr "Compile Time Guarantees" +msgstr "ضمانت‌های زمان کامپایل" -#: src/SUMMARY.md:25 src/why-rust/runtime.md:1 +#: src/SUMMARY.md:26 src/why-rust/runtime.md:1 msgid "Runtime Guarantees" -msgstr "Runtime Guarantees" +msgstr "ضمانت‌های زمان اجرا" -#: src/SUMMARY.md:26 src/why-rust/modern.md:1 +#: src/SUMMARY.md:27 src/why-rust/modern.md:1 msgid "Modern Features" msgstr "ویژگی‌های جدید" -#: src/SUMMARY.md:27 src/basic-syntax.md:1 +#: src/SUMMARY.md:28 src/basic-syntax.md:1 msgid "Basic Syntax" msgstr "سینتکس‌های مقدماتی" -#: src/SUMMARY.md:28 src/basic-syntax/scalar-types.md:1 +#: src/SUMMARY.md:29 src/basic-syntax/scalar-types.md:1 msgid "Scalar Types" -msgstr "Scalar Types" +msgstr "تایپ‌های عددی" -#: src/SUMMARY.md:29 src/basic-syntax/compound-types.md:1 +#: src/SUMMARY.md:30 src/basic-syntax/compound-types.md:1 msgid "Compound Types" -msgstr "Compound Types" +msgstr "تایپ‌های مرکب" -#: src/SUMMARY.md:30 src/basic-syntax/references.md:1 +#: src/SUMMARY.md:31 src/basic-syntax/references.md:1 msgid "References" msgstr "مراجع" -#: src/SUMMARY.md:31 src/basic-syntax/references-dangling.md:1 +#: src/SUMMARY.md:32 src/basic-syntax/references-dangling.md:1 msgid "Dangling References" msgstr "ارجاعات تعلیق شده" -#: src/SUMMARY.md:32 src/basic-syntax/slices.md:1 +#: src/SUMMARY.md:33 src/basic-syntax/slices.md:1 msgid "Slices" -msgstr "Slices" +msgstr "برش ها" -#: src/SUMMARY.md:33 +#: src/SUMMARY.md:34 msgid "String vs str" msgstr "String در مقابل str" -#: src/SUMMARY.md:34 src/basic-syntax/functions.md:1 +#: src/SUMMARY.md:35 src/basic-syntax/functions.md:1 msgid "Functions" msgstr "توابع" -#: src/SUMMARY.md:35 src/basic-syntax/rustdoc.md:1 +#: src/SUMMARY.md:36 src/basic-syntax/rustdoc.md:1 msgid "Rustdoc" -msgstr "Rustdoc" +msgstr "داکیومنت نویسی" -#: src/SUMMARY.md:36 src/SUMMARY.md:83 src/basic-syntax/methods.md:1 +#: src/SUMMARY.md:37 src/SUMMARY.md:103 src/basic-syntax/methods.md:1 #: src/methods.md:1 msgid "Methods" msgstr "متدها" -#: src/SUMMARY.md:37 +#: src/SUMMARY.md:38 msgid "Overloading" msgstr "سربارگذاری" -#: src/SUMMARY.md:38 src/SUMMARY.md:67 src/SUMMARY.md:91 src/SUMMARY.md:120 -#: src/SUMMARY.md:149 src/SUMMARY.md:178 src/SUMMARY.md:205 src/SUMMARY.md:226 -#: src/SUMMARY.md:254 src/SUMMARY.md:276 src/SUMMARY.md:297 +#: src/SUMMARY.md:39 src/SUMMARY.md:72 src/SUMMARY.md:106 src/SUMMARY.md:126 +#: src/SUMMARY.md:155 src/SUMMARY.md:185 src/SUMMARY.md:212 src/SUMMARY.md:233 +#: src/SUMMARY.md:261 src/SUMMARY.md:283 src/SUMMARY.md:304 #: 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 @@ -140,921 +145,905 @@ msgstr "سربارگذاری" msgid "Exercises" msgstr "تمرین‌ها" -#: src/SUMMARY.md:39 src/exercises/day-1/implicit-conversions.md:1 +#: src/SUMMARY.md:40 src/exercises/day-1/implicit-conversions.md:1 msgid "Implicit Conversions" -msgstr "تبدیل های غیر صریح" +msgstr "تبدیل‌های غیر صریح" -#: src/SUMMARY.md:40 +#: src/SUMMARY.md:41 msgid "Arrays and for Loops" msgstr "آرایه ها و حلقه های for" -#: src/SUMMARY.md:42 +#: src/SUMMARY.md:43 msgid "Day 1: Afternoon" msgstr "روز ۱: بعد از ظهر" -#: src/SUMMARY.md:44 src/basic-syntax/variables.md:1 +#: src/SUMMARY.md:45 src/SUMMARY.md:296 src/control-flow.md:1 +msgid "Control Flow" +msgstr "کنترل جریان" + +#: src/SUMMARY.md:46 src/control-flow/blocks.md:1 +msgid "Blocks" +msgstr "بلوک‌ها" + +#: src/SUMMARY.md:47 +msgid "if expressions" +msgstr "عبارت if" + +#: src/SUMMARY.md:48 +msgid "for expressions" +msgstr "عبارت for" + +#: src/SUMMARY.md:49 +msgid "while expressions" +msgstr "عبارت حلقه while" + +#: src/SUMMARY.md:50 +msgid "break & continue" +msgstr "توقف و ادامه دادن (break & continue)" + +#: src/SUMMARY.md:51 +msgid "loop expressions" +msgstr "عبارت loop" + +#: src/SUMMARY.md:53 src/basic-syntax/variables.md:1 msgid "Variables" msgstr "متغیرها" -#: src/SUMMARY.md:45 src/basic-syntax/type-inference.md:1 +#: src/SUMMARY.md:54 src/basic-syntax/type-inference.md:1 msgid "Type Inference" -msgstr "Type Inference" +msgstr "تعیین تایپ ضمنی" -#: src/SUMMARY.md:46 +#: src/SUMMARY.md:55 msgid "static & const" msgstr "نمادهای ایستا و ثابت" -#: src/SUMMARY.md:47 src/basic-syntax/scopes-shadowing.md:1 +#: src/SUMMARY.md:56 src/basic-syntax/scopes-shadowing.md:1 msgid "Scopes and Shadowing" msgstr "محدوده‌ها و سایه‌گذاری" -#: src/SUMMARY.md:48 src/memory-management.md:1 +#: src/SUMMARY.md:57 src/enums.md:1 +msgid "Enums" +msgstr "Enums" + +#: src/SUMMARY.md:58 src/enums/variant-payloads.md:1 +msgid "Variant Payloads" +msgstr "Variant Payloads" + +#: src/SUMMARY.md:59 src/enums/sizes.md:1 +msgid "Enum Sizes" +msgstr "اندازه ی Enum ها" + +#: src/SUMMARY.md:61 src/control-flow/novel.md:1 +#, fuzzy +msgid "Novel Control Flow" +msgstr "کنترل جریان پیشرفته" + +#: src/SUMMARY.md:62 +msgid "if let expressions" +msgstr "عبارت if let" + +#: src/SUMMARY.md:63 +msgid "while let expressions" +msgstr "عبارت while let" + +#: src/SUMMARY.md:64 +msgid "match expressions" +msgstr "عبارت تطبیق" + +#: src/SUMMARY.md:66 src/SUMMARY.md:74 src/pattern-matching.md:1 +msgid "Pattern Matching" +msgstr "تطبیق الگو" + +#: src/SUMMARY.md:67 src/pattern-matching/destructuring-enums.md:1 +msgid "Destructuring Enums" +msgstr "تخریب Enumها" + +#: src/SUMMARY.md:68 src/pattern-matching/destructuring-structs.md:1 +msgid "Destructuring Structs" +msgstr "تخریب ساختارها" + +#: src/SUMMARY.md:69 src/pattern-matching/destructuring-arrays.md:1 +msgid "Destructuring Arrays" +msgstr "تخریب آرایه‌ها" + +#: src/SUMMARY.md:70 src/pattern-matching/match-guards.md:1 +msgid "Match Guards" +msgstr "گاردهای تطبیق" + +#: src/SUMMARY.md:73 src/exercises/day-1/luhn.md:1 +#: src/exercises/day-1/solutions-afternoon.md:3 +msgid "Luhn Algorithm" +msgstr "الگوریتم Luhn" + +#: src/SUMMARY.md:76 +msgid "Day 2: Morning" +msgstr "روز دوم: صبح" + +#: src/SUMMARY.md:82 src/memory-management.md:1 msgid "Memory Management" msgstr "مدیریت حافظه" -#: src/SUMMARY.md:49 +#: src/SUMMARY.md:83 msgid "Stack vs Heap" -msgstr "Stack vs Heap" +msgstr "مقایسه پشته و انباشت" -#: src/SUMMARY.md:50 +#: src/SUMMARY.md:84 msgid "Stack Memory" -msgstr "حافظه Stack" +msgstr "حافظه پشته" -#: src/SUMMARY.md:51 src/memory-management/manual.md:1 +#: src/SUMMARY.md:85 src/memory-management/manual.md:1 msgid "Manual Memory Management" msgstr "مدیریت دستی حافظه" -#: src/SUMMARY.md:52 src/memory-management/scope-based.md:1 +#: src/SUMMARY.md:86 src/memory-management/scope-based.md:1 msgid "Scope-Based Memory Management" msgstr "مدیریت حافظه مبتنی بر دامنه" -#: src/SUMMARY.md:53 +#: src/SUMMARY.md:87 msgid "Garbage Collection" -msgstr "Garbage Collection" +msgstr "جمع آوری زباله (GC)" -#: src/SUMMARY.md:54 +#: src/SUMMARY.md:88 msgid "Rust Memory Management" -msgstr "مدیریت منابع Rust" - -#: src/SUMMARY.md:55 src/memory-management/comparison.md:1 -msgid "Comparison" -msgstr "مقایسه" +msgstr "مدیریت حافظه Rust" -#: src/SUMMARY.md:56 src/ownership.md:1 +#: src/SUMMARY.md:89 src/ownership.md:1 msgid "Ownership" msgstr "مالکیت" -#: src/SUMMARY.md:57 src/ownership/move-semantics.md:1 +#: src/SUMMARY.md:90 src/ownership/move-semantics.md:1 msgid "Move Semantics" -msgstr "مفاهیم جابجایی" +msgstr "مفاهیم جابه‌جایی" -#: src/SUMMARY.md:58 src/ownership/moved-strings-rust.md:1 +#: src/SUMMARY.md:91 src/ownership/moved-strings-rust.md:1 msgid "Moved Strings in Rust" msgstr "های جابجا شده در Rust String" -#: src/SUMMARY.md:59 +#: src/SUMMARY.md:92 msgid "Double Frees in Modern C++" msgstr "آزاد سازی مضاعف در مدرن سی++" -#: src/SUMMARY.md:60 src/ownership/moves-function-calls.md:1 +#: src/SUMMARY.md:93 src/ownership/moves-function-calls.md:1 msgid "Moves in Function Calls" -msgstr "حرکت‌ها در فراخوانی توابع" +msgstr "جابه‌جایی در صدا زدن توابع" -#: src/SUMMARY.md:61 src/ownership/copy-clone.md:1 +#: src/SUMMARY.md:94 src/ownership/copy-clone.md:1 msgid "Copying and Cloning" msgstr "کپی برداری و شبیه سازی" -#: src/SUMMARY.md:62 src/ownership/borrowing.md:1 +#: src/SUMMARY.md:95 src/ownership/borrowing.md:1 msgid "Borrowing" msgstr "قرض گرفتن" -#: src/SUMMARY.md:63 src/ownership/shared-unique-borrows.md:1 +#: src/SUMMARY.md:96 src/ownership/shared-unique-borrows.md:1 msgid "Shared and Unique Borrows" -msgstr "قرض گرفتن های مشترک و منحصر به فرد" +msgstr "قرض گرفتن‌های مشترک و منحصر به فرد" -#: src/SUMMARY.md:64 src/ownership/lifetimes.md:1 +#: src/SUMMARY.md:97 src/ownership/lifetimes.md:1 msgid "Lifetimes" msgstr "چرخه حیات" -#: src/SUMMARY.md:65 src/ownership/lifetimes-function-calls.md:1 +#: src/SUMMARY.md:98 src/ownership/lifetimes-function-calls.md:1 msgid "Lifetimes in Function Calls" -msgstr "طول عمر فراخوانی های تابع" +msgstr "طول عمر در فراخوانی‌ توابع" -#: src/SUMMARY.md:66 src/ownership/lifetimes-data-structures.md:1 +#: src/SUMMARY.md:99 src/ownership/lifetimes-data-structures.md:1 msgid "Lifetimes in Data Structures" msgstr "طول عمر در ساختمان داده" -#: src/SUMMARY.md:68 src/exercises/day-1/book-library.md:1 -msgid "Storing Books" -msgstr "ذخیره سازی کتاب" - -#: src/SUMMARY.md:69 src/exercises/day-1/iterators-and-ownership.md:1 -msgid "Iterators and Ownership" -msgstr "تکرار کننده ها و مالکیت" - -#: src/SUMMARY.md:72 -msgid "Day 2: Morning" -msgstr "روز دوم: صبح" - -#: src/SUMMARY.md:77 src/structs.md:1 +#: src/SUMMARY.md:100 src/structs.md:1 msgid "Structs" msgstr "ساختارها" -#: src/SUMMARY.md:78 src/structs/tuple-structs.md:1 +#: src/SUMMARY.md:101 src/structs/tuple-structs.md:1 msgid "Tuple Structs" -msgstr "Tuple Structs" +msgstr "ساختار‌های چند‌گانه (Tuple Structs)" -#: src/SUMMARY.md:79 src/structs/field-shorthand.md:1 +#: src/SUMMARY.md:102 src/structs/field-shorthand.md:1 msgid "Field Shorthand Syntax" msgstr "نحو اختصاری فیلد" -#: src/SUMMARY.md:80 src/enums.md:1 -msgid "Enums" -msgstr "Enums" - -#: src/SUMMARY.md:81 src/enums/variant-payloads.md:1 -msgid "Variant Payloads" -msgstr "Variant Payloads" - -#: src/SUMMARY.md:82 src/enums/sizes.md:1 -msgid "Enum Sizes" -msgstr "اندازه ی Enum ها" - -#: src/SUMMARY.md:84 src/methods/receiver.md:1 +#: src/SUMMARY.md:104 src/methods/receiver.md:1 msgid "Method Receiver" msgstr "متد دریافتی" -#: src/SUMMARY.md:85 src/SUMMARY.md:160 src/SUMMARY.md:275 +#: src/SUMMARY.md:105 src/SUMMARY.md:167 src/SUMMARY.md:282 #: src/methods/example.md:1 src/concurrency/shared_state/example.md:1 msgid "Example" msgstr "مثال" -#: src/SUMMARY.md:86 src/pattern-matching.md:1 -msgid "Pattern Matching" -msgstr "تطبیق الگو" - -#: src/SUMMARY.md:87 src/pattern-matching/destructuring-enums.md:1 -msgid "Destructuring Enums" -msgstr "تخریب Enumها" - -#: src/SUMMARY.md:88 src/pattern-matching/destructuring-structs.md:1 -msgid "Destructuring Structs" -msgstr "تخریب ساختارها" - -#: src/SUMMARY.md:89 src/pattern-matching/destructuring-arrays.md:1 -msgid "Destructuring Arrays" -msgstr "تخریب آرایه‌ها" - -#: src/SUMMARY.md:90 src/pattern-matching/match-guards.md:1 -msgid "Match Guards" -msgstr "گاردهای تطبیق" +#: src/SUMMARY.md:107 src/exercises/day-2/book-library.md:1 +msgid "Storing Books" +msgstr "ذخیره سازی کتاب" -#: src/SUMMARY.md:92 src/exercises/day-2/health-statistics.md:1 +#: src/SUMMARY.md:108 src/exercises/day-2/health-statistics.md:1 +#: src/exercises/day-2/solutions-morning.md:151 msgid "Health Statistics" msgstr "آمارهای سلامتی" -#: src/SUMMARY.md:93 src/exercises/day-2/solutions-morning.md:3 -msgid "Points and Polygons" -msgstr "نقطه‌ها و چند ضلعی‌ها" - -#: src/SUMMARY.md:95 +#: src/SUMMARY.md:110 msgid "Day 2: Afternoon" msgstr "روز دوم: عصر" -#: src/SUMMARY.md:97 src/SUMMARY.md:289 src/control-flow.md:1 -msgid "Control Flow" -msgstr "کنترل مسیر" - -#: src/SUMMARY.md:98 src/control-flow/blocks.md:1 -msgid "Blocks" -msgstr "بلوک‌ها" - -#: src/SUMMARY.md:99 -msgid "if expressions" -msgstr "عبارت if" - -#: src/SUMMARY.md:100 -msgid "if let expressions" -msgstr "عبارت if let" - -#: src/SUMMARY.md:101 -msgid "while expressions" -msgstr "عبارت حلقه while" - -#: src/SUMMARY.md:102 -msgid "while let expressions" -msgstr "عبارت while let" - -#: src/SUMMARY.md:103 -msgid "for expressions" -msgstr "عبارت for" - -#: src/SUMMARY.md:104 -msgid "loop expressions" -msgstr "عبارت loop" - -#: src/SUMMARY.md:105 -msgid "match expressions" -msgstr "عبارت تطبیق" - -#: src/SUMMARY.md:106 -msgid "break & continue" -msgstr "توقف و ادامه دادن (break & continue)" - -#: src/SUMMARY.md:107 src/std.md:1 +#: src/SUMMARY.md:112 src/std.md:1 msgid "Standard Library" msgstr "کتابخانه‌های استاندارد" -#: src/SUMMARY.md:108 +#: src/SUMMARY.md:113 msgid "Option and Result" msgstr "Option و Result" -#: src/SUMMARY.md:109 src/std/string.md:1 +#: src/SUMMARY.md:114 src/std/string.md:1 msgid "String" msgstr "String" -#: src/SUMMARY.md:110 +#: src/SUMMARY.md:115 msgid "Vec" msgstr "Vec" -#: src/SUMMARY.md:111 +#: src/SUMMARY.md:116 msgid "HashMap" msgstr "HashMap" -#: src/SUMMARY.md:112 +#: src/SUMMARY.md:117 msgid "Box" msgstr "Box" -#: src/SUMMARY.md:113 +#: src/SUMMARY.md:118 msgid "Recursive Data Types" -msgstr "انواع داده های بازگشتی" +msgstr "انواع داده‌های بازگشتی" -#: src/SUMMARY.md:114 src/std/box-niche.md:1 +#: src/SUMMARY.md:119 src/std/box-niche.md:1 msgid "Niche Optimization" msgstr "بهینه سازی Niche" -#: src/SUMMARY.md:115 +#: src/SUMMARY.md:120 msgid "Rc" msgstr "Rc" -#: src/SUMMARY.md:116 src/modules.md:1 +#: src/SUMMARY.md:121 +msgid "Cell/RefCell" +msgstr "" + +#: src/SUMMARY.md:122 src/modules.md:1 msgid "Modules" msgstr "ماژول‌ها" -#: src/SUMMARY.md:117 src/modules/visibility.md:1 +#: src/SUMMARY.md:123 src/modules/visibility.md:1 msgid "Visibility" msgstr "قابلیت دید" -#: src/SUMMARY.md:118 src/modules/paths.md:1 +#: src/SUMMARY.md:124 src/modules/paths.md:1 msgid "Paths" msgstr "مسیر" -#: src/SUMMARY.md:119 src/modules/filesystem.md:1 +#: src/SUMMARY.md:125 src/modules/filesystem.md:1 msgid "Filesystem Hierarchy" -msgstr "توالی فایل سیستم‌ها" +msgstr "سلسله‌ مراتب فایل‌سیستم" -#: src/SUMMARY.md:121 src/exercises/day-2/luhn.md:1 -#: src/exercises/day-2/solutions-afternoon.md:3 -msgid "Luhn Algorithm" -msgstr "الگوریتم Luhn" +#: src/SUMMARY.md:127 src/exercises/day-2/iterators-and-ownership.md:1 +msgid "Iterators and Ownership" +msgstr "تکرار کننده ها و مالکیت" -#: src/SUMMARY.md:122 src/exercises/day-2/strings-iterators.md:1 -#: src/exercises/day-2/solutions-afternoon.md:97 +#: src/SUMMARY.md:128 src/exercises/day-2/strings-iterators.md:1 +#: src/exercises/day-2/solutions-afternoon.md:3 msgid "Strings and Iterators" msgstr "رشته ها و تکرار کننده‌ها (Strings and Iterators)" -#: src/SUMMARY.md:125 +#: src/SUMMARY.md:131 msgid "Day 3: Morning" msgstr "روز سوم: صبح" -#: src/SUMMARY.md:130 src/generics.md:1 +#: src/SUMMARY.md:136 src/generics.md:1 msgid "Generics" -msgstr "Generics" +msgstr "جنریک‌ها" -#: src/SUMMARY.md:131 src/generics/data-types.md:1 +#: src/SUMMARY.md:137 src/generics/data-types.md:1 msgid "Generic Data Types" msgstr "انواع داده‌ها جنریک (Generic Data Types)" -#: src/SUMMARY.md:132 src/generics/methods.md:1 +#: src/SUMMARY.md:138 src/generics/methods.md:1 msgid "Generic Methods" msgstr "متدهای جنریک" -#: src/SUMMARY.md:133 src/generics/monomorphization.md:1 +#: src/SUMMARY.md:139 src/generics/monomorphization.md:1 msgid "Monomorphization" msgstr "تک شکلی" -#: src/SUMMARY.md:134 src/traits.md:1 +#: src/SUMMARY.md:140 src/traits.md:1 msgid "Traits" msgstr "صفت‌ها" -#: src/SUMMARY.md:135 src/traits/trait-objects.md:1 +#: src/SUMMARY.md:141 src/traits/trait-objects.md:1 msgid "Trait Objects" msgstr "آبجکت‌های موصوفی (Trait Objects)" -#: src/SUMMARY.md:136 src/traits/deriving-traits.md:1 +#: src/SUMMARY.md:142 src/traits/deriving-traits.md:1 msgid "Deriving Traits" msgstr "صفات استنتاجی (Deriving Traits)" -#: src/SUMMARY.md:137 src/traits/default-methods.md:1 +#: src/SUMMARY.md:143 src/traits/default-methods.md:1 msgid "Default Methods" msgstr "متدهای پیشفرض" -#: src/SUMMARY.md:138 src/traits/trait-bounds.md:1 +#: src/SUMMARY.md:144 src/traits/trait-bounds.md:1 msgid "Trait Bounds" msgstr "صفات مرزی (Trait Bounds)" -#: src/SUMMARY.md:139 +#: src/SUMMARY.md:145 msgid "impl Trait" msgstr "صفات impl (impl Trait)" -#: src/SUMMARY.md:140 src/traits/important-traits.md:1 +#: src/SUMMARY.md:146 src/traits/important-traits.md:1 msgid "Important Traits" msgstr "صفات مهم" -#: src/SUMMARY.md:141 +#: src/SUMMARY.md:147 msgid "Iterator" msgstr "تکرار کننده" -#: src/SUMMARY.md:142 src/traits/from-iterator.md:1 +#: src/SUMMARY.md:148 src/traits/from-iterator.md:1 msgid "FromIterator" msgstr "FromIterator" -#: src/SUMMARY.md:143 +#: src/SUMMARY.md:149 msgid "From and Into" msgstr "From و Into" -#: src/SUMMARY.md:144 +#: src/SUMMARY.md:150 msgid "Read and Write" msgstr "Read and Write" -#: src/SUMMARY.md:145 +#: src/SUMMARY.md:151 msgid "Drop" msgstr "رها کردن" -#: src/SUMMARY.md:146 +#: src/SUMMARY.md:152 msgid "Default" msgstr "پیش‌فرض" -#: src/SUMMARY.md:147 +#: src/SUMMARY.md:153 msgid "Operators: Add, Mul, ..." msgstr "عملگر‌های: جمع و ضرب, ..." -#: src/SUMMARY.md:148 +#: src/SUMMARY.md:154 msgid "Closures: Fn, FnMut, FnOnce" msgstr "بسته شدن‌ها: Fn, FnMut, FnOnce" -#: src/SUMMARY.md:150 src/exercises/day-3/simple-gui.md:1 -#: src/exercises/day-3/solutions-morning.md:3 +#: src/SUMMARY.md:156 msgid "A Simple GUI Library" msgstr "یک کتابخانه GUI ساده" -#: src/SUMMARY.md:152 +#: src/SUMMARY.md:157 src/exercises/day-3/solutions-morning.md:142 +msgid "Points and Polygons" +msgstr "نقطه‌ها و چند ضلعی‌ها" + +#: src/SUMMARY.md:159 msgid "Day 3: Afternoon" msgstr "روز سوم: عصر" -#: src/SUMMARY.md:154 src/error-handling.md:1 +#: src/SUMMARY.md:161 src/error-handling.md:1 msgid "Error Handling" msgstr "مدیریت خطا (Error Handling)" -#: src/SUMMARY.md:155 src/error-handling/panics.md:1 +#: src/SUMMARY.md:162 src/error-handling/panics.md:1 msgid "Panics" -msgstr "Panics" +msgstr "پانیک‌ها" -#: src/SUMMARY.md:156 +#: src/SUMMARY.md:163 msgid "Catching Stack Unwinding" msgstr "گرفتن Stack در حال بازگشایی" -#: src/SUMMARY.md:157 +#: src/SUMMARY.md:164 msgid "Structured Error Handling" msgstr "مدیریت خطای ساختاری" -#: src/SUMMARY.md:158 +#: src/SUMMARY.md:165 msgid "Propagating Errors with ?" msgstr "انتشار خطاها با استفاده از عملگر ؟" -#: src/SUMMARY.md:159 src/error-handling/converting-error-types.md:1 +#: src/SUMMARY.md:166 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:161 src/error-handling/deriving-error-enums.md:1 +#: src/SUMMARY.md:168 src/error-handling/deriving-error-enums.md:1 msgid "Deriving Error Enums" -msgstr "استخراج شماره های خطا" +msgstr "استخراج شماره‌های خطا" -#: src/SUMMARY.md:162 src/error-handling/dynamic-errors.md:1 +#: src/SUMMARY.md:169 src/error-handling/dynamic-errors.md:1 msgid "Dynamic Error Types" msgstr "انواع خطاهای Dynamic" -#: src/SUMMARY.md:163 src/error-handling/error-contexts.md:1 +#: src/SUMMARY.md:170 src/error-handling/error-contexts.md:1 msgid "Adding Context to Errors" msgstr "اضافه کردن زمینه (Context) به خطاها" -#: src/SUMMARY.md:164 src/testing.md:1 +#: src/SUMMARY.md:171 src/testing.md:1 msgid "Testing" msgstr "تست‌کردن" -#: src/SUMMARY.md:165 src/testing/unit-tests.md:1 +#: src/SUMMARY.md:172 src/testing/unit-tests.md:1 msgid "Unit Tests" msgstr "تست‌های واحد (Unit Tests)" -#: src/SUMMARY.md:166 src/testing/test-modules.md:1 +#: src/SUMMARY.md:173 src/testing/test-modules.md:1 msgid "Test Modules" msgstr "تست‌ ماژول‌ها" -#: src/SUMMARY.md:167 src/testing/doc-tests.md:1 +#: src/SUMMARY.md:174 src/testing/doc-tests.md:1 msgid "Documentation Tests" msgstr "تست‌ سندها" -#: src/SUMMARY.md:168 src/testing/integration-tests.md:1 +#: src/SUMMARY.md:175 src/testing/integration-tests.md:1 msgid "Integration Tests" msgstr "Integration Tests" -#: src/SUMMARY.md:169 src/bare-metal/useful-crates.md:1 +#: src/SUMMARY.md:176 src/bare-metal/useful-crates.md:1 #, fuzzy msgid "Useful crates" -msgstr "جعبه های (crates) کاربردی" +msgstr "جعبه‌های (crates) کاربردی" -#: src/SUMMARY.md:170 src/unsafe.md:1 +#: src/SUMMARY.md:177 src/unsafe.md:1 msgid "Unsafe Rust" -msgstr "Unsafe Rust" +msgstr "Rust ناایمن" -#: src/SUMMARY.md:171 src/unsafe/raw-pointers.md:1 +#: src/SUMMARY.md:178 src/unsafe/raw-pointers.md:1 msgid "Dereferencing Raw Pointers" msgstr "عدم ارجاع به اشاره گرهای خام" -#: src/SUMMARY.md:172 src/unsafe/mutable-static-variables.md:1 +#: src/SUMMARY.md:179 src/unsafe/mutable-static-variables.md:1 msgid "Mutable Static Variables" msgstr "متغیرهای ثابت قابل تغییر" -#: src/SUMMARY.md:173 src/unsafe/unions.md:1 +#: src/SUMMARY.md:180 src/unsafe/unions.md:1 msgid "Unions" -msgstr "Unions" +msgstr "نوع داده چندگانه" -#: src/SUMMARY.md:174 src/unsafe/calling-unsafe-functions.md:1 +#: src/SUMMARY.md:181 src/unsafe/calling-unsafe-functions.md:1 msgid "Calling Unsafe Functions" msgstr "فراخوانی متدهای ناامن" -#: src/SUMMARY.md:175 src/unsafe/writing-unsafe-functions.md:1 +#: src/SUMMARY.md:182 src/unsafe/writing-unsafe-functions.md:1 msgid "Writing Unsafe Functions" msgstr "نوشتن متدهای ناامن" -#: src/SUMMARY.md:176 +#: src/SUMMARY.md:183 msgid "Extern Functions" msgstr "توابع خارجی (Extern Functions)" -#: src/SUMMARY.md:177 src/unsafe/unsafe-traits.md:1 +#: src/SUMMARY.md:184 src/unsafe/unsafe-traits.md:1 msgid "Implementing Unsafe Traits" msgstr "پیاده سازی صفات (Traits) ناامن" -#: src/SUMMARY.md:179 src/exercises/day-3/safe-ffi-wrapper.md:1 +#: src/SUMMARY.md:186 src/exercises/day-3/safe-ffi-wrapper.md:1 #: src/exercises/day-3/solutions-afternoon.md:3 msgid "Safe FFI Wrapper" msgstr "امن بودن FFI Wrapper" -#: src/SUMMARY.md:182 src/SUMMARY.md:252 -#: src/running-the-course/course-structure.md:16 src/bare-metal/android.md:1 +#: src/SUMMARY.md:189 src/SUMMARY.md:259 src/bare-metal/android.md:1 msgid "Android" -msgstr "Android" +msgstr "اندروید" -#: src/SUMMARY.md:187 src/android/setup.md:1 +#: src/SUMMARY.md:194 src/android/setup.md:1 msgid "Setup" msgstr "تنظیم" -#: src/SUMMARY.md:188 src/android/build-rules.md:1 +#: src/SUMMARY.md:195 src/android/build-rules.md:1 msgid "Build Rules" msgstr "قوانین ساخت" -#: src/SUMMARY.md:189 +#: src/SUMMARY.md:196 msgid "Binary" msgstr "باینری" -#: src/SUMMARY.md:190 +#: src/SUMMARY.md:197 msgid "Library" msgstr "کتابخانه" -#: src/SUMMARY.md:191 src/android/aidl.md:1 +#: src/SUMMARY.md:198 src/android/aidl.md:1 msgid "AIDL" msgstr "AIDL" -#: src/SUMMARY.md:192 +#: src/SUMMARY.md:199 msgid "Interface" msgstr "رابط (Interface)" -#: src/SUMMARY.md:193 +#: src/SUMMARY.md:200 msgid "Implementation" msgstr "پیاده سازی" -#: src/SUMMARY.md:194 +#: src/SUMMARY.md:201 msgid "Server" msgstr "سرور" -#: src/SUMMARY.md:195 src/android/aidl/deploy.md:1 +#: src/SUMMARY.md:202 src/android/aidl/deploy.md:1 msgid "Deploy" msgstr "دیپلوی" -#: src/SUMMARY.md:196 +#: src/SUMMARY.md:203 msgid "Client" msgstr "کاربر" -#: src/SUMMARY.md:197 src/android/aidl/changing.md:1 +#: src/SUMMARY.md:204 src/android/aidl/changing.md:1 msgid "Changing API" msgstr "تغییر دادن API" -#: src/SUMMARY.md:198 src/SUMMARY.md:242 src/android/logging.md:1 +#: src/SUMMARY.md:205 src/SUMMARY.md:249 src/android/logging.md:1 #: src/bare-metal/aps/logging.md:1 msgid "Logging" msgstr "لاگ" -#: src/SUMMARY.md:199 src/android/interoperability.md:1 +#: src/SUMMARY.md:206 src/android/interoperability.md:1 msgid "Interoperability" msgstr "قابلیت همکاری" -#: src/SUMMARY.md:200 +#: src/SUMMARY.md:207 msgid "With C" msgstr "با C" -#: src/SUMMARY.md:201 +#: src/SUMMARY.md:208 msgid "Calling C with Bindgen" msgstr "فراخوانی C با Bindgen" -#: src/SUMMARY.md:202 +#: src/SUMMARY.md:209 msgid "Calling Rust from C" msgstr "فراخوانی Rust از C" -#: src/SUMMARY.md:203 src/android/interoperability/cpp.md:1 +#: src/SUMMARY.md:210 src/android/interoperability/cpp.md:1 msgid "With C++" msgstr "با ++C" -#: src/SUMMARY.md:204 +#: src/SUMMARY.md:211 msgid "With Java" msgstr "با جاوا" -#: src/SUMMARY.md:208 +#: src/SUMMARY.md:215 msgid "Bare Metal: Morning" msgstr "با Bare Metal: صبح" -#: src/SUMMARY.md:213 +#: src/SUMMARY.md:220 msgid "no_std" msgstr "no_std" -#: src/SUMMARY.md:214 +#: src/SUMMARY.md:221 msgid "A Minimal Example" msgstr "یک مثال ساده" -#: src/SUMMARY.md:215 +#: src/SUMMARY.md:222 msgid "alloc" msgstr "alloc" -#: src/SUMMARY.md:216 src/bare-metal/microcontrollers.md:1 +#: src/SUMMARY.md:223 src/bare-metal/microcontrollers.md:1 msgid "Microcontrollers" msgstr "میکروکنترلرها" -#: src/SUMMARY.md:217 src/bare-metal/microcontrollers/mmio.md:1 +#: src/SUMMARY.md:224 src/bare-metal/microcontrollers/mmio.md:1 msgid "Raw MMIO" msgstr "Raw MMIO" -#: src/SUMMARY.md:218 +#: src/SUMMARY.md:225 msgid "PACs" msgstr "PACs" -#: src/SUMMARY.md:219 +#: src/SUMMARY.md:226 msgid "HAL Crates" msgstr "HAL Crates" -#: src/SUMMARY.md:220 +#: src/SUMMARY.md:227 msgid "Board Support Crates" msgstr "Board Support Crates" -#: src/SUMMARY.md:221 +#: src/SUMMARY.md:228 msgid "The Type State Pattern" msgstr "انواع State Pattern" -#: src/SUMMARY.md:222 +#: src/SUMMARY.md:229 msgid "embedded-hal" msgstr "embedded-hal" -#: src/SUMMARY.md:223 +#: src/SUMMARY.md:230 msgid "probe-rs, cargo-embed" msgstr "probe-rs, cargo-embed" -#: src/SUMMARY.md:224 src/bare-metal/microcontrollers/debugging.md:1 +#: src/SUMMARY.md:231 src/bare-metal/microcontrollers/debugging.md:1 msgid "Debugging" msgstr "اشکال یابی (Debugging)" -#: src/SUMMARY.md:225 src/SUMMARY.md:245 +#: src/SUMMARY.md:232 src/SUMMARY.md:252 msgid "Other Projects" msgstr "باقی پروژه‌ها" -#: src/SUMMARY.md:227 src/exercises/bare-metal/compass.md:1 +#: src/SUMMARY.md:234 src/exercises/bare-metal/compass.md:1 #: src/exercises/bare-metal/solutions-morning.md:3 msgid "Compass" msgstr "قطب‌نما" -#: src/SUMMARY.md:229 +#: src/SUMMARY.md:236 msgid "Bare Metal: Afternoon" msgstr "با Bare Metal: عصر" -#: src/SUMMARY.md:231 +#: src/SUMMARY.md:238 msgid "Application Processors" -msgstr "پردازنده های برنامه" +msgstr "پردازنده‌های برنامه" -#: src/SUMMARY.md:232 src/bare-metal/aps/entry-point.md:1 +#: src/SUMMARY.md:239 src/bare-metal/aps/entry-point.md:1 msgid "Getting Ready to Rust" msgstr "آماده شدن برای Rust" -#: src/SUMMARY.md:233 +#: src/SUMMARY.md:240 msgid "Inline Assembly" msgstr "اسمبلی درونی" -#: src/SUMMARY.md:234 +#: src/SUMMARY.md:241 msgid "MMIO" msgstr "MMIO" -#: src/SUMMARY.md:235 +#: src/SUMMARY.md:242 msgid "Let's Write a UART Driver" msgstr "بریم یک درایور UART بنویسیم" -#: src/SUMMARY.md:236 +#: src/SUMMARY.md:243 msgid "More Traits" msgstr "صفت‌های بیشتر" -#: src/SUMMARY.md:237 +#: src/SUMMARY.md:244 msgid "A Better UART Driver" msgstr "یک درایور بهتر UART" -#: src/SUMMARY.md:238 src/bare-metal/aps/better-uart/bitflags.md:1 +#: src/SUMMARY.md:245 src/bare-metal/aps/better-uart/bitflags.md:1 msgid "Bitflags" -msgstr "پرچم های بیتی (Bitflags)" +msgstr "پرچم‌های بیتی (Bitflags)" -#: src/SUMMARY.md:239 +#: src/SUMMARY.md:246 msgid "Multiple Registers" msgstr "رجیستر‌های چندگانه" -#: src/SUMMARY.md:240 src/bare-metal/aps/better-uart/driver.md:1 +#: src/SUMMARY.md:247 src/bare-metal/aps/better-uart/driver.md:1 msgid "Driver" msgstr "درایور" -#: src/SUMMARY.md:241 src/SUMMARY.md:243 +#: src/SUMMARY.md:248 src/SUMMARY.md:250 msgid "Using It" msgstr "استفاده از آن" -#: src/SUMMARY.md:244 src/bare-metal/aps/exceptions.md:1 +#: src/SUMMARY.md:251 src/bare-metal/aps/exceptions.md:1 msgid "Exceptions" msgstr "استثناها" -#: src/SUMMARY.md:246 +#: src/SUMMARY.md:253 #, fuzzy msgid "Useful Crates" -msgstr "جعبه های (Crates) کاربردی" +msgstr "جعبه‌های (Crates) کاربردی" -#: src/SUMMARY.md:247 +#: src/SUMMARY.md:254 msgid "zerocopy" msgstr "صفر کپی (zerocopy)" -#: src/SUMMARY.md:248 +#: src/SUMMARY.md:255 msgid "aarch64-paging" msgstr "aarch64-paging" -#: src/SUMMARY.md:249 +#: src/SUMMARY.md:256 msgid "buddy_system_allocator" msgstr "buddy_system_allocator" -#: src/SUMMARY.md:250 +#: src/SUMMARY.md:257 msgid "tinyvec" msgstr "tinyvec" -#: src/SUMMARY.md:251 +#: src/SUMMARY.md:258 msgid "spin" msgstr "چرخش" -#: src/SUMMARY.md:253 src/bare-metal/android/vmbase.md:1 +#: src/SUMMARY.md:260 src/bare-metal/android/vmbase.md:1 msgid "vmbase" msgstr "vmbase" -#: src/SUMMARY.md:255 +#: src/SUMMARY.md:262 msgid "RTC Driver" msgstr "درایور RTC" -#: src/SUMMARY.md:258 +#: src/SUMMARY.md:265 msgid "Concurrency: Morning" msgstr "همزمانی: صبح" -#: src/SUMMARY.md:263 src/concurrency/threads.md:1 +#: src/SUMMARY.md:270 src/concurrency/threads.md:1 msgid "Threads" msgstr "تردها" -#: src/SUMMARY.md:264 src/concurrency/scoped-threads.md:1 +#: src/SUMMARY.md:271 src/concurrency/scoped-threads.md:1 msgid "Scoped Threads" msgstr "محدوده تردها" -#: src/SUMMARY.md:265 src/concurrency/channels.md:1 +#: src/SUMMARY.md:272 src/concurrency/channels.md:1 msgid "Channels" msgstr "کانال‌ها" -#: src/SUMMARY.md:266 src/concurrency/channels/unbounded.md:1 +#: src/SUMMARY.md:273 src/concurrency/channels/unbounded.md:1 msgid "Unbounded Channels" msgstr "کانال‌های نامحدود" -#: src/SUMMARY.md:267 src/concurrency/channels/bounded.md:1 +#: src/SUMMARY.md:274 src/concurrency/channels/bounded.md:1 msgid "Bounded Channels" -msgstr "کانال های محدود" +msgstr "کانال‌های محدود" -#: src/SUMMARY.md:268 +#: src/SUMMARY.md:275 msgid "Send and Sync" msgstr "ارسال و همگام سازی" -#: src/SUMMARY.md:268 +#: src/SUMMARY.md:275 msgid "Send" msgstr "ارسال" -#: src/SUMMARY.md:268 +#: src/SUMMARY.md:275 msgid "Sync" msgstr "همگام سازی" -#: src/SUMMARY.md:271 src/concurrency/send-sync/examples.md:1 +#: src/SUMMARY.md:278 src/concurrency/send-sync/examples.md:1 msgid "Examples" msgstr "مثال‌ها" -#: src/SUMMARY.md:272 src/concurrency/shared_state.md:1 +#: src/SUMMARY.md:279 src/concurrency/shared_state.md:1 msgid "Shared State" msgstr "ناحیه‌های مشترک" -#: src/SUMMARY.md:273 +#: src/SUMMARY.md:280 msgid "Arc" msgstr "Shared State" -#: src/SUMMARY.md:274 +#: src/SUMMARY.md:281 msgid "Mutex" msgstr "Mutex" -#: src/SUMMARY.md:277 src/SUMMARY.md:298 +#: src/SUMMARY.md:284 src/SUMMARY.md:305 #: src/exercises/concurrency/dining-philosophers.md:1 #: src/exercises/concurrency/solutions-morning.md:3 msgid "Dining Philosophers" msgstr "فلسفه Dining" -#: src/SUMMARY.md:278 src/exercises/concurrency/link-checker.md:1 +#: src/SUMMARY.md:285 src/exercises/concurrency/link-checker.md:1 msgid "Multi-threaded Link Checker" msgstr "جستجوگر پیوند چند تِردی" -#: src/SUMMARY.md:280 +#: src/SUMMARY.md:287 msgid "Concurrency: Afternoon" msgstr "همزمانی: عصر" -#: src/SUMMARY.md:282 +#: src/SUMMARY.md:289 msgid "Async Basics" msgstr "مبانی Async" -#: src/SUMMARY.md:283 +#: src/SUMMARY.md:290 msgid "async/await" msgstr "async/await" -#: src/SUMMARY.md:284 src/async/futures.md:1 +#: src/SUMMARY.md:291 src/async/futures.md:1 msgid "Futures" msgstr "Futures" -#: src/SUMMARY.md:285 src/async/runtimes.md:1 +#: src/SUMMARY.md:292 src/async/runtimes.md:1 msgid "Runtimes" msgstr "Runtimes" -#: src/SUMMARY.md:286 src/async/runtimes/tokio.md:1 +#: src/SUMMARY.md:293 src/async/runtimes/tokio.md:1 msgid "Tokio" msgstr "Tokio" -#: src/SUMMARY.md:287 src/exercises/concurrency/link-checker.md:126 -#: src/exercises/concurrency/chat-app.md:140 src/async/tasks.md:1 +#: src/SUMMARY.md:294 src/exercises/concurrency/link-checker.md:126 +#: src/async/tasks.md:1 src/exercises/concurrency/chat-app.md:143 msgid "Tasks" msgstr "Task" -#: src/SUMMARY.md:288 src/async/channels.md:1 +#: src/SUMMARY.md:295 src/async/channels.md:1 msgid "Async Channels" msgstr "کانال‌های Async" -#: src/SUMMARY.md:290 src/async/control-flow/join.md:1 +#: src/SUMMARY.md:297 src/async/control-flow/join.md:1 msgid "Join" msgstr "Join" -#: src/SUMMARY.md:291 src/async/control-flow/select.md:1 +#: src/SUMMARY.md:298 src/async/control-flow/select.md:1 msgid "Select" msgstr "Select" -#: src/SUMMARY.md:292 +#: src/SUMMARY.md:299 msgid "Pitfalls" msgstr "مشکل‌ها" -#: src/SUMMARY.md:293 +#: src/SUMMARY.md:300 msgid "Blocking the Executor" msgstr "مسدود کردن Executor" -#: src/SUMMARY.md:294 src/async/pitfalls/pin.md:1 +#: src/SUMMARY.md:301 src/async/pitfalls/pin.md:1 msgid "Pin" msgstr "Pin" -#: src/SUMMARY.md:295 src/async/pitfalls/async-traits.md:1 +#: src/SUMMARY.md:302 src/async/pitfalls/async-traits.md:1 msgid "Async Traits" msgstr "صفات Async" -#: src/SUMMARY.md:296 src/async/pitfalls/cancellation.md:1 +#: src/SUMMARY.md:303 src/async/pitfalls/cancellation.md:1 msgid "Cancellation" msgstr "لغو" -#: src/SUMMARY.md:299 src/exercises/concurrency/chat-app.md:1 -#: src/exercises/concurrency/solutions-afternoon.md:119 +#: src/SUMMARY.md:306 src/exercises/concurrency/chat-app.md:1 +#: src/exercises/concurrency/solutions-afternoon.md:95 msgid "Broadcast Chat Application" msgstr "پخش برنامه چت" -#: src/SUMMARY.md:302 +#: src/SUMMARY.md:309 msgid "Final Words" msgstr "کلمات آخر" -#: src/SUMMARY.md:306 src/thanks.md:1 +#: src/SUMMARY.md:313 src/thanks.md:1 msgid "Thanks!" msgstr "سپاس!" -#: src/SUMMARY.md:307 +#: src/SUMMARY.md:314 src/glossary.md:1 +msgid "Glossary" +msgstr "واژه نامه" + +#: src/SUMMARY.md:315 msgid "Other Resources" msgstr "منابع دیگر" -#: src/SUMMARY.md:308 src/credits.md:1 +#: src/SUMMARY.md:316 src/credits.md:1 msgid "Credits" msgstr "اعتبارها" -#: src/SUMMARY.md:311 src/exercises/solutions.md:1 +#: src/SUMMARY.md:319 src/exercises/solutions.md:1 msgid "Solutions" msgstr "راه حل‌ها" -#: src/SUMMARY.md:316 +#: src/SUMMARY.md:324 msgid "Day 1 Morning" msgstr "روز ۱ صبح" -#: src/SUMMARY.md:317 +#: src/SUMMARY.md:325 msgid "Day 1 Afternoon" msgstr "روز ۱ عصر" -#: src/SUMMARY.md:318 +#: src/SUMMARY.md:326 msgid "Day 2 Morning" msgstr "روز ۲ صبح" -#: src/SUMMARY.md:319 +#: src/SUMMARY.md:327 msgid "Day 2 Afternoon" msgstr "روز ۲ عصر" -#: src/SUMMARY.md:320 +#: src/SUMMARY.md:328 msgid "Day 3 Morning" msgstr "روز ۳ صبح" -#: src/SUMMARY.md:321 +#: src/SUMMARY.md:329 msgid "Day 3 Afternoon" msgstr "روز ۳ عصر" -#: src/SUMMARY.md:322 +#: src/SUMMARY.md:330 msgid "Bare Metal Rust Morning" msgstr "صبح‌گاه با Bare Metal Rust" -#: src/SUMMARY.md:323 src/exercises/bare-metal/solutions-afternoon.md:1 +#: src/SUMMARY.md:331 src/exercises/bare-metal/solutions-afternoon.md:1 msgid "Bare Metal Rust Afternoon" msgstr "عصرانه با Bare Metal Rust" -#: src/SUMMARY.md:324 +#: src/SUMMARY.md:332 msgid "Concurrency Morning" msgstr "صبح‌گاه با همزمانی (Concurrency)" -#: src/SUMMARY.md:325 +#: src/SUMMARY.md:333 msgid "Concurrency Afternoon" msgstr "همزمانی: عصر" #: 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)" -msgstr "" -"[![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)" - -#: src/index.md:3 -msgid "Build workflow" -msgstr "ساخت workflow" - -#: src/index.md:3 +#, fuzzy 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/index.md:4 -msgid "GitHub contributors" -msgstr "مشارکت کنندگان GitHub" - -#: src/index.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)" @@ -1065,20 +1054,6 @@ msgstr "" "io/github/stars/google/comprehensive-rust?style=flat-square)](https://github." "com/google/comprehensive-rust/stargazers)" -#: src/index.md:5 -msgid "GitHub stars" -msgstr "ستاره های GitHub" - -#: src/index.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 "" -"[![GitHub stars](https://img.shields.io/github/stars/google/comprehensive-" -"rust?style=flat-square)](https://github.com/google/comprehensive-rust/" -"stargazers)" - #: src/index.md:7 msgid "" "This is a free Rust course developed by the Android team at Google. The " @@ -1091,37 +1066,44 @@ msgstr "" #: src/index.md:11 msgid "" +"The latest version of the course can be found at . If you are reading somewhere else, please check there " +"for updates." +msgstr "" + +#: src/index.md:15 +msgid "" "The goal of the course is to teach you Rust. We assume you don't know " "anything about Rust and hope to:" msgstr "" "هدف از این دوره آموزش Rust به شماست. ما فرض می کنیم شما چیزی از درباره Rust " "نمی دانید امید به یادگیری:" -#: src/index.md:14 +#: src/index.md:18 msgid "Give you a comprehensive understanding of the Rust syntax and language." msgstr "درک جامعی از syntax و زبان Rust به شما می دهد." -#: src/index.md:15 +#: src/index.md:19 msgid "Enable you to modify existing programs and write new programs in Rust." msgstr "" "شما را قادر می سازد تا برنامه های موجود را تغییر دهید و برنامه های جدید را " "در Rust بنویسید." -#: src/index.md:16 +#: src/index.md:20 msgid "Show you common Rust idioms." msgstr "اصطلاحات رایج Rust را به شما نشان می دهد." -#: src/index.md:18 +#: src/index.md:22 msgid "We call the first three course days Rust Fundamentals." msgstr "ما سه روز اول دوره را Rust Fundamentals می نامیم." -#: src/index.md:20 +#: src/index.md:24 msgid "" "Building on this, you're invited to dive into one or more specialized topics:" msgstr "" "با تکیه بر این، از شما دعوت می شود تا به یک یا چند موضوع تخصصی بپردازید:" -#: src/index.md:22 +#: src/index.md:26 #, fuzzy msgid "" "[Android](android.md): a half-day course on using Rust for Android platform " @@ -1136,14 +1118,14 @@ msgstr "" "استفاده از نخ ها و موتکس) و همزمانی async/wait (همکاری چندوظیفه ای با " "استفاده از حالت futures)." -#: src/index.md:24 +#: src/index.md:28 msgid "" "[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 "" -#: src/index.md:27 +#: src/index.md:31 msgid "" "[Concurrency](concurrency.md): a whole-day class on concurrency in Rust. We " "cover both classical concurrency (preemptively scheduling using threads and " @@ -1151,11 +1133,11 @@ msgid "" "futures)." msgstr "" -#: src/index.md:33 +#: src/index.md:37 msgid "Non-Goals" msgstr "موارد غیر اهداف آموزشی" -#: src/index.md:35 +#: src/index.md:39 msgid "" "Rust is a large language and we won't be able to cover all of it in a few " "days. Some non-goals of this course are:" @@ -1163,7 +1145,7 @@ msgstr "" "Rust زبان بزرگی است و تا چند روز دیگر نمی‌توانیم همه آن را پوشش دهیم. برخی از " "اهداف غیر از این دوره عبارتند از:" -#: src/index.md:38 +#: src/index.md:42 msgid "" "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]" @@ -1173,11 +1155,11 @@ msgstr "" "(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." -#: src/index.md:42 +#: src/index.md:46 msgid "Assumptions" msgstr "فرضیه ها" -#: src/index.md:44 +#: src/index.md:48 msgid "" "The course assumes that you already know how to program. Rust is a " "statically-typed language and we will sometimes make comparisons with C and " @@ -1188,7 +1170,7 @@ msgstr "" "و ما گاهی اوقات زبان Rust را با C و C++ مقایسه می کنیم تا رویکرد‌های Rust را " "بهتر توضیح دهیم یا در در حالت مقایسه با آن قرار دهیم." -#: src/index.md:48 +#: src/index.md:52 msgid "" "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." @@ -1196,7 +1178,7 @@ msgstr "" "اگر می‌دانید چگونه به زبانی با دینامیک تایپ مانند پایتون یا جاوا اسکریپت " "برنامه‌نویسی کنید می‌توانید به خوبی این روش را دنبال کنید." -#: src/index.md:53 +#: src/index.md:57 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 " @@ -1214,10 +1196,20 @@ msgid "" msgstr "" #: src/running-the-course.md:8 +msgid "" +"We typically run classes from 10:00 am to 4:00 pm, with a 1 hour lunch break " +"in the middle. This leaves 2.5 hours for the morning class and 2.5 hours for " +"the afternoon class. Note that this is just a recommendation: you can also " +"spend 3 hour on the morning session to give people more time for exercises. " +"The downside of longer session is that people can become very tired after 6 " +"full hours of class in the afternoon." +msgstr "" + +#: src/running-the-course.md:16 msgid "Before you run the course, you will want to:" msgstr "قبل از اجرای دوره، شما می خواهید:" -#: src/running-the-course.md:10 +#: src/running-the-course.md:18 msgid "" "Make yourself familiar with the course material. We've included speaker " "notes to help highlight the key points (please help us by contributing more " @@ -1226,7 +1218,7 @@ msgid "" "Notes\"). This way you have a clean screen to present to the class." msgstr "" -#: src/running-the-course.md:16 +#: src/running-the-course.md:24 msgid "" "Decide on the dates. Since the course takes at least three full days, we " "recommend that you schedule the days over two weeks. Course participants " @@ -1234,7 +1226,7 @@ msgid "" "helps them process all the information we give them." msgstr "" -#: src/running-the-course.md:21 +#: src/running-the-course.md:29 msgid "" "Find a room large enough for your in-person participants. We recommend a " "class size of 15-25 people. That's small enough that people are comfortable " @@ -1245,7 +1237,7 @@ msgid "" "instructor, so a lectern won't be very helpful for you." msgstr "" -#: src/running-the-course.md:29 +#: src/running-the-course.md:37 msgid "" "On the day of your course, show up to the room a little early to set things " "up. We recommend presenting directly using `mdbook serve` running on your " @@ -1255,7 +1247,7 @@ msgid "" "you or the course participants spot them." msgstr "" -#: src/running-the-course.md:35 +#: src/running-the-course.md:43 msgid "" "Let people solve the exercises by themselves or in small groups. We " "typically spend 30-45 minutes on exercises in the morning and in the " @@ -1266,13 +1258,13 @@ msgid "" "information in the standard library." msgstr "" -#: src/running-the-course.md:43 +#: src/running-the-course.md:51 msgid "" "That is all, good luck running the course! We hope it will be as much fun " "for you as it has been for us!" msgstr "" -#: src/running-the-course.md:46 +#: src/running-the-course.md:54 msgid "" "Please [provide feedback](https://github.com/google/comprehensive-rust/" "discussions/86) afterwards so that we can keep improving the course. We " @@ -1287,26 +1279,34 @@ msgstr "" "github.com/google/comprehensive-rust/discussions/100)!" #: src/running-the-course/course-structure.md:5 -msgid "The course is fast paced and covers a lot of ground:" -msgstr "یاد گیری این دوره کوتاه است و مبحث‌های زیادی را پوشش می دهد:" +msgid "Rust Fundamentals" +msgstr "" #: src/running-the-course/course-structure.md:7 -msgid "Day 1: Basic Rust, ownership and the borrow checker." -msgstr "روز 1: زنگ اولیه، مالکیت و چک کننده قرض." - -#: src/running-the-course/course-structure.md:8 -msgid "Day 2: Compound data types, pattern matching, the standard library." +msgid "" +"The first three days make up [Rust Fundaments](../welcome-day-1.md). The " +"days are fast paced and we cover a lot of ground:" msgstr "" -#: src/running-the-course/course-structure.md:9 -msgid "Day 3: Traits and generics, error handling, testing, unsafe Rust." +#: src/running-the-course/course-structure.md:10 +msgid "Day 1: Basic Rust, syntax, control flow, creating and consuming values." msgstr "" #: src/running-the-course/course-structure.md:11 +msgid "" +"Day 2: Memory management, ownership, compound data types, and the standard " +"library." +msgstr "" + +#: src/running-the-course/course-structure.md:12 +msgid "Day 3: Generics, traits, error handling, testing, and unsafe Rust." +msgstr "" + +#: src/running-the-course/course-structure.md:14 msgid "Deep Dives" msgstr "شیرجه در اعماق" -#: src/running-the-course/course-structure.md:13 +#: src/running-the-course/course-structure.md:16 msgid "" "In addition to the 3-day class on Rust Fundamentals, we cover some more " "specialized topics:" @@ -1314,17 +1314,23 @@ msgstr "" "علاوه بر کلاس 3 روزه Rust Fundamentals، موضوعات تخصصی تری را نیز پوشش می " "دهیم:" -#: src/running-the-course/course-structure.md:18 +#: src/running-the-course/course-structure.md:19 +#, fuzzy +msgid "Rust in Android" +msgstr "Android" + +#: src/running-the-course/course-structure.md:21 +#, 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 برای توسعه پلتفرم اندروید است. این شامل قابلیت تعامل با C، C++ و جاوا " "می شود." -#: src/running-the-course/course-structure.md:22 +#: src/running-the-course/course-structure.md:25 #, fuzzy msgid "" "You will need an [AOSP checkout](https://source.android.com/docs/setup/" @@ -1340,7 +1346,7 @@ msgstr "" "حاصل می کند که سیستم ساخت اندروید فایل های «Android.bp» را در «src/android/» " "می بیند." -#: src/running-the-course/course-structure.md:27 +#: src/running-the-course/course-structure.md:30 msgid "" "Ensure that `adb sync` works with your emulator or real device and pre-build " "all Android examples using `src/android/build_all.sh`. Read the script to " @@ -1351,21 +1357,23 @@ msgstr "" "بسازید. اسکریپت را بخوانید تا دستوراتی را که اجرا می‌کند ببینید و مطمئن شوید " "که وقتی آنها را اجرا می‌کنید به درستی کار می‌کنند." -#: src/running-the-course/course-structure.md:34 -msgid "Bare-Metal" +#: src/running-the-course/course-structure.md:37 +#, fuzzy +msgid "Bare-Metal Rust" msgstr "Bare-Metal" -#: src/running-the-course/course-structure.md:36 +#: src/running-the-course/course-structure.md:39 +#, 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 برای توسعه bare-metal (embedded) هم میکروکنترلرها و هم " "پردازنده های برنامه پوشش داده شده اند." -#: src/running-the-course/course-structure.md:40 +#: src/running-the-course/course-structure.md:43 msgid "" "For the microcontroller part, you will need to buy the [BBC micro:bit]" "(https://microbit.org/) v2 development board ahead of time. Everybody will " @@ -1376,19 +1384,21 @@ msgstr "" "v2 را خریداری کنید. همه باید تعدادی بسته را همانطور که در \\[welcome " "page\\] (../bare-metal.md) توضیح داده شده نصب کنند." -#: src/running-the-course/course-structure.md:45 -msgid "Concurrency" -msgstr "همزمانی" +#: src/running-the-course/course-structure.md:48 +#, fuzzy +msgid "Concurrency in Rust" +msgstr "صبح‌گاه با همزمانی (Concurrency)" -#: src/running-the-course/course-structure.md:47 +#: src/running-the-course/course-structure.md:50 +#, 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` اینگونه است." -#: src/running-the-course/course-structure.md:50 +#: src/running-the-course/course-structure.md:53 #, fuzzy msgid "" "You will need a fresh crate set up and the dependencies downloaded and ready " @@ -1399,27 +1409,11 @@ msgstr "" "هستند نیاز دارید. سپس می‌توانید مثال‌ها را در «src/main.rs» به راحتی copy/" "paste کنید و با آنها آزمایش کنید:" -#: src/running-the-course/course-structure.md:54 -msgid "" -"```shell\n" -"cargo init concurrency\n" -"cd concurrency\n" -"cargo add tokio --features full\n" -"cargo run\n" -"```" -msgstr "" -"```shell\n" -"cargo init concurrency\n" -"cd concurrency\n" -"cargo add tokio --features full\n" -"cargo run\n" -"```" - -#: src/running-the-course/course-structure.md:61 +#: src/running-the-course/course-structure.md:64 msgid "Format" msgstr "فرمت" -#: src/running-the-course/course-structure.md:63 +#: src/running-the-course/course-structure.md:66 msgid "" "The course is meant to be very interactive and we recommend letting the " "questions drive the exploration of Rust!" @@ -1472,10 +1466,11 @@ 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), [@hugojacob](https://github." -"com/hugojacob), [@joaovicmendes](https://github.com/joaovicmendes) and " +"com/hugojacob), [@joaovicmendes](https://github.com/joaovicmendes), and " "[@henrif75](https://github.com/henrif75)." msgstr "" "[Brazilian Portuguese](https://google.github.io/comprehensive-rust/pt-BR/) " @@ -1485,25 +1480,59 @@ msgstr "" #: src/running-the-course/translations.md:7 msgid "" +"[Chinese (Simplified)](https://google.github.io/comprehensive-rust/zh-CN/) " +"by [@suetfei](https://github.com/suetfei), [@wnghl](https://github.com/" +"wnghl), [@anlunx](https://github.com/anlunx), [@kongy](https://github.com/" +"kongy), [@noahdragon](https://github.com/noahdragon), [@superwhd](https://" +"github.com/superwhd), [@SketchK](https://github.com/SketchK), and [@nodmp]" +"(https://github.com/nodmp)." +msgstr "" + +#: src/running-the-course/translations.md:8 +#, fuzzy +msgid "" +"[Chinese (Traditional)](https://google.github.io/comprehensive-rust/zh-TW/) " +"by [@hueich](https://github.com/hueich), [@victorhsieh](https://github.com/" +"victorhsieh), [@mingyc](https://github.com/mingyc), [@kuanhungchen](https://" +"github.com/kuanhungchen), and [@johnathan79717](https://github.com/" +"johnathan79717)." +msgstr "" +"[Brazilian Portuguese](https://google.github.io/comprehensive-rust/pt-BR/) " +"by [@rastringer](https://github.com/rastringer), [@hugojacob](https://github." +"com/hugojacob), [@joaovicmendes](https://github.com/joaovicmendes) and " +"[@henrif75](https://github.com/henrif75)." + +#: src/running-the-course/translations.md:9 +#, fuzzy +msgid "" "[Korean](https://google.github.io/comprehensive-rust/ko/) by [@keispace]" -"(https://github.com/keispace), [@jiyongp](https://github.com/jiyongp) and " +"(https://github.com/keispace), [@jiyongp](https://github.com/jiyongp), and " "[@jooyunghan](https://github.com/jooyunghan)." msgstr "" "[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)." -#: src/running-the-course/translations.md:9 +#: src/running-the-course/translations.md:10 +#, fuzzy +msgid "" +"[Spanish](https://google.github.io/comprehensive-rust/es/) by [@deavid]" +"(https://github.com/deavid)." +msgstr "" +"[Bengali](https://google.github.io/comprehensive-rust/bn/) توسط " +"[@raselmandol](https://github.com/raselmandol)." + +#: src/running-the-course/translations.md:12 msgid "" "Use the language picker in the top-right corner to switch between languages." msgstr "" "از انتخابگر زبان در گوشه بالا سمت راست برای جابه‌جایی بین زبان‌ها استفاده کنید." -#: src/running-the-course/translations.md:11 +#: src/running-the-course/translations.md:14 msgid "Incomplete Translations" msgstr "ترجمه‌های کامل نشده" -#: src/running-the-course/translations.md:13 +#: src/running-the-course/translations.md:16 msgid "" "There is a large number of in-progress translations. We link to the most " "recently updated translations:" @@ -1511,7 +1540,7 @@ msgstr "" "تعداد زیادی ترجمه در حال انجام وجود دارد. ما به آخرین ترجمه های به روز شده " "پیوند می دهیم:" -#: src/running-the-course/translations.md:16 +#: src/running-the-course/translations.md:19 msgid "" "[Bengali](https://google.github.io/comprehensive-rust/bn/) by [@raselmandol]" "(https://github.com/raselmandol)." @@ -1519,7 +1548,7 @@ msgstr "" "[Bengali](https://google.github.io/comprehensive-rust/bn/) توسط " "[@raselmandol](https://github.com/raselmandol)." -#: src/running-the-course/translations.md:17 +#: src/running-the-course/translations.md:20 msgid "" "[French](https://google.github.io/comprehensive-rust/fr/) by [@KookaS]" "(https://github.com/KookaS) and [@vcaen](https://github.com/vcaen)." @@ -1527,7 +1556,7 @@ msgstr "" "[French](https://google.github.io/comprehensive-rust/fr/) توسط [@KookaS]" "(https://github.com/KookaS) و [@vcaen](https://github.com/vcaen)." -#: src/running-the-course/translations.md:18 +#: src/running-the-course/translations.md:21 msgid "" "[German](https://google.github.io/comprehensive-rust/de/) by [@Throvn]" "(https://github.com/Throvn) and [@ronaldfw](https://github.com/ronaldfw)." @@ -1535,7 +1564,7 @@ msgstr "" "[German](https://google.github.io/comprehensive-rust/de/) توسط [@Throvn]" "(https://github.com/Throvn) و [@ronaldfw](https://github.com/ronaldfw)." -#: src/running-the-course/translations.md:19 +#: src/running-the-course/translations.md:22 msgid "" "[Japanese](https://google.github.io/comprehensive-rust/ja/) by [@CoinEZ-JPN]" "(https://github.com/CoinEZ) and [@momotaro1105](https://github.com/" @@ -1545,7 +1574,7 @@ msgstr "" "JPN](https://github.com/CoinEZ) و [@momotaro1105](https://github.com/" "momotaro1105)." -#: src/running-the-course/translations.md:21 +#: src/running-the-course/translations.md:24 msgid "" "If you want to help with this effort, please see [our instructions](https://" "github.com/google/comprehensive-rust/blob/main/TRANSLATIONS.md) for how to " @@ -1579,17 +1608,29 @@ msgid "**Please follow the instructions on .**" msgstr "**لطفا دستورالعمل را اجرا کنید .**" #: src/cargo.md:12 +#, fuzzy msgid "" "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." +"use to install to different compiler versions." msgstr "" "این ابزار ساخت Cargo ('cargo') و کامپایلر Rust ('rustc') را در اختیار شما " "قرار می دهد. همچنین «rustup» را دریافت خواهید کرد، یک ابزار خط فرمان که " "می‌توانید از آن برای نصب/تغییر toolchains، راه‌اندازی cross کامپایل و ... " "استفاده کنید." -#: src/cargo.md:16 +#: src/cargo.md:14 +msgid "" +"After installing Rust, you should configure your editor or IDE to work with " +"Rust. Most editors do this by talking to [rust-analyzer](https://rust-" +"analyzer.github.io/), which provides auto-completion and jump-to-definition " +"functionality for [VS Code](https://code.visualstudio.com/), [Emacs](https://" +"rust-analyzer.github.io/manual.html#emacs), [Vim/Neovim](https://rust-" +"analyzer.github.io/manual.html#vimneovim), and many others. There is also a " +"different IDE available called [RustRover](https://www.jetbrains.com/rust/)." +msgstr "" + +#: src/cargo.md:18 msgid "" "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 " @@ -1601,41 +1642,6 @@ msgstr "" "یک نسخه قدیمی Rust را جهت نصب می دهد و ممکن است منجر به رفتار غیرمنتظره " "برنامه شود. command مورد نظر این خواهد بود:" -#: src/cargo.md:18 -msgid "" -"```shell\n" -" sudo apt install cargo rust-src rustfmt\n" -"```" -msgstr "" -"```shell\n" -" sudo apt install cargo rust-src rustfmt\n" -"```" - -#: src/cargo.md:22 -msgid "" -"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 "" -"پیشنهاد می کنیم از [VS Code](https://code.visualstudio.com/) برای ویرایش کد " -"استفاده کنید (اما هر ویرایشگر سازگار با LSP با ust-analyzer[3](https://rust-" -"analyzer.github.io/) کار می کند)." - -#: 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 " -"tradeoffs. If you prefer them, you can install the [Rust Plugin](https://www." -"jetbrains.com/rust/). Please take note that as of January 2023 debugging " -"only works on the CLion version of the JetBrains IDEA suite." -msgstr "" -"برخی از افراد همچنین دوست دارند از خانواده IDE [JetBrains](https://www." -"jetbrains.com/clion/) استفاده کنند، که آنالیزهای خود را انجام می دهند اما " -"یان مسئله tradeoffهای خاص خود را دارد. اگر آنها را ترجیح می دهید، می توانید " -"[Plugin Rust](https://www.jetbrains.com/rust/) را نصب کنید. لطفاً توجه داشته " -"باشید که از ژانویه 2023 اشکال زدایی (debugging) فقط در نسخه CLion مجموعه " -"JetBrains IDEA کار می کند." - #: src/cargo/rust-ecosystem.md:1 msgid "The Rust Ecosystem" msgstr "اکوسیستم رراست" @@ -1693,9 +1699,10 @@ msgstr "" #: src/cargo/rust-ecosystem.md:21 src/hello-world.md:25 #: src/hello-world/small-example.md:27 src/why-rust/runtime.md:10 -#: src/why-rust/modern.md:21 src/basic-syntax/compound-types.md:30 -#: src/basic-syntax/references.md:23 +#: src/why-rust/modern.md:21 src/basic-syntax/compound-types.md:32 +#: src/basic-syntax/references.md:24 #: src/pattern-matching/destructuring-enums.md:35 +#: src/ownership/double-free-modern-cpp.md:55 #: src/error-handling/try-operator.md:48 #: src/error-handling/converting-error-types-example.md:50 #: src/concurrency/threads.md:30 src/async/async-await.md:25 @@ -1920,23 +1927,13 @@ msgstr "" "«rustc» و «cargo» کاربردی بدهد. در زمان نگارش، آخرین نسخه پایدار Rust دارای " "این version numberها است:" -#: src/cargo/running-locally.md:8 +#: src/cargo/running-locally.md:15 msgid "" -"```shell\n" -"% rustc --version\n" -"rustc 1.69.0 (84c898d65 2023-04-16)\n" -"% cargo --version\n" -"cargo 1.69.0 (6e9a83356 2023-04-12)\n" -"```" +"You can use any later version too since Rust maintains backwards " +"compatibility." msgstr "" -"```shell\n" -"% rustc --version\n" -"rustc 1.69.0 (84c898d65 2023-04-16)\n" -"% cargo --version\n" -"cargo 1.69.0 (6e9a83356 2023-04-12)\n" -"```" -#: src/cargo/running-locally.md:15 +#: src/cargo/running-locally.md:17 msgid "" "With this in place, follow these steps to build a Rust binary from one of " "the examples in this training:" @@ -1944,58 +1941,26 @@ msgstr "" "با این کار، این مراحل را دنبال کنید تا از یکی از مثال‌های این آموزش، یک " "باینری Rust بسازید:" -#: src/cargo/running-locally.md:18 +#: src/cargo/running-locally.md:20 msgid "Click the \"Copy to clipboard\" button on the example you want to copy." msgstr "" "روی دکمه \"کپی در کلیپ بورد\" در نمونه ای که می خواهید کپی کنید؛ کلیک کنید." -#: src/cargo/running-locally.md:20 +#: src/cargo/running-locally.md:22 msgid "" "Use `cargo new exercise` to create a new `exercise/` directory for your code:" msgstr "" "از «cargo new exercise» برای ایجاد دایرکتوری «excerise/» جدید برای کد خود " "استفاده کنید:" -#: src/cargo/running-locally.md:22 -msgid "" -"```shell\n" -"$ cargo new exercise\n" -" Created binary (application) `exercise` package\n" -"```" -msgstr "" -"```shell\n" -"$ cargo new exercise\n" -" Created binary (application) `exercise` package\n" -"```" - -#: src/cargo/running-locally.md:27 +#: src/cargo/running-locally.md:29 msgid "" "Navigate into `exercise/` and use `cargo run` to build and run your binary:" msgstr "" "به «exercise/» بروید و از «cargo run» برای ساخت و اجرای باینری خود استفاده " "کنید:" -#: src/cargo/running-locally.md:29 -msgid "" -"```shell\n" -"$ cd exercise\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" -" Running `target/debug/exercise`\n" -"Hello, world!\n" -"```" -msgstr "" -"```shell\n" -"$ cd exercise\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.75s\n" -" Running `target/debug/exercise`\n" -"Hello, world!\n" -"```" - -#: src/cargo/running-locally.md:38 +#: src/cargo/running-locally.md:40 msgid "" "Replace the boiler-plate code in `src/main.rs` with your own code. For " "example, using the example on the previous page, make `src/main.rs` look like" @@ -2003,7 +1968,7 @@ msgstr "" "کد صفحه دیگر را در `src/main.rs` با کد خود جایگزین کنید. برای مثال، با " "استفاده از مثال در صفحه قبل، «src/main.rs» را شبیه به آن کنید." -#: src/cargo/running-locally.md:41 +#: src/cargo/running-locally.md:43 msgid "" "```rust\n" "fn main() {\n" @@ -2017,29 +1982,11 @@ msgstr "" "}\n" "```" -#: src/cargo/running-locally.md:47 +#: src/cargo/running-locally.md:49 msgid "Use `cargo run` to build and run your updated binary:" msgstr "برای ساختن و اجرای باینری به روز شده خود از «Cargo Run» استفاده کنید:" -#: src/cargo/running-locally.md:49 -msgid "" -"```shell\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" -" Running `target/debug/exercise`\n" -"Edit me!\n" -"```" -msgstr "" -"```shell\n" -"$ cargo run\n" -" Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)\n" -" Finished dev [unoptimized + debuginfo] target(s) in 0.24s\n" -" Running `target/debug/exercise`\n" -"Edit me!\n" -"```" - -#: src/cargo/running-locally.md:57 +#: src/cargo/running-locally.md:59 msgid "" "Use `cargo check` to quickly check your project for errors, use `cargo " "build` to compile it without running it. You will find the output in `target/" @@ -2051,7 +1998,7 @@ msgstr "" "«target/debug/» برای ساخت اشکال زدایی معمولی خواهید یافت. برای تولید نسخه " "بهینه سازی شده در «target/release/» از «cargo build --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 " @@ -2061,7 +2008,7 @@ msgstr "" "هنگامی که دستورات \"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 " @@ -2093,16 +2040,13 @@ 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 "" -"Memory management: stack vs heap, manual memory management, scope-based " -"memory management, و garbage collection." #: src/welcome-day-1.md:12 -msgid "" -"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." -msgstr "Ownership: حرکت semantics،copying و cloning, borrowing و lifetimes." +msgid "Pattern matching: destructuring enums, structs, and arrays." +msgstr "" #: src/welcome-day-1.md:16 msgid "Please remind the students that:" @@ -2305,7 +2249,7 @@ 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 "" @@ -2445,6 +2389,196 @@ msgid "" "collector) as well as access to low-level hardware (should you need it)" msgstr "" +#: src/why-rust/an-example-in-c.md:4 +msgid "Let's consider the following \"minimum wrong example\" program in C:" +msgstr "" + +#: src/why-rust/an-example-in-c.md:6 +msgid "" +"```c,editable\n" +"#include \n" +"#include \n" +"#include \n" +"\n" +"int main(int argc, char* argv[]) {\n" +"\tchar *buf, *filename;\n" +"\tFILE *fp;\n" +"\tsize_t bytes, len;\n" +"\tstruct stat st;\n" +"\n" +"\tswitch (argc) {\n" +"\t\tcase 1:\n" +"\t\t\tprintf(\"Too few arguments!\\n\");\n" +"\t\t\treturn 1;\n" +"\n" +"\t\tcase 2:\n" +"\t\t\tfilename = argv[argc];\n" +"\t\t\tstat(filename, &st);\n" +"\t\t\tlen = st.st_size;\n" +"\t\t\t\n" +"\t\t\tbuf = (char*)malloc(len);\n" +"\t\t\tif (!buf)\n" +"\t\t\t\tprintf(\"malloc failed!\\n\", len);\n" +"\t\t\t\treturn 1;\n" +"\n" +"\t\t\tfp = fopen(filename, \"rb\");\n" +"\t\t\tbytes = fread(buf, 1, len, fp);\n" +"\t\t\tif (bytes = st.st_size)\n" +"\t\t\t\tprintf(\"%s\", buf);\n" +"\t\t\telse\n" +"\t\t\t\tprintf(\"fread failed!\\n\");\n" +"\n" +"\t\tcase 3:\n" +"\t\t\tprintf(\"Too many arguments!\\n\");\n" +"\t\t\treturn 1;\n" +"\t}\n" +"\n" +"\treturn 0;\n" +"}\n" +"```" +msgstr "" + +#: src/why-rust/an-example-in-c.md:48 +msgid "How many bugs do you spot?" +msgstr "" + +#: src/why-rust/an-example-in-c.md:52 +msgid "" +"Despite just 29 lines of code, this C example contains serious bugs in at " +"least 11:" +msgstr "" + +#: src/why-rust/an-example-in-c.md:54 +msgid "Assignment `=` instead of equality comparison `==` (line 28)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:55 +msgid "Excess argument to `printf` (line 23)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:56 +msgid "File descriptor leak (after line 26)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:57 +msgid "Forgotten braces in multi-line `if` (line 22)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:58 +msgid "Forgotten `break` in a `switch` statement (line 32)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:59 +msgid "" +"Forgotten NUL-termination of the `buf` string, leading to a buffer overflow " +"(line 29)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:60 +msgid "Memory leak by not freeing the `malloc`\\-allocated buffer (line 21)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:61 +msgid "Out-of-bounds access (line 17)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:62 +msgid "Unchecked cases in the `switch` statement (line 11)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:63 +msgid "Unchecked return values of `stat` and `fopen` (lines 18 and 26)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:65 +msgid "" +"_Shouldn't these bugs be obvious even for a C compiler?_ \n" +"No, surprisingly this code compiles warning-free at the default warning " +"level, even in the latest GCC version (13.2 as of writing)." +msgstr "" + +#: src/why-rust/an-example-in-c.md:68 +msgid "" +"_Isn't this a highly unrealistic example?_ \n" +"Absolutely not, these kind of bugs have lead to serious security " +"vulnerabilities in the past. Some examples:" +msgstr "" + +#: src/why-rust/an-example-in-c.md:71 +msgid "" +"Assignment `=` instead of equality comparison `==`: [The Linux Backdoor " +"Attempt of 2003](https://freedom-to-tinker.com/2013/10/09/the-linux-backdoor-" +"attempt-of-2003)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:72 +msgid "" +"Forgotten braces in multi-line `if`: [The Apple goto fail vulnerability]" +"(https://dwheeler.com/essays/apple-goto-fail.html)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:73 +msgid "" +"Forgotten `break` in a `switch` statement: [The break that broke sudo]" +"(https://nakedsecurity.sophos.com/2012/05/21/anatomy-of-a-security-hole-the-" +"break-that-broke-sudo)" +msgstr "" + +#: src/why-rust/an-example-in-c.md:75 +msgid "" +"_How is Rust any better here?_ \n" +"Safe Rust makes all of these bugs impossible:" +msgstr "" + +#: src/why-rust/an-example-in-c.md:78 +msgid "Assignments inside an `if` clause are not supported." +msgstr "" + +#: src/why-rust/an-example-in-c.md:79 +msgid "Format strings are checked at compile-time." +msgstr "" + +#: src/why-rust/an-example-in-c.md:80 +msgid "Resources are freed at the end of scope via the `Drop` trait." +msgstr "" + +#: src/why-rust/an-example-in-c.md:81 +msgid "All `if` clauses require braces." +msgstr "" + +#: src/why-rust/an-example-in-c.md:82 +msgid "" +"`match` (as the Rust equivalent to `switch`) does not fall-through, hence " +"you can't accidentally forget a `break`." +msgstr "" + +#: src/why-rust/an-example-in-c.md:83 +msgid "Buffer slices carry their size and don't rely on a NUL terminator." +msgstr "" + +#: src/why-rust/an-example-in-c.md:84 +msgid "" +"Heap-allocated memory is freed via the `Drop` trait when the corresponding " +"`Box` leaves the scope." +msgstr "" + +#: src/why-rust/an-example-in-c.md:85 +msgid "" +"Out-of-bounds accesses cause a panic or can be checked via the `get` method " +"of a slice." +msgstr "" + +#: src/why-rust/an-example-in-c.md:86 +msgid "`match` mandates that all cases are handled." +msgstr "" + +#: src/why-rust/an-example-in-c.md:87 +msgid "" +"Fallible Rust functions return `Result` values that need to be unwrapped and " +"thereby checked for success. Additionally, the compiler emits a warning if " +"you miss to check the return value of a function marked with `#[must_use]`." +msgstr "" + #: src/why-rust/compile-time.md:3 msgid "Static memory management at compile time:" msgstr "" @@ -2796,31 +2930,11 @@ msgid "" "amount of `#` on either side of the quotes:" msgstr "" -#: src/basic-syntax/scalar-types.md:27 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" println!(r#\"link\"#);\n" -" println!(\"link\");\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/scalar-types.md:34 +#: src/basic-syntax/scalar-types.md:35 msgid "Byte strings allow you to create a `&[u8]` value directly:" msgstr "" -#: src/basic-syntax/scalar-types.md:36 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" println!(\"{:?}\", b\"abc\");\n" -" println!(\"{:?}\", &[97, 98, 99]);\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/scalar-types.md:43 +#: src/basic-syntax/scalar-types.md:45 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 " @@ -2855,37 +2969,15 @@ msgstr "" msgid "Array assignment and access:" msgstr "" -#: src/basic-syntax/compound-types.md:10 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut a: [i8; 10] = [42; 10];\n" -" a[5] = 0;\n" -" println!(\"a: {:?}\", a);\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/compound-types.md:18 +#: src/basic-syntax/compound-types.md:19 msgid "Tuple assignment and access:" msgstr "" -#: src/basic-syntax/compound-types.md:20 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let t: (i8, bool) = (7, true);\n" -" println!(\"1st index: {}\", t.0);\n" -" println!(\"2nd index: {}\", t.1);\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/compound-types.md:32 +#: src/basic-syntax/compound-types.md:34 msgid "Arrays:" msgstr "" -#: src/basic-syntax/compound-types.md:34 +#: src/basic-syntax/compound-types.md:36 msgid "" "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 " @@ -2893,43 +2985,44 @@ msgid "" "different types." msgstr "" -#: src/basic-syntax/compound-types.md:38 +#: src/basic-syntax/compound-types.md:40 msgid "We can use literals to assign values to arrays." msgstr "" -#: src/basic-syntax/compound-types.md:40 +#: src/basic-syntax/compound-types.md:42 msgid "" -"In the main function, the print statement asks for the debug implementation " -"with the `?` format parameter: `{}` gives the default output, `{:?}` gives " -"the debug output. We could also have used `{a}` and `{a:?}` without " -"specifying the value after the format string." +"The `println!` macro asks for the debug implementation with the `?` format " +"parameter: `{}` gives the default output, `{:?}` gives the debug output. " +"Types such as integers and strings implement the default output, but arrays " +"only implement the debug output. This means that we must use debug output " +"here." msgstr "" -#: src/basic-syntax/compound-types.md:45 +#: src/basic-syntax/compound-types.md:47 msgid "" "Adding `#`, eg `{a:#?}`, invokes a \"pretty printing\" format, which can be " "easier to read." msgstr "" -#: src/basic-syntax/compound-types.md:47 +#: src/basic-syntax/compound-types.md:49 msgid "Tuples:" msgstr "" -#: src/basic-syntax/compound-types.md:49 +#: src/basic-syntax/compound-types.md:51 msgid "Like arrays, tuples have a fixed length." msgstr "" -#: src/basic-syntax/compound-types.md:51 +#: src/basic-syntax/compound-types.md:53 msgid "Tuples group together values of different types into a compound type." msgstr "" -#: src/basic-syntax/compound-types.md:53 +#: src/basic-syntax/compound-types.md:55 msgid "" "Fields of a tuple can be accessed by the period and the index of the value, " "e.g. `t.0`, `t.1`." msgstr "" -#: src/basic-syntax/compound-types.md:55 +#: src/basic-syntax/compound-types.md:57 msgid "" "The empty tuple `()` is also known as the \"unit type\". It is both a type, " "and the only valid value of that type - that is to say both the type and its " @@ -2937,7 +3030,7 @@ msgid "" "function or expression has no return value, as we'll see in a future slide. " msgstr "" -#: src/basic-syntax/compound-types.md:59 +#: src/basic-syntax/compound-types.md:61 msgid "" "You can think of it as `void` that can be familiar to you from other " "programming languages." @@ -2947,41 +3040,29 @@ msgstr "" msgid "Like C++, Rust has references:" msgstr "" -#: src/basic-syntax/references.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut x: i32 = 10;\n" -" let ref_x: &mut i32 = &mut x;\n" -" *ref_x = 20;\n" -" println!(\"x: {x}\");\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/references.md:14 +#: src/basic-syntax/references.md:15 msgid "Some notes:" msgstr "" -#: src/basic-syntax/references.md:16 +#: src/basic-syntax/references.md:17 msgid "" "We must dereference `ref_x` when assigning to it, similar to C and C++ " "pointers." msgstr "" -#: src/basic-syntax/references.md:17 +#: src/basic-syntax/references.md:18 msgid "" "Rust will auto-dereference in some cases, in particular when invoking " "methods (try `ref_x.count_ones()`)." msgstr "" -#: src/basic-syntax/references.md:19 +#: src/basic-syntax/references.md:20 msgid "" "References that are declared as `mut` can be bound to different values over " "their lifetime." msgstr "" -#: src/basic-syntax/references.md:25 +#: src/basic-syntax/references.md:26 msgid "" "Be sure to note the difference between `let mut ref_x: &i32` and `let ref_x: " "&mut i32`. The first one represents a mutable reference which can be bound " @@ -2993,31 +3074,17 @@ msgstr "" msgid "Rust will statically forbid dangling references:" msgstr "" -#: src/basic-syntax/references-dangling.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"fn main() {\n" -" let ref_x: &i32;\n" -" {\n" -" let x: i32 = 10;\n" -" ref_x = &x;\n" -" }\n" -" println!(\"ref_x: {ref_x}\");\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/references-dangling.md:16 +#: src/basic-syntax/references-dangling.md:17 msgid "A reference is said to \"borrow\" the value it refers to." msgstr "" -#: src/basic-syntax/references-dangling.md:17 +#: src/basic-syntax/references-dangling.md:18 msgid "" "Rust is tracking the lifetimes of all references to ensure they live long " "enough." msgstr "" -#: src/basic-syntax/references-dangling.md:19 +#: src/basic-syntax/references-dangling.md:20 msgid "We will talk more about borrowing when we get to ownership." msgstr "" @@ -3025,66 +3092,52 @@ msgstr "" msgid "A slice gives you a view into a larger collection:" msgstr "" -#: src/basic-syntax/slices.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\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:16 +#: src/basic-syntax/slices.md:17 msgid "Slices borrow data from the sliced type." msgstr "" -#: src/basic-syntax/slices.md:17 +#: src/basic-syntax/slices.md:18 msgid "Question: What happens if you modify `a[3]` right before printing `s`?" msgstr "" -#: src/basic-syntax/slices.md:21 +#: src/basic-syntax/slices.md:22 msgid "" "We create a slice by borrowing `a` and specifying the starting and ending " "indexes in brackets." msgstr "" -#: src/basic-syntax/slices.md:23 +#: src/basic-syntax/slices.md:24 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:25 +#: src/basic-syntax/slices.md:26 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:27 +#: src/basic-syntax/slices.md:28 msgid "" "To easily create a slice of the full array, we can therefore use `&a[..]`." msgstr "" -#: src/basic-syntax/slices.md:29 +#: src/basic-syntax/slices.md:30 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:31 +#: src/basic-syntax/slices.md:32 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:33 +#: src/basic-syntax/slices.md:34 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` at " @@ -3178,62 +3231,31 @@ msgid "" "Fizz_buzz) interview question:" msgstr "" -#: src/basic-syntax/functions.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" print_fizzbuzz_to(20);\n" -"}\n" -"\n" -"fn is_divisible(n: u32, divisor: u32) -> bool {\n" -" if divisor == 0 {\n" -" return false;\n" -" }\n" -" n % divisor == 0\n" -"}\n" -"\n" -"fn fizzbuzz(n: u32) -> String {\n" -" let fizz = if is_divisible(n, 3) { \"fizz\" } else { \"\" };\n" -" let buzz = if is_divisible(n, 5) { \"buzz\" } else { \"\" };\n" -" if fizz.is_empty() && buzz.is_empty() {\n" -" return format!(\"{n}\");\n" -" }\n" -" format!(\"{fizz}{buzz}\")\n" -"}\n" -"\n" -"fn print_fizzbuzz_to(n: u32) {\n" -" for i in 1..=n {\n" -" println!(\"{}\", fizzbuzz(i));\n" -" }\n" -"}\n" -"```" -msgstr "" - -#: src/basic-syntax/functions.md:35 +#: src/basic-syntax/functions.md:36 msgid "" "We refer in `main` to a function written below. Neither forward declarations " "nor headers are necessary. " msgstr "" -#: src/basic-syntax/functions.md:36 +#: src/basic-syntax/functions.md:37 msgid "" "Declaration parameters are followed by a type (the reverse of some " "programming languages), then a return type." msgstr "" -#: src/basic-syntax/functions.md:37 +#: src/basic-syntax/functions.md:38 msgid "" "The last expression in a function body (or any block) becomes the return " "value. Simply omit the `;` at the end of the expression." msgstr "" -#: src/basic-syntax/functions.md:38 +#: src/basic-syntax/functions.md:39 msgid "" "Some functions have no return value, and return the 'unit type', `()`. The " "compiler will infer this if the `-> ()` return type is omitted." msgstr "" -#: src/basic-syntax/functions.md:39 +#: src/basic-syntax/functions.md:40 msgid "" "The range expression in the `for` loop in `print_fizzbuzz_to()` contains " "`=n`, which causes it to include the upper bound." @@ -3251,6 +3273,11 @@ msgid "" "argument.\n" "///\n" "/// If the second argument is zero, the result is false.\n" +"///\n" +"/// # Example\n" +"/// ```\n" +"/// assert!(is_divisible_by(42, 2));\n" +"/// ```\n" "fn is_divisible_by(lhs: u32, rhs: u32) -> bool {\n" " if rhs == 0 {\n" " return false; // Corner case, early return\n" @@ -3261,33 +3288,34 @@ msgid "" "```" msgstr "" -#: src/basic-syntax/rustdoc.md:17 +#: src/basic-syntax/rustdoc.md:22 msgid "" "The contents are treated as Markdown. All published Rust library crates are " "automatically documented at [`docs.rs`](https://docs.rs) using the [rustdoc]" "(https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html) tool. It is " -"idiomatic to document all public items in an API using this pattern." +"idiomatic to document all public items in an API using this pattern. Code " +"snippets can document usage and will be used as unit tests." msgstr "" -#: src/basic-syntax/rustdoc.md:24 +#: src/basic-syntax/rustdoc.md:30 msgid "" "Show students the generated docs for the `rand` crate at [`docs.rs/rand`]" "(https://docs.rs/rand)." msgstr "" -#: src/basic-syntax/rustdoc.md:27 +#: src/basic-syntax/rustdoc.md:33 msgid "" "This course does not include rustdoc on slides, just to save space, but in " "real code they should be present." msgstr "" -#: src/basic-syntax/rustdoc.md:30 +#: src/basic-syntax/rustdoc.md:36 msgid "" "Inner doc comments are discussed later (in the page on modules) and need not " "be addressed here." msgstr "" -#: src/basic-syntax/rustdoc.md:33 +#: src/basic-syntax/rustdoc.md:39 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](../" @@ -3337,15 +3365,6 @@ msgstr "" msgid "Add a static method called `Rectangle::new` and call this from `main`:" msgstr "" -#: src/basic-syntax/methods.md:36 -msgid "" -"```rust,editable,compile_fail\n" -"fn new(width: u32, height: u32) -> Rectangle {\n" -" Rectangle { width, height }\n" -"}\n" -"```" -msgstr "" - #: src/basic-syntax/methods.md:42 msgid "" "While _technically_, Rust does not have custom constructors, static methods " @@ -3454,14 +3473,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 @@ -3471,23 +3488,7 @@ msgid "" "implicit_conversion)). You can see this in a program like this:" msgstr "" -#: src/exercises/day-1/implicit-conversions.md:6 -msgid "" -"```rust,editable,compile_fail\n" -"fn multiply(x: i16, y: i16) -> i16 {\n" -" x * y\n" -"}\n" -"\n" -"fn main() {\n" -" let x: i8 = 15;\n" -" let y: i16 = 1000;\n" -"\n" -" println!(\"{x} * {y} = {}\", multiply(x, y));\n" -"}\n" -"```" -msgstr "" - -#: src/exercises/day-1/implicit-conversions.md:19 +#: src/exercises/day-1/implicit-conversions.md:20 msgid "" "The Rust integer types all implement the [`From`](https://doc.rust-lang." "org/std/convert/trait.From.html) and [`Into`](https://doc.rust-lang.org/" @@ -3497,7 +3498,7 @@ msgid "" "expresses that it can be converted into another type." msgstr "" -#: src/exercises/day-1/implicit-conversions.md:25 +#: src/exercises/day-1/implicit-conversions.md:26 msgid "" "The standard library has an implementation of `From for i16`, which " "means that we can convert a variable `x` of type `i8` to an `i16` by " @@ -3506,22 +3507,22 @@ msgid "" "i8`." msgstr "" -#: src/exercises/day-1/implicit-conversions.md:30 +#: src/exercises/day-1/implicit-conversions.md:31 msgid "" "The same applies for your own `From` implementations for your own types, so " "it is sufficient to only implement `From` to get a respective `Into` " "implementation automatically." msgstr "" -#: src/exercises/day-1/implicit-conversions.md:33 +#: src/exercises/day-1/implicit-conversions.md:34 msgid "Execute the above program and look at the compiler error." msgstr "" -#: src/exercises/day-1/implicit-conversions.md:35 +#: src/exercises/day-1/implicit-conversions.md:36 msgid "Update the code above to use `into()` to do the conversion." msgstr "" -#: src/exercises/day-1/implicit-conversions.md:37 +#: src/exercises/day-1/implicit-conversions.md:38 msgid "" "Change the types of `x` and `y` to other things (such as `f32`, `bool`, " "`i128`) to see which types you can convert to which other types. Try " @@ -3539,42 +3540,25 @@ msgstr "" msgid "We saw that an array can be declared like this:" msgstr "" -#: src/exercises/day-1/for-loops.md:5 -msgid "" -"```rust\n" -"let array = [10, 20, 30];\n" -"```" -msgstr "" - #: src/exercises/day-1/for-loops.md:9 msgid "" "You can print such an array by asking for its debug representation with `{:?}" "`:" msgstr "" -#: src/exercises/day-1/for-loops.md:11 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let array = [10, 20, 30];\n" -" println!(\"array: {array:?}\");\n" -"}\n" -"```" -msgstr "" - -#: src/exercises/day-1/for-loops.md:18 +#: src/exercises/day-1/for-loops.md:19 msgid "" "Rust lets you iterate over things like arrays and ranges using the `for` " "keyword:" msgstr "" -#: src/exercises/day-1/for-loops.md:21 +#: src/exercises/day-1/for-loops.md:22 msgid "" "```rust,editable\n" "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" @@ -3588,33 +3572,24 @@ msgid "" "```" msgstr "" -#: src/exercises/day-1/for-loops.md:38 +#: src/exercises/day-1/for-loops.md:39 msgid "" "Use the above to write a function `pretty_print` which pretty-print a matrix " "and a function `transpose` which will transpose a matrix (turn rows into " "columns):" msgstr "" -#: src/exercises/day-1/for-loops.md:41 -msgid "" -"```bob\n" -" ⎛⎡1 2 3⎤⎞ ⎡1 4 7⎤\n" -"\"transpose\"⎜⎢4 5 6⎥⎟ \"==\"⎢2 5 8⎥\n" -" ⎝⎣7 8 9⎦⎠ ⎣3 6 9⎦\n" -"```" -msgstr "" - -#: src/exercises/day-1/for-loops.md:47 +#: src/exercises/day-1/for-loops.md:49 msgid "Hard-code both functions to operate on 3 × 3 matrices." msgstr "" -#: src/exercises/day-1/for-loops.md:49 +#: src/exercises/day-1/for-loops.md:51 msgid "" "Copy the code below to and implement the " "functions:" msgstr "" -#: src/exercises/day-1/for-loops.md:52 +#: src/exercises/day-1/for-loops.md:54 msgid "" "```rust,should_panic\n" "// TODO: remove this when you're done with your implementation.\n" @@ -3645,311 +3620,421 @@ msgid "" "```" msgstr "" -#: src/exercises/day-1/for-loops.md:80 +#: src/exercises/day-1/for-loops.md:82 msgid "Bonus Question" msgstr "" -#: src/exercises/day-1/for-loops.md:82 +#: src/exercises/day-1/for-loops.md:84 msgid "" "Could you use `&[i32]` slices instead of hard-coded 3 × 3 matrices for your " "argument and return types? Something like `&[&[i32]]` for a two-dimensional " "slice-of-slices. Why or why not?" msgstr "" -#: src/exercises/day-1/for-loops.md:87 +#: src/exercises/day-1/for-loops.md:89 msgid "" "See the [`ndarray` crate](https://docs.rs/ndarray/) for a production quality " "implementation." msgstr "" -#: src/exercises/day-1/for-loops.md:92 +#: src/exercises/day-1/for-loops.md:94 msgid "" "The solution and the answer to the bonus section are available in the " "[Solution](solutions-morning.md#arrays-and-for-loops) section." msgstr "" -#: src/basic-syntax/variables.md:3 +#: src/exercises/day-1/for-loops.md:97 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 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let x: i32 = 10;\n" -" println!(\"x: {x}\");\n" -" // x = 20;\n" -" // println!(\"x: {x}\");\n" -"}\n" -"```" +#: src/exercises/day-1/for-loops.md:100 +msgid "Without the `&`..." msgstr "" -#: src/basic-syntax/variables.md:17 +#: src/exercises/day-1/for-loops.md:101 msgid "" -"Due to type inference the `i32` is optional. We will gradually show the " -"types less and less as the course progresses." +"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:18 +#: src/exercises/day-1/for-loops.md:104 msgid "" -"Note that since `println!` is a macro, `x` is not moved, even using the " -"function like syntax of `println!(\"x: {}\", x)`" +"An implicit array copy would have occurred. Since `i32` is a copy type, " +"then `[i32; 3]` is also a copy type." 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.md:3 +msgid "" +"As we have seen, `if` is an expression in Rust. It is used to conditionally " +"evaluate one of two blocks, but the blocks can have a value which then " +"becomes the value of the `if` expression. Other control flow expressions " +"work similarly in Rust." msgstr "" -#: src/basic-syntax/type-inference.md:5 +#: src/control-flow/blocks.md:3 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" -"```" +"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:26 +#: src/control-flow/blocks.md:27 msgid "" -"This slide demonstrates how the Rust compiler infers types based on " -"constraints given by variable declarations and usages." +"If the last expression ends with `;`, then the resulting value and type is " +"`()`." msgstr "" -#: src/basic-syntax/type-inference.md:28 +#: src/control-flow/blocks.md:29 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." +"The same rule is used for functions: the value of the function body is the " +"return value:" msgstr "" -#: src/basic-syntax/type-inference.md:32 -msgid "" -"The following code tells the compiler to copy into a certain generic " -"container without the code ever explicitly specifying the contained type, " -"using `_` as a placeholder:" +#: src/control-flow/blocks.md:45 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/type-inference.md:34 +#: src/control-flow/blocks.md:46 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut v = Vec::new();\n" -" v.push((10, false));\n" -" v.push((20, true));\n" -" println!(\"v: {v:?}\");\n" -"\n" -" let vv = v.iter().collect::>();\n" -" println!(\"vv: {vv:?}\");\n" -"}\n" -"```" +"The point of this slide is to show that blocks have a type and value in " +"Rust. " msgstr "" -#: src/basic-syntax/type-inference.md:46 +#: src/control-flow/blocks.md:47 msgid "" -"[`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." +"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:1 -msgid "Static and Constant Variables" +#: src/control-flow/if-expressions.md:1 +msgid "`if` expressions" msgstr "" -#: src/basic-syntax/static-and-const.md:3 +#: src/control-flow/if-expressions.md:3 msgid "" -"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. " +"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:6 -msgid "`const`" +#: 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/basic-syntax/static-and-const.md:8 +#: src/control-flow/if-expressions.md:35 msgid "" -"Constant variables are evaluated at compile time and their values are " -"inlined wherever they are used:" +"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: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 digest = compute_digest(\"Hello\");\n" -" println!(\"Digest: {digest:?}\");\n" -"}\n" -"```" +#: src/control-flow/for-expressions.md:1 +msgid "`for` loops" msgstr "" -#: src/basic-syntax/static-and-const.md:29 +#: src/control-flow/for-expressions.md:3 msgid "" -"According to the [Rust RFC Book](https://rust-lang.github.io/rfcs/0246-const-" -"vs-static.html) these are inlined upon use." +"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:31 -msgid "" -"Only functions marked `const` can be called at compile time to generate " -"`const` values. `const` functions can however be called at runtime." +#: src/control-flow/for-expressions.md:22 +msgid "You can use `break` and `continue` here as usual." msgstr "" -#: src/basic-syntax/static-and-const.md:33 -msgid "`static`" +#: src/control-flow/for-expressions.md:26 +msgid "Index iteration is not a special syntax in Rust for just that case." msgstr "" -#: 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:" +#: src/control-flow/for-expressions.md:27 +msgid "`(0..10)` is a range that implements an `Iterator` trait. " msgstr "" -#: src/basic-syntax/static-and-const.md:37 +#: src/control-flow/for-expressions.md:28 msgid "" -"```rust,editable\n" -"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" -"\n" -"fn main() {\n" -" println!(\"{BANNER}\");\n" -"}\n" -"```" +"`step_by` is a method that returns another `Iterator` that skips every other " +"element. " msgstr "" -#: src/basic-syntax/static-and-const.md:45 +#: src/control-flow/for-expressions.md:29 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. When a " -"globally-scoped value does not have a reason to need object identity, " -"`const` is generally preferred." +"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/control-flow/while-expressions.md:1 +msgid "`while` loops" msgstr "" -#: src/basic-syntax/static-and-const.md:49 +#: src/control-flow/while-expressions.md:3 msgid "" -"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." +"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/static-and-const.md:57 -msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." +#: src/control-flow/break-continue.md:1 +msgid "`break` and `continue`" msgstr "" -#: src/basic-syntax/static-and-const.md:58 +#: src/control-flow/break-continue.md:3 msgid "" -"`static`, on the other hand, is much more similar to a `const` or mutable " -"global variable in C++." +"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/static-and-const.md:59 +#: src/control-flow/break-continue.md:4 msgid "" -"`static` provides object identity: an address in memory and state as " -"required by types with interior mutability such as `Mutex`." +"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/static-and-const.md:60 +#: src/control-flow/break-continue.md:7 msgid "" -"It isn't super common that one would need a runtime evaluated constant, but " -"it is helpful and safer than using a static." +"Both `continue` and `break` can optionally take a label argument which is " +"used to break out of nested loops:" msgstr "" -#: src/basic-syntax/static-and-const.md:61 -msgid "`thread_local` data can be created with the macro `std::thread_local`." +#: src/control-flow/break-continue.md:29 +msgid "" +"In this case we break the outer loop after 3 iterations of the inner loop." msgstr "" -#: src/basic-syntax/static-and-const.md:63 -msgid "Properties table:" +#: src/control-flow/loop-expressions.md:1 +msgid "`loop` expressions" msgstr "" -#: src/basic-syntax/static-and-const.md:65 -msgid "Property" -msgstr "" +#: src/control-flow/loop-expressions.md:3 +msgid "" +"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/control-flow/loop-expressions.md:6 +msgid "Here you must either `break` or `return` to stop the loop:" +msgstr "" + +#: src/control-flow/loop-expressions.md:28 +msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." +msgstr "" + +#: src/control-flow/loop-expressions.md:29 +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/basic-syntax/variables.md:3 +msgid "" +"Rust provides type safety via static typing. Variable bindings are immutable " +"by default:" +msgstr "" + +#: src/basic-syntax/variables.md:18 +msgid "" +"Due to type inference the `i32` is optional. We will gradually show the " +"types less and less as the course progresses." +msgstr "" + +#: src/basic-syntax/type-inference.md:3 +msgid "Rust will look at how the variable is _used_ to determine the type:" +msgstr "" + +#: src/basic-syntax/type-inference.md:27 +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:29 +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/basic-syntax/type-inference.md:33 +msgid "" +"The following code tells the compiler to copy into a certain generic " +"container without the code ever explicitly specifying the contained type, " +"using `_` as a placeholder:" +msgstr "" + +#: src/basic-syntax/type-inference.md:48 +msgid "" +"[`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/basic-syntax/static-and-const.md:1 +msgid "Static and Constant Variables" +msgstr "" + +#: src/basic-syntax/static-and-const.md:3 +msgid "" +"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/basic-syntax/static-and-const.md:6 +msgid "`const`" +msgstr "" + +#: src/basic-syntax/static-and-const.md:8 +msgid "" +"Constant variables are evaluated at compile time and their values are " +"inlined wherever they are used:" +msgstr "" + +#: src/basic-syntax/static-and-const.md:30 +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/basic-syntax/static-and-const.md:32 +msgid "" +"Only functions marked `const` can be called at compile time to generate " +"`const` values. `const` functions can however be called at runtime." +msgstr "" + +#: src/basic-syntax/static-and-const.md:34 +msgid "`static`" +msgstr "" + +#: src/basic-syntax/static-and-const.md:36 +msgid "" +"Static variables will live during the whole execution of the program, and " +"therefore will not move:" +msgstr "" + +#: src/basic-syntax/static-and-const.md:38 +msgid "" +"```rust,editable\n" +"static BANNER: &str = \"Welcome to RustOS 3.14\";\n" +"\n" +"fn main() {\n" +" println!(\"{BANNER}\");\n" +"}\n" +"```" +msgstr "" + +#: src/basic-syntax/static-and-const.md:46 +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. When a " +"globally-scoped value does not have a reason to need object identity, " +"`const` is generally preferred." +msgstr "" + +#: src/basic-syntax/static-and-const.md:50 +msgid "" +"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/basic-syntax/static-and-const.md:65 +#: src/basic-syntax/static-and-const.md:58 +msgid "Mention that `const` behaves semantically similar to C++'s `constexpr`." +msgstr "" + +#: src/basic-syntax/static-and-const.md:59 +msgid "" +"`static`, on the other hand, is much more similar to a `const` or mutable " +"global variable in C++." +msgstr "" + +#: src/basic-syntax/static-and-const.md:60 +msgid "" +"`static` provides object identity: an address in memory and state as " +"required by types with interior mutability such as `Mutex`." +msgstr "" + +#: src/basic-syntax/static-and-const.md:61 +msgid "" +"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/basic-syntax/static-and-const.md:62 +msgid "`thread_local` data can be created with the macro `std::thread_local`." +msgstr "" + +#: src/basic-syntax/static-and-const.md:64 +msgid "Properties table:" +msgstr "" + +#: src/basic-syntax/static-and-const.md:66 +msgid "Property" +msgstr "" + +#: src/basic-syntax/static-and-const.md:66 msgid "Static" msgstr "" -#: src/basic-syntax/static-and-const.md:65 +#: src/basic-syntax/static-and-const.md:66 msgid "Constant" msgstr "" -#: src/basic-syntax/static-and-const.md:67 +#: src/basic-syntax/static-and-const.md:68 msgid "Has an address in memory" msgstr "" -#: src/basic-syntax/static-and-const.md:67 #: src/basic-syntax/static-and-const.md:68 -#: src/basic-syntax/static-and-const.md:70 +#: src/basic-syntax/static-and-const.md:69 #: src/basic-syntax/static-and-const.md:71 +#: src/basic-syntax/static-and-const.md:72 msgid "Yes" msgstr "" -#: src/basic-syntax/static-and-const.md:67 +#: src/basic-syntax/static-and-const.md:68 msgid "No (inlined)" msgstr "" -#: src/basic-syntax/static-and-const.md:68 +#: src/basic-syntax/static-and-const.md:69 msgid "Lives for the entire duration of the program" msgstr "" -#: src/basic-syntax/static-and-const.md:68 #: src/basic-syntax/static-and-const.md:69 -#: src/basic-syntax/static-and-const.md:71 +#: src/basic-syntax/static-and-const.md:70 +#: src/basic-syntax/static-and-const.md:72 msgid "No" msgstr "" -#: src/basic-syntax/static-and-const.md:69 +#: src/basic-syntax/static-and-const.md:70 msgid "Can be mutable" msgstr "" -#: src/basic-syntax/static-and-const.md:69 +#: src/basic-syntax/static-and-const.md:70 msgid "Yes (unsafe)" msgstr "" -#: src/basic-syntax/static-and-const.md:70 +#: src/basic-syntax/static-and-const.md:71 msgid "Evaluated at compile time" msgstr "" -#: src/basic-syntax/static-and-const.md:70 +#: src/basic-syntax/static-and-const.md:71 msgid "Yes (initialised at compile time)" msgstr "" -#: src/basic-syntax/static-and-const.md:71 +#: src/basic-syntax/static-and-const.md:72 msgid "Inlined wherever it is used" msgstr "" @@ -4003,2123 +4088,2059 @@ msgid "" "does not change." msgstr "" -#: src/basic-syntax/scopes-shadowing.md:30 +#: src/enums.md:3 +msgid "" +"The `enum` keyword allows the creation of a type which has a few different " +"variants:" +msgstr "" + +#: 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" -" let a = 1;\n" -" let b = &a;\n" -" let a = a + 1;\n" -" println!(\"{a} {b}\");\n" +" println!(\"You got: {:?}\", flip_coin());\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, ..." +#: src/enums.md:36 +msgid "Enumerations allow you to collect a set of values under one type" msgstr "" -#: src/memory-management.md:6 +#: src/enums.md:37 msgid "" -"Full safety via automatic memory management at runtime: Java, Python, Go, " -"Haskell, ..." +"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.md:8 -msgid "Rust offers a new mix:" +#: src/enums.md:38 +msgid "This might be a good time to compare Structs and Enums:" msgstr "" -#: src/memory-management.md:10 +#: src/enums.md:39 msgid "" -"Full control _and_ safety via compile time enforcement of correct memory " -"management." -msgstr "" - -#: src/memory-management.md:13 -msgid "It does this with an explicit ownership concept." -msgstr "" - -#: src/memory-management.md:15 -msgid "First, let's refresh how memory management works." +"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/stack-vs-heap.md:1 -msgid "The Stack vs The Heap" +#: src/enums.md:40 +msgid "In both, associated functions are defined within an `impl` block." msgstr "" -#: src/memory-management/stack-vs-heap.md:3 -msgid "Stack: Continuous area of memory for local variables." +#: 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/stack-vs-heap.md:4 -msgid "Values have fixed sizes known at compile time." +#: 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/stack-vs-heap.md:5 -msgid "Extremely fast: just move a stack pointer." +#: 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/stack-vs-heap.md:6 -msgid "Easy to manage: follows function calls." +#: 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/stack-vs-heap.md:7 -msgid "Great memory locality." +#: 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/stack-vs-heap.md:9 -msgid "Heap: Storage of values outside of function calls." +#: 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/stack-vs-heap.md:10 -msgid "Values have dynamic sizes determined at runtime." +#: 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/stack-vs-heap.md:11 -msgid "Slightly slower than the stack: some book-keeping needed." +#: 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/stack-vs-heap.md:12 -msgid "No guarantee of memory locality." +#: src/enums/variant-payloads.md:40 +msgid "`match` inspects a hidden discriminant field in the `enum`." msgstr "" -#: src/memory-management/stack.md:1 -msgid "Stack and Heap Example" +#: src/enums/variant-payloads.md:41 +msgid "" +"It is possible to retrieve the discriminant by calling `std::mem::" +"discriminant()`" msgstr "" -#: src/memory-management/stack.md:3 +#: src/enums/variant-payloads.md:42 msgid "" -"Creating a `String` puts fixed-sized metadata on the stack and dynamically " -"sized data, the actual string, on the heap:" +"This is useful, for example, if implementing `PartialEq` for structs where " +"comparing field values doesn't affect equality." msgstr "" -#: src/memory-management/stack.md:6 +#: 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/enums/sizes.md:3 +msgid "" +"Rust enums are packed tightly, taking constraints due to alignment into " +"account:" +msgstr "" + +#: src/enums/sizes.md:5 msgid "" "```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" -" let s1 = String::from(\"Hello\");\n" +" dbg_size::();\n" "}\n" "```" msgstr "" -#: src/memory-management/stack.md:12 +#: src/enums/sizes.md:24 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" -"```" +"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." +"html)." msgstr "" -#: src/memory-management/stack.md:28 +#: src/enums/sizes.md:30 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." +"Internally Rust is using a field (discriminant) to keep track of the enum " +"variant." msgstr "" -#: src/memory-management/stack.md:30 +#: src/enums/sizes.md:32 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)" +"You can control the discriminant if needed (e.g., for compatibility with C):" msgstr "" -#: src/memory-management/stack.md:32 +#: src/enums/sizes.md:50 msgid "" -"We can inspect the memory layout with `unsafe` code. However, you should " -"point out that this is rightfully unsafe!" +"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " +"bytes." msgstr "" -#: src/memory-management/stack.md:34 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut s1 = String::from(\"Hello\");\n" -" s1.push(' ');\n" -" s1.push_str(\"world\");\n" -" // DON'T DO THIS AT HOME! For educational purposes only.\n" -" // String provides no guarantees about its layout, so this could lead " -"to\n" -" // undefined behavior.\n" -" unsafe {\n" -" let (ptr, capacity, len): (usize, usize, usize) = std::mem::" -"transmute(s1);\n" -" println!(\"ptr = {ptr:#x}, len = {len}, capacity = {capacity}\");\n" -" }\n" -"}\n" -"```" +#: src/enums/sizes.md:54 +msgid "Try out other types such as" msgstr "" -#: src/memory-management/manual.md:3 -msgid "You allocate and deallocate heap memory yourself." +#: src/enums/sizes.md:56 +msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," msgstr "" -#: src/memory-management/manual.md:5 +#: src/enums/sizes.md:57 msgid "" -"If not done with care, this can lead to crashes, bugs, security " -"vulnerabilities, and memory leaks." -msgstr "" - -#: src/memory-management/manual.md:7 -msgid "C Example" +"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " +"see below)," msgstr "" -#: src/memory-management/manual.md:9 -msgid "You must call `free` on every pointer you allocate with `malloc`:" +#: src/enums/sizes.md:58 +msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," msgstr "" -#: src/memory-management/manual.md:11 +#: src/enums/sizes.md:59 msgid "" -"```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" -"```" +"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " +"optimization, see below)." msgstr "" -#: src/memory-management/manual.md:21 -msgid "" -"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." +#: src/enums/sizes.md:61 src/memory-management/stack.md:32 +msgid "More to Explore" msgstr "" -#: src/memory-management/scope-based.md:3 +#: src/enums/sizes.md:63 msgid "" -"Constructors and destructors let you hook into the lifetime of an object." +"Rust has several optimizations it can employ to make enums take up less " +"space." msgstr "" -#: src/memory-management/scope-based.md:5 +#: src/enums/sizes.md:65 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." +"Niche optimization: Rust will merge unused bit patterns for the enum " +"discriminant." msgstr "" -#: src/memory-management/scope-based.md:9 +#: src/enums/sizes.md:68 msgid "" -"This is often called _resource acquisition is initialization_ (RAII) and " -"gives you smart pointers." +"Null pointer optimization: For [some types](https://doc.rust-lang.org/std/" +"option/#representation), Rust guarantees that `size_of::()` equals " +"`size_of::>()`." msgstr "" -#: src/memory-management/scope-based.md:12 -msgid "C++ Example" +#: src/enums/sizes.md:72 +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/memory-management/scope-based.md:14 +#: src/enums/sizes.md:109 msgid "" -"```c++\n" -"void say_hello(std::unique_ptr person) {\n" -" std::cout << \"Hello \" << person->name << std::endl;\n" -"}\n" -"```" +"More complex example if you want to discuss what happens when we chain more " +"than 256 `Option`s together." msgstr "" -#: src/memory-management/scope-based.md:20 +#: src/control-flow/novel.md:3 msgid "" -"The `std::unique_ptr` object is allocated on the stack, and points to memory " -"allocated on the heap." +"Rust has a few control flow constructs which differ from other languages. " +"They are used for pattern matching:" msgstr "" -#: src/memory-management/scope-based.md:22 -msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." +#: src/control-flow/novel.md:6 src/control-flow/if-let-expressions.md:1 +msgid "`if let` expressions" msgstr "" -#: src/memory-management/scope-based.md:23 -msgid "The destructor frees the `Person` object it points to." +#: src/control-flow/novel.md:7 +#, fuzzy +msgid "`while let` expressions" +msgstr "عبارت while let" + +#: src/control-flow/novel.md:8 src/control-flow/match-expressions.md:1 +msgid "`match` expressions" msgstr "" -#: src/memory-management/scope-based.md:25 +#: src/control-flow/if-let-expressions.md:3 msgid "" -"Special move constructors are used when passing ownership to a function:" +"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/memory-management/scope-based.md:27 +#: src/control-flow/if-let-expressions.md:7 msgid "" -"```c++\n" -"std::unique_ptr person = find_person(\"Carla\");\n" -"say_hello(std::move(person));\n" +"```rust,editable\n" +"fn main() {\n" +" let arg = std::env::args().next();\n" +" if let Some(value) = arg {\n" +" println!(\"Program name: {value}\");\n" +" } else {\n" +" println!(\"Missing name?\");\n" +" }\n" +"}\n" "```" msgstr "" -#: src/memory-management/garbage-collection.md:1 -msgid "Automatic Memory Management" +#: src/control-flow/if-let-expressions.md:18 +#: src/control-flow/while-let-expressions.md:22 +#: src/control-flow/match-expressions.md:23 +msgid "" +"See [pattern matching](../pattern-matching.md) for more details on patterns " +"in Rust." msgstr "" -#: src/memory-management/garbage-collection.md:3 +#: src/control-flow/if-let-expressions.md:23 msgid "" -"An alternative to manual and scope-based memory management is automatic " -"memory management:" +"Unlike `match`, `if let` does not have to cover all branches. This can make " +"it more concise than `match`." msgstr "" -#: src/memory-management/garbage-collection.md:6 -msgid "The programmer never allocates or deallocates memory explicitly." +#: src/control-flow/if-let-expressions.md:24 +msgid "A common usage is handling `Some` values when working with `Option`." msgstr "" -#: src/memory-management/garbage-collection.md:7 +#: src/control-flow/if-let-expressions.md:25 msgid "" -"A garbage collector finds unused memory and deallocates it for the " -"programmer." -msgstr "" - -#: src/memory-management/garbage-collection.md:9 -msgid "Java Example" -msgstr "" - -#: src/memory-management/garbage-collection.md:11 -msgid "The `person` object is not deallocated after `sayHello` returns:" +"Unlike `match`, `if let` does not support guard clauses for pattern matching." msgstr "" -#: src/memory-management/garbage-collection.md:13 +#: src/control-flow/if-let-expressions.md:26 msgid "" -"```java\n" -"void sayHello(Person person) {\n" -" System.out.println(\"Hello \" + person.getName());\n" -"}\n" -"```" -msgstr "" - -#: src/memory-management/rust.md:1 -msgid "Memory Management in Rust" -msgstr "" - -#: src/memory-management/rust.md:3 -msgid "Memory management in Rust is a mix:" +"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/memory-management/rust.md:5 -msgid "Safe and correct like Java, but without a garbage collector." +#: src/control-flow/while-let-expressions.md:1 +msgid "`while let` loops" msgstr "" -#: src/memory-management/rust.md:6 +#: src/control-flow/while-let-expressions.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." -msgstr "" - -#: src/memory-management/rust.md:7 -msgid "Scope-based like C++, but the compiler enforces full adherence." +"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/memory-management/rust.md:8 +#: src/control-flow/while-let-expressions.md:18 msgid "" -"A Rust user can choose the right abstraction for the situation, some even " -"have no cost at runtime like C." -msgstr "" - -#: src/memory-management/rust.md:10 -msgid "Rust achieves this by modeling _ownership_ explicitly." +"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/memory-management/rust.md:14 +#: src/control-flow/while-let-expressions.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." +"Point out that the `while let` loop will keep going as long as the value " +"matches the pattern." msgstr "" -#: src/memory-management/rust.md:16 +#: src/control-flow/while-let-expressions.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." -msgstr "" - -#: src/memory-management/comparison.md:5 -msgid "Pros of Different Memory Management Techniques" -msgstr "" - -#: src/memory-management/comparison.md:7 src/memory-management/comparison.md:22 -msgid "Manual like C:" +"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/memory-management/comparison.md:8 src/memory-management/comparison.md:14 -#: src/memory-management/comparison.md:17 -msgid "No runtime overhead." +#: 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/memory-management/comparison.md:9 src/memory-management/comparison.md:26 -msgid "Automatic like Java:" +#: src/control-flow/match-expressions.md:7 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" match std::env::args().next().as_deref() {\n" +" Some(\"cat\") => println!(\"Will do cat things\"),\n" +" Some(\"ls\") => println!(\"Will ls some files\"),\n" +" Some(\"mv\") => println!(\"Let's move some files\"),\n" +" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" +" None => println!(\"Hmm, no program name?\"),\n" +" _ => println!(\"Unknown program name!\"),\n" +" }\n" +"}\n" +"```" msgstr "" -#: src/memory-management/comparison.md:10 -msgid "Fully automatic." +#: 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/memory-management/comparison.md:11 -#: src/memory-management/comparison.md:18 -msgid "Safe and correct." +#: src/control-flow/match-expressions.md:28 +msgid "Save the match expression to a variable and print it out." msgstr "" -#: src/memory-management/comparison.md:12 -#: src/memory-management/comparison.md:29 -msgid "Scope-based like C++:" +#: src/control-flow/match-expressions.md:29 +msgid "Remove `.as_deref()` and explain the error." msgstr "" -#: src/memory-management/comparison.md:13 -msgid "Partially automatic." +#: src/control-flow/match-expressions.md:30 +msgid "" +"`std::env::args().next()` returns an `Option`, but we cannot match " +"against `String`." msgstr "" -#: src/memory-management/comparison.md:15 -msgid "Compiler-enforced scope-based like Rust:" +#: src/control-flow/match-expressions.md:31 +msgid "" +"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " +"this turns `Option` into `Option<&str>`." msgstr "" -#: src/memory-management/comparison.md:16 -msgid "Enforced by compiler." +#: src/control-flow/match-expressions.md:32 +msgid "" +"We can now use pattern matching to match against the `&str` inside `Option`." msgstr "" -#: src/memory-management/comparison.md:20 -msgid "Cons of Different Memory Management Techniques" +#: src/pattern-matching.md:3 +msgid "" +"The `match` keyword lets you match a value against one or more _patterns_. " +"The comparisons are done from top to bottom and the first match wins." msgstr "" -#: src/memory-management/comparison.md:23 -msgid "Use-after-free." +#: src/pattern-matching.md:6 +msgid "The patterns can be simple values, similarly to `switch` in C and C++:" msgstr "" -#: src/memory-management/comparison.md:24 -msgid "Double-frees." +#: src/pattern-matching.md:8 +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" +"}\n" +"```" msgstr "" -#: src/memory-management/comparison.md:25 -msgid "Memory leaks." +#: src/pattern-matching.md:21 +msgid "The `_` pattern is a wildcard pattern which matches any value." msgstr "" -#: src/memory-management/comparison.md:27 -msgid "Garbage collection pauses." +#: src/pattern-matching.md:26 +msgid "" +"You might point out how some specific characters are being used when in a " +"pattern" msgstr "" -#: src/memory-management/comparison.md:28 -msgid "Destructor delays." +#: src/pattern-matching.md:27 +msgid "`|` as an `or`" msgstr "" -#: src/memory-management/comparison.md:30 -msgid "Complex, opt-in by programmer (on C++)." +#: src/pattern-matching.md:28 +msgid "`..` can expand as much as it needs to be" msgstr "" -#: src/memory-management/comparison.md:31 -msgid "Circular references can lead to memory leaks" +#: src/pattern-matching.md:29 +msgid "`1..=5` represents an inclusive range" msgstr "" -#: src/memory-management/comparison.md:32 -msgid "Potential runtime overhead" +#: src/pattern-matching.md:30 +msgid "`_` is a wild card" msgstr "" -#: src/memory-management/comparison.md:33 -msgid "Compiler-enforced and scope-based like Rust:" +#: src/pattern-matching.md:31 +msgid "" +"It can be useful to show how binding works, by for instance replacing a " +"wildcard character with a variable, or removing the quotes around `q`." msgstr "" -#: src/memory-management/comparison.md:34 -msgid "Some upfront complexity." +#: src/pattern-matching.md:32 +msgid "You can demonstrate matching on a reference." msgstr "" -#: src/memory-management/comparison.md:35 -msgid "Can reject valid programs." +#: src/pattern-matching.md:33 +msgid "" +"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.md:3 +#: src/pattern-matching/destructuring-enums.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:" +"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.md:6 +#: src/pattern-matching/destructuring-enums.md:6 msgid "" -"```rust,editable,compile_fail\n" -"struct Point(i32, i32);\n" +"```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" -" {\n" -" let p = Point(3, 4);\n" -" println!(\"x: {}\", p.0);\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" -" println!(\"y: {}\", p.1);\n" "}\n" "```" msgstr "" -#: src/ownership.md:18 +#: src/pattern-matching/destructuring-enums.md:29 msgid "" -"At the end of the scope, the variable is _dropped_ and the data is freed." +"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.md:19 -msgid "A destructor can run here to free up resources." +#: 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.md:20 -msgid "We say that the variable _owns_ the value." +#: src/pattern-matching/destructuring-enums.md:37 +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." msgstr "" -#: src/ownership/move-semantics.md:3 -msgid "An assignment will transfer _ownership_ between variables:" +#: src/pattern-matching/destructuring-structs.md:3 +msgid "You can also destructure `structs`:" msgstr "" -#: src/ownership/move-semantics.md:5 +#: src/pattern-matching/destructuring-structs.md:5 msgid "" "```rust,editable\n" +"struct Foo {\n" +" x: (u32, u32),\n" +" y: u32,\n" +"}\n" +"\n" +"#[rustfmt::skip]\n" "fn main() {\n" -" let s1: String = String::from(\"Hello!\");\n" -" let s2: String = s1;\n" -" println!(\"s2: {s2}\");\n" -" // println!(\"s1: {s1}\");\n" +" let foo = Foo { x: (1, 2), y: 3 };\n" +" match foo {\n" +" Foo { x: (1, b), y } => println!(\"x.0 = 1, b = {b}, y = {y}\"),\n" +" Foo { y: 2, x: i } => println!(\"y = 2, x = {i:?}\"),\n" +" Foo { y, .. } => println!(\"y = {y}, other fields were " +"ignored\"),\n" +" }\n" "}\n" "```" msgstr "" -#: src/ownership/move-semantics.md:14 -msgid "The assignment of `s1` to `s2` transfers ownership." -msgstr "" - -#: src/ownership/move-semantics.md:15 -msgid "When `s1` goes out of scope, nothing happens: it does not own anything." -msgstr "" - -#: src/ownership/move-semantics.md:16 -msgid "When `s2` goes out of scope, the string data is freed." +#: src/pattern-matching/destructuring-structs.md:23 +msgid "Change the literal values in `foo` to match with the other patterns." msgstr "" -#: src/ownership/move-semantics.md:17 -msgid "There is always _exactly_ one variable binding which owns a value." +#: 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/move-semantics.md:21 +#: src/pattern-matching/destructuring-structs.md:25 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!)." +"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/move-semantics.md:23 +#: src/pattern-matching/destructuring-arrays.md:3 msgid "" -"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." +"You can destructure arrays, tuples, and slices by matching on their elements:" msgstr "" -#: src/ownership/move-semantics.md:25 +#: src/pattern-matching/destructuring-arrays.md:5 msgid "" -"Simple values (such as integers) can be marked `Copy` (see later slides)." +"```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/move-semantics.md:27 -msgid "In Rust, clones are explicit (by using `clone`)." +#: src/pattern-matching/destructuring-arrays.md:21 +msgid "" +"Destructuring of slices of unknown length also works with patterns of fixed " +"length." msgstr "" -#: src/ownership/moved-strings-rust.md:3 +#: src/pattern-matching/destructuring-arrays.md:24 msgid "" "```rust,editable\n" "fn main() {\n" -" let s1: String = String::from(\"Rust\");\n" -" let s2: String = s1;\n" +" inspect(&[0, -2, 3]);\n" +" inspect(&[0, -2, 3, 4]);\n" +"}\n" +"\n" +"#[rustfmt::skip]\n" +"fn inspect(slice: &[i32]) {\n" +" println!(\"Tell me about {slice:?}\");\n" +" match slice {\n" +" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" +" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" +" _ => println!(\"All elements were ignored\"),\n" +" }\n" "}\n" "```" msgstr "" -#: src/ownership/moved-strings-rust.md:10 -msgid "The heap data from `s1` is reused for `s2`." -msgstr "" - -#: src/ownership/moved-strings-rust.md:11 -msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." +#: src/pattern-matching/destructuring-arrays.md:41 +msgid "Create a new pattern using `_` to represent an element. " msgstr "" -#: src/ownership/moved-strings-rust.md:13 -msgid "Before move to `s2`:" +#: src/pattern-matching/destructuring-arrays.md:42 +msgid "Add more values to the array." msgstr "" -#: src/ownership/moved-strings-rust.md:15 +#: src/pattern-matching/destructuring-arrays.md:43 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" -"```" +"Point out that how `..` will expand to account for different number of " +"elements." msgstr "" -#: src/ownership/moved-strings-rust.md:30 -msgid "After move to `s2`:" +#: src/pattern-matching/destructuring-arrays.md:44 +msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" msgstr "" -#: src/ownership/moved-strings-rust.md:32 +#: src/pattern-matching/match-guards.md:3 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:1 -msgid "Extra Work in Modern C++" -msgstr "" - -#: src/ownership/double-free-modern-cpp.md:3 -msgid "Modern C++ solves this differently:" +"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/double-free-modern-cpp.md:5 +#: src/pattern-matching/match-guards.md:6 msgid "" -"```c++\n" -"std::string s1 = \"Cpp\";\n" -"std::string s2 = s1; // Duplicate the data in s1.\n" +"```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/double-free-modern-cpp.md:10 +#: src/pattern-matching/match-guards.md:23 msgid "" -"The heap data from `s1` is duplicated and `s2` gets its own independent copy." +"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/double-free-modern-cpp.md:11 -msgid "When `s1` and `s2` go out of scope, they each free their own memory." +#: src/pattern-matching/match-guards.md:24 +msgid "" +"They are not the same as separate `if` expression inside of the match arm. " +"An `if` expression inside of the branch block (after `=>`) happens after the " +"match arm is selected. Failing the `if` condition inside of that block won't " +"result in other arms of the original `match` expression being considered." msgstr "" -#: src/ownership/double-free-modern-cpp.md:13 -msgid "Before copy-assignment:" +#: src/pattern-matching/match-guards.md:26 +msgid "You can use the variables defined in the pattern in your if expression." msgstr "" -#: src/ownership/double-free-modern-cpp.md:16 +#: src/pattern-matching/match-guards.md:27 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" -"```" +"The condition defined in the guard applies to every expression in a pattern " +"with an `|`." msgstr "" -#: src/ownership/double-free-modern-cpp.md:30 -msgid "After copy-assignment:" +#: src/exercises/day-1/afternoon.md:1 +msgid "Day 1: Afternoon Exercises" 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/exercises/day-1/afternoon.md:3 +msgid "We will look at two things:" msgstr "" -#: src/ownership/moves-function-calls.md:3 -msgid "" -"When you pass a value to a function, the value is assigned to the function " -"parameter. This transfers ownership:" -msgstr "" +#: src/exercises/day-1/afternoon.md:5 +#, fuzzy +msgid "The Luhn algorithm," +msgstr "الگوریتم Luhn" -#: src/ownership/moves-function-calls.md:6 -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" -"}\n" -"```" +#: src/exercises/day-1/afternoon.md:7 +msgid "An exercise on pattern matching." msgstr "" -#: src/ownership/moves-function-calls.md:20 +#: 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 "" -"With the first call to `say_hello`, `main` gives up ownership of `name`. " -"Afterwards, `name` cannot be used anymore within `main`." +"After looking at the exercises, you can look at the [solutions](solutions-" +"afternoon.md) provided." msgstr "" -#: src/ownership/moves-function-calls.md:21 +#: src/exercises/day-1/luhn.md:3 msgid "" -"The heap memory allocated for `name` will be freed at the end of the " -"`say_hello` function." +"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/ownership/moves-function-calls.md:22 -msgid "" -"`main` can retain ownership if it passes `name` as a reference (`&name`) and " -"if `say_hello` accepts a reference as a parameter." +#: src/exercises/day-1/luhn.md:7 +msgid "Ignore all spaces. Reject number with less than two digits." msgstr "" -#: src/ownership/moves-function-calls.md:23 +#: src/exercises/day-1/luhn.md:9 msgid "" -"Alternatively, `main` can pass a clone of `name` in the first call (`name." -"clone()`)." +"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/ownership/moves-function-calls.md:24 +#: src/exercises/day-1/luhn.md:12 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." +"After doubling a digit, sum the digits if the result is greater than 9. So " +"doubling `7` becomes `14` which becomes `1 + 4 = 5`." msgstr "" -#: src/ownership/copy-clone.md:3 -msgid "" -"While move semantics are the default, certain types are copied by default:" +#: src/exercises/day-1/luhn.md:15 +msgid "Sum all the undoubled and doubled digits." msgstr "" -#: src/ownership/copy-clone.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let x = 42;\n" -" let y = x;\n" -" println!(\"x: {x}\");\n" -" println!(\"y: {y}\");\n" -"}\n" -"```" +#: src/exercises/day-1/luhn.md:17 +msgid "The credit card number is valid if the sum ends with `0`." msgstr "" -#: src/ownership/copy-clone.md:14 -msgid "These types implement the `Copy` trait." +#: src/exercises/day-1/luhn.md:19 +msgid "" +"Copy the code below to and implement the " +"function." msgstr "" -#: src/ownership/copy-clone.md:16 -msgid "You can opt-in your own types to use copy semantics:" +#: 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/ownership/copy-clone.md:18 +#: src/exercises/day-1/luhn.md:25 msgid "" -"```rust,editable\n" -"#[derive(Copy, Clone, Debug)]\n" -"struct Point(i32, i32);\n" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" "\n" -"fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = p1;\n" -" println!(\"p1: {p1:?}\");\n" -" println!(\"p2: {p2:?}\");\n" +"pub fn luhn(cc_number: &str) -> bool {\n" +" unimplemented!()\n" +"}\n" +"\n" +"#[test]\n" +"fn test_non_digit_cc_number() {\n" +" assert!(!luhn(\"foo\"));\n" +" assert!(!luhn(\"foo 0 0\"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_empty_cc_number() {\n" +" assert!(!luhn(\"\"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_single_digit_cc_number() {\n" +" assert!(!luhn(\"0\"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_two_digit_cc_number() {\n" +" assert!(luhn(\" 0 0 \"));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_valid_cc_number() {\n" +" assert!(luhn(\"4263 9826 4026 9299\"));\n" +" assert!(luhn(\"4539 3195 0343 6467\"));\n" +" assert!(luhn(\"7992 7398 713\"));\n" "}\n" +"\n" +"#[test]\n" +"fn test_invalid_cc_number() {\n" +" assert!(!luhn(\"4223 9826 4026 9299\"));\n" +" assert!(!luhn(\"4539 3195 0343 6476\"));\n" +" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +"}\n" +"\n" +"#[allow(dead_code)]\n" +"fn main() {}\n" "```" 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." +#: src/exercises/day-1/pattern-matching.md:1 +msgid "Exercise: Expression Evaluation" msgstr "" -#: src/ownership/copy-clone.md:35 -msgid "Copying and cloning are not the same thing:" +#: src/exercises/day-1/pattern-matching.md:3 +msgid "Let's write a simple recursive evaluator for arithmetic expressions. " msgstr "" -#: src/ownership/copy-clone.md:37 +#: src/exercises/day-1/pattern-matching.md:5 msgid "" -"Copying refers to bitwise copies of memory regions and does not work on " -"arbitrary objects." +"```rust\n" +"/// An operation to perform on two subexpressions.\n" +"#[derive(Debug)]\n" +"enum Operation {\n" +" Add,\n" +" Sub,\n" +" Mul,\n" +" Div,\n" +"}\n" +"\n" +"/// An expression, in tree form.\n" +"#[derive(Debug)]\n" +"enum Expression {\n" +" /// An operation on two subexpressions.\n" +" Op {\n" +" op: Operation,\n" +" left: Box,\n" +" right: Box,\n" +" },\n" +"\n" +" /// A literal value\n" +" Value(i64),\n" +"}\n" +"\n" +"/// The result of evaluating an expression.\n" +"#[derive(Debug, PartialEq, Eq)]\n" +"enum Res {\n" +" /// Evaluation was successful, with the given result.\n" +" Ok(i64),\n" +" /// Evaluation failed, with the given error message.\n" +" Err(String),\n" +"}\n" +"// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.\n" +"use Res::{Err, Ok};\n" +"\n" +"fn eval(e: Expression) -> Res {\n" +" todo!()\n" +"}\n" +"\n" +"#[test]\n" +"fn test_value() {\n" +" assert_eq!(eval(Expression::Value(19)), Ok(19));\n" +"}\n" +"\n" +"#[test]\n" +"fn test_sum() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(20)),\n" +" }),\n" +" Ok(30)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_recursion() {\n" +" let term1 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(9)),\n" +" };\n" +" let term2 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(3)),\n" +" right: Box::new(Expression::Value(4)),\n" +" }),\n" +" right: Box::new(Expression::Value(5)),\n" +" };\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(term1),\n" +" right: Box::new(term2),\n" +" }),\n" +" Ok(85)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_error() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Div,\n" +" left: Box::new(Expression::Value(99)),\n" +" right: Box::new(Expression::Value(0)),\n" +" }),\n" +" Err(String::from(\"division by zero\"))\n" +" );\n" +"}\n" +"```" msgstr "" -#: src/ownership/copy-clone.md:38 +#: src/exercises/day-1/pattern-matching.md:100 msgid "" -"Copying does not allow for custom logic (unlike copy constructors in C++)." +"The `Box` type here is a smart pointer, and will be covered in detail later " +"in the course. An expression can be \"boxed\" with `Box::new` as seen in the " +"tests. To evaluate a boxed expression, use the deref operator to \"unbox\" " +"it: `eval(*boxed_expr)`." msgstr "" -#: src/ownership/copy-clone.md:39 +#: src/exercises/day-1/pattern-matching.md:105 msgid "" -"Cloning is a more general operation and also allows for custom behavior by " -"implementing the `Clone` trait." +"Some expressions cannot be evaluated and will return an error. The `Res` " +"type represents either a successful value or an error with a message. This " +"is very similar to the standard-library `Result` which we will see later." msgstr "" -#: src/ownership/copy-clone.md:40 -msgid "Copying does not work on types that implement the `Drop` trait." -msgstr "" - -#: src/ownership/copy-clone.md:42 src/ownership/lifetimes-function-calls.md:29 -msgid "In the above example, try the following:" +#: src/exercises/day-1/pattern-matching.md:109 +msgid "" +"Copy and paste the code into the Rust playground, and begin implementing " +"`eval`. The final product should pass the tests. It may be helpful to use " +"`todo!()` and get the tests to pass one-by-one." msgstr "" -#: src/ownership/copy-clone.md:44 +#: src/exercises/day-1/pattern-matching.md:113 msgid "" -"Add a `String` field to `struct Point`. It will not compile because `String` " -"is not a `Copy` type." +"If you finish early, try writing a test that results in an integer overflow. " +"How could you handle this with `Res::Err` instead of a panic?" msgstr "" -#: src/ownership/copy-clone.md:45 -msgid "" -"Remove `Copy` from the `derive` attribute. The compiler error is now in the " -"`println!` for `p1`." +#: src/welcome-day-2.md:1 +msgid "Welcome to Day 2" msgstr "" -#: src/ownership/copy-clone.md:46 -msgid "Show that it works if you clone `p1` instead." +#: src/welcome-day-2.md:3 +msgid "Now that we have seen a fair amount of Rust, we will continue with:" msgstr "" -#: src/ownership/copy-clone.md:48 +#: src/welcome-day-2.md:5 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." +"Memory management: stack vs heap, manual memory management, scope-based " +"memory management, and garbage collection." msgstr "" +"Memory management: stack vs heap, manual memory management, scope-based " +"memory management, و garbage collection." -#: src/ownership/borrowing.md:3 +#: src/welcome-day-2.md:8 msgid "" -"Instead of transferring ownership when calling a function, you can let a " -"function _borrow_ the value:" -msgstr "" +"Ownership: move semantics, copying and cloning, borrowing, and lifetimes." +msgstr "Ownership: حرکت semantics،copying و cloning, borrowing و lifetimes." -#: src/ownership/borrowing.md:6 +#: src/welcome-day-2.md:10 +#, fuzzy +msgid "Structs and methods." +msgstr "رشته ها و تکرار کننده‌ها (Strings and Iterators)" + +#: src/welcome-day-2.md:12 msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn add(p1: &Point, p2: &Point) -> Point {\n" -" Point(p1.0 + p2.0, p1.1 + p2.1)\n" -"}\n" -"\n" -"fn main() {\n" -" let p1 = Point(3, 4);\n" -" let p2 = Point(10, 20);\n" -" let p3 = add(&p1, &p2);\n" -" println!(\"{p1:?} + {p2:?} = {p3:?}\");\n" -"}\n" -"```" +"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " +"`Rc` and `Arc`." msgstr "" -#: src/ownership/borrowing.md:22 -msgid "The `add` function _borrows_ two points and returns a new point." +#: src/welcome-day-2.md:15 +msgid "Modules: visibility, paths, and filesystem hierarchy." msgstr "" -#: src/ownership/borrowing.md:23 -msgid "The caller retains ownership of the inputs." +#: src/memory-management.md:3 +msgid "Traditionally, languages have fallen into two broad categories:" msgstr "" -#: src/ownership/borrowing.md:27 -msgid "Notes on stack returns:" +#: src/memory-management.md:5 +msgid "Full control via manual memory management: C, C++, Pascal, ..." msgstr "" -#: src/ownership/borrowing.md:28 +#: src/memory-management.md:6 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/) 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:" +"Full safety via automatic memory management at runtime: Java, Python, Go, " +"Haskell, ..." +msgstr "" + +#: src/memory-management.md:8 +msgid "Rust offers a new mix:" msgstr "" -#: src/ownership/borrowing.md:30 +#: src/memory-management.md:10 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" -"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" -"```" +"Full control _and_ safety via compile time enforcement of correct memory " +"management." msgstr "" -#: src/ownership/borrowing.md:48 -msgid "The Rust compiler can do return value optimization (RVO)." +#: src/memory-management.md:13 +msgid "It does this with an explicit ownership concept." msgstr "" -#: 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." +#: src/memory-management.md:15 +msgid "First, let's refresh how memory management works." msgstr "" -#: src/ownership/shared-unique-borrows.md:3 -msgid "Rust puts constraints on the ways you can borrow values:" +#: src/memory-management/stack-vs-heap.md:1 +msgid "The Stack vs The Heap" msgstr "" -#: src/ownership/shared-unique-borrows.md:5 -msgid "You can have one or more `&T` values at any given time, _or_" +#: src/memory-management/stack-vs-heap.md:3 +msgid "Stack: Continuous area of memory for local variables." msgstr "" -#: src/ownership/shared-unique-borrows.md:6 -msgid "You can have exactly one `&mut T` value." +#: src/memory-management/stack-vs-heap.md:4 +msgid "Values have fixed sizes known at compile time." msgstr "" -#: src/ownership/shared-unique-borrows.md:8 -msgid "" -"```rust,editable,compile_fail\n" -"fn main() {\n" -" let mut a: i32 = 10;\n" -" let b: &i32 = &a;\n" -"\n" -" {\n" -" let c: &mut i32 = &mut a;\n" -" *c = 20;\n" -" }\n" -"\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\n" -"}\n" -"```" +#: src/memory-management/stack-vs-heap.md:5 +msgid "Extremely fast: just move a stack pointer." 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." +#: src/memory-management/stack-vs-heap.md:6 +msgid "Easy to manage: follows function calls." 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/memory-management/stack-vs-heap.md:7 +msgid "Great memory locality." msgstr "" -#: src/ownership/shared-unique-borrows.md:27 -msgid "" -"After that change, the compiler realizes that `b` is only ever used before " -"the new mutable borrow of `a` through `c`. This is a feature of the borrow " -"checker called \"non-lexical lifetimes\"." +#: src/memory-management/stack-vs-heap.md:9 +msgid "Heap: Storage of values outside of function calls." msgstr "" -#: src/ownership/lifetimes.md:3 -msgid "A borrowed value has a _lifetime_:" +#: src/memory-management/stack-vs-heap.md:10 +msgid "Values have dynamic sizes determined at runtime." msgstr "" -#: src/ownership/lifetimes.md:5 -msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." +#: src/memory-management/stack-vs-heap.md:11 +msgid "Slightly slower than the stack: some book-keeping needed." msgstr "" -#: src/ownership/lifetimes.md:6 -msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." +#: src/memory-management/stack-vs-heap.md:12 +msgid "No guarantee of memory locality." +msgstr "" + +#: src/memory-management/stack.md:1 +msgid "Stack and Heap Example" msgstr "" -#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:23 +#: src/memory-management/stack.md:3 msgid "" -"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " -"lifetime `a`\"." +"Creating a `String` puts fixed-sized metadata on the stack and dynamically " +"sized data, the actual string, on the heap:" msgstr "" -#: src/ownership/lifetimes.md:9 +#: src/memory-management/stack.md:6 msgid "" -"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " -"yourself." +"```rust,editable\n" +"fn main() {\n" +" let s1 = String::from(\"Hello\");\n" +"}\n" +"```" msgstr "" -#: src/ownership/lifetimes.md:11 +#: src/memory-management/stack.md:28 msgid "" -"Lifetime annotations create constraints; the compiler verifies that there is " -"a valid solution." +"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/ownership/lifetimes.md:13 +#: src/memory-management/stack.md:30 msgid "" -"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)." +"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/ownership/lifetimes-function-calls.md:3 +#: src/memory-management/stack.md:34 msgid "" -"In addition to borrowing its arguments, a function can return a borrowed " -"value:" +"We can inspect the memory layout with `unsafe` Rust. However, you should " +"point out that this is rightfully unsafe!" msgstr "" -#: src/ownership/lifetimes-function-calls.md:5 +#: src/memory-management/stack.md:37 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(i32, i32);\n" -"\n" -"fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {\n" -" if p1.0 < p2.0 { p1 } else { p2 }\n" -"}\n" -"\n" "fn main() {\n" -" let p1: Point = Point(10, 10);\n" -" let p2: Point = Point(20, 20);\n" -" let p3: &Point = left_most(&p1, &p2);\n" -" println!(\"left-most point: {:?}\", p3);\n" +" 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/ownership/lifetimes-function-calls.md:21 -msgid "`'a` is a generic parameter, it is inferred by the compiler." +#: src/memory-management/manual.md:3 +msgid "You allocate and deallocate heap memory yourself." msgstr "" -#: src/ownership/lifetimes-function-calls.md:22 -msgid "Lifetimes start with `'` and `'a` is a typical default name." +#: 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/ownership/lifetimes-function-calls.md:25 -msgid "" -"The _at least_ part is important when parameters are in different scopes." +#: src/memory-management/manual.md:7 +msgid "C Example" 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:" +#: src/memory-management/manual.md:9 +msgid "You must call `free` on every pointer you allocate with `malloc`:" msgstr "" -#: src/ownership/lifetimes-function-calls.md:32 +#: src/memory-management/manual.md:11 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" -"\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" +"```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/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 +#: src/memory-management/manual.md:21 msgid "" -"Reset the workspace and change the function signature to `fn left_most<'a, " -"'b>(p1: &'a Point, p2: &'a Point) -> &'b Point`. This will not compile " -"because the relationship between the lifetimes `'a` and `'b` is unclear." -msgstr "" - -#: src/ownership/lifetimes-function-calls.md:53 -msgid "Another way to explain it:" +"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/ownership/lifetimes-function-calls.md:54 +#: src/memory-management/scope-based.md:3 msgid "" -"Two references to two values are borrowed by a function and the function " -"returns another reference." +"Constructors and destructors let you hook into the lifetime of an object." msgstr "" -#: src/ownership/lifetimes-function-calls.md:56 +#: src/memory-management/scope-based.md:5 msgid "" -"It must have come from one of those two inputs (or from a global variable)." +"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/ownership/lifetimes-function-calls.md:57 +#: src/memory-management/scope-based.md:9 msgid "" -"Which one is it? The compiler needs to know, so at the call site the " -"returned reference is not used for longer than a variable from where the " -"reference came from." +"This is often called _resource acquisition is initialization_ (RAII) and " +"gives you smart pointers." msgstr "" -#: src/ownership/lifetimes-data-structures.md:3 -msgid "" -"If a data type stores borrowed data, it must be annotated with a lifetime:" +#: src/memory-management/scope-based.md:12 +msgid "C++ Example" msgstr "" -#: src/ownership/lifetimes-data-structures.md:5 +#: src/memory-management/scope-based.md:14 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 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" +"```c++\n" +"void say_hello(std::unique_ptr person) {\n" +" std::cout << \"Hello \" << person->name << std::endl;\n" "}\n" "```" msgstr "" -#: src/ownership/lifetimes-data-structures.md:25 +#: src/memory-management/scope-based.md:20 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." +"The `std::unique_ptr` object is allocated on the stack, and points to memory " +"allocated on the heap." msgstr "" -#: src/ownership/lifetimes-data-structures.md:26 -msgid "" -"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " -"the borrow checker throws an error." +#: src/memory-management/scope-based.md:22 +msgid "At the end of `say_hello`, the `std::unique_ptr` destructor will run." msgstr "" -#: src/ownership/lifetimes-data-structures.md:27 -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." +#: src/memory-management/scope-based.md:23 +msgid "The destructor frees the `Person` object it points to." msgstr "" -#: src/ownership/lifetimes-data-structures.md:28 -msgid "When possible, make data structures own their data directly." +#: src/memory-management/scope-based.md:25 +msgid "" +"Special move constructors are used when passing ownership to a function:" msgstr "" -#: src/ownership/lifetimes-data-structures.md:29 -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." +#: src/memory-management/garbage-collection.md:1 +msgid "Automatic Memory Management" msgstr "" -#: src/exercises/day-1/afternoon.md:1 -msgid "Day 1: Afternoon Exercises" +#: src/memory-management/garbage-collection.md:3 +msgid "" +"An alternative to manual and scope-based memory management is automatic " +"memory management:" msgstr "" -#: src/exercises/day-1/afternoon.md:3 -msgid "We will look at two things:" +#: src/memory-management/garbage-collection.md:6 +msgid "The programmer never allocates or deallocates memory explicitly." msgstr "" -#: src/exercises/day-1/afternoon.md:5 -msgid "A small book library," +#: src/memory-management/garbage-collection.md:7 +msgid "" +"A garbage collector finds unused memory and deallocates it for the " +"programmer." msgstr "" -#: src/exercises/day-1/afternoon.md:7 -msgid "Iterators and ownership (hard)." +#: src/memory-management/garbage-collection.md:9 +msgid "Java Example" msgstr "" -#: src/exercises/day-1/book-library.md:3 -msgid "" -"We will learn much more about structs and the `Vec` type tomorrow. For " -"now, you just need to know part of its API:" +#: src/memory-management/garbage-collection.md:11 +msgid "The `person` object is not deallocated after `sayHello` returns:" msgstr "" -#: src/exercises/day-1/book-library.md:6 +#: src/memory-management/garbage-collection.md:13 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" +"```java\n" +"void sayHello(Person person) {\n" +" System.out.println(\"Hello \" + person.getName());\n" "}\n" "```" msgstr "" -#: src/exercises/day-1/book-library.md:18 -msgid "" -"Use this to model a library's book collection. Copy the code below to " -" and update the types to make it compile:" +#: src/memory-management/rust.md:1 +msgid "Memory Management in Rust" msgstr "" -#: src/exercises/day-1/book-library.md:21 -msgid "" -"```rust,should_panic\n" -"struct Library {\n" -" books: Vec,\n" -"}\n" -"\n" -"struct Book {\n" -" title: String,\n" -" year: u16,\n" -"}\n" -"\n" -"impl Book {\n" -" // This is a constructor, used below.\n" -" fn new(title: &str, year: u16) -> Book {\n" -" Book {\n" -" title: String::from(title),\n" -" year,\n" -" }\n" -" }\n" -"}\n" -"\n" -"// Implement the methods below. 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 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" -"```" +#: src/memory-management/rust.md:3 +msgid "Memory management in Rust is a mix:" +msgstr "" + +#: src/memory-management/rust.md:5 +msgid "Safe and correct like Java, but without a garbage collector." msgstr "" -#: src/exercises/day-1/book-library.md:102 -msgid "[Solution](solutions-afternoon.md#designing-a-library)" +#: src/memory-management/rust.md:6 +msgid "Scope-based like C++, but the compiler enforces full adherence." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:3 +#: src/memory-management/rust.md:7 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." +"A Rust user can choose the right abstraction for the situation, some even " +"have no cost at runtime like C." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 -msgid "`Iterator`" +#: src/memory-management/rust.md:9 +msgid "Rust achieves this by modeling _ownership_ explicitly." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:10 +#: src/memory-management/rust.md:13 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:" +"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/exercises/day-1/iterators-and-ownership.md:13 +#: src/memory-management/rust.md:15 msgid "" -"```rust\n" -"pub trait Iterator {\n" -" type Item;\n" -" fn next(&mut self) -> Option;\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/exercises/day-1/iterators-and-ownership.md:20 -msgid "You use this trait like this:" +#: src/ownership.md:3 +msgid "" +"All variable bindings have a _scope_ where they are valid and it is an error " +"to use a variable outside its scope:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:22 +#: src/ownership.md:19 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" -"```" +"At the end of the scope, the variable is _dropped_ and the data is freed." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:34 -msgid "What is the type returned by the iterator? Test your answer here:" +#: src/ownership.md:20 +msgid "A destructor can run here to free up resources." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:36 +#: src/ownership.md:21 +msgid "We say that the variable _owns_ the value." +msgstr "" + +#: src/ownership/move-semantics.md:3 +msgid "An assignment will transfer _ownership_ between variables:" +msgstr "" + +#: src/ownership/move-semantics.md:5 msgid "" -"```rust,editable,compile_fail\n" +"```rust,editable\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" +" let s1: String = String::from(\"Hello!\");\n" +" let s2: String = s1;\n" +" println!(\"s2: {s2}\");\n" +" // println!(\"s1: {s1}\");\n" "}\n" "```" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:46 -msgid "Why is this type used?" +#: src/ownership/move-semantics.md:14 +msgid "The assignment of `s1` to `s2` transfers ownership." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:48 -msgid "`IntoIterator`" +#: src/ownership/move-semantics.md:15 +msgid "When `s1` goes out of scope, nothing happens: it does not own anything." 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/ownership/move-semantics.md:16 +msgid "When `s2` goes out of scope, the string data is freed." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:53 -msgid "" -"```rust\n" -"pub trait IntoIterator {\n" -" type Item;\n" -" type IntoIter: Iterator;\n" -"\n" -" fn into_iter(self) -> Self::IntoIter;\n" -"}\n" -"```" +#: src/ownership/move-semantics.md:17 +msgid "There is always _exactly_ one variable binding which owns a value." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:62 +#: src/ownership/move-semantics.md:21 msgid "" -"The syntax here means that every implementation of `IntoIterator` must " -"declare two types:" +"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/exercises/day-1/iterators-and-ownership.md:65 -msgid "`Item`: the type we iterate over, such as `i8`," +#: src/ownership/move-semantics.md:23 +msgid "" +"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/exercises/day-1/iterators-and-ownership.md:66 -msgid "`IntoIter`: the `Iterator` type returned by the `into_iter` method." +#: src/ownership/move-semantics.md:25 +msgid "" +"Simple values (such as integers) can be marked `Copy` (see later slides)." 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/ownership/move-semantics.md:27 +msgid "In Rust, clones are explicit (by using `clone`)." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:71 -msgid "Like before, what is the type returned by the iterator?" +#: src/ownership/moved-strings-rust.md:11 +msgid "The heap data from `s1` is reused for `s2`." 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/ownership/moved-strings-rust.md:12 +msgid "When `s1` goes out of scope, nothing happens (it has been moved from)." msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:83 -msgid "`for` Loops" +#: src/ownership/moved-strings-rust.md:14 +msgid "Before move to `s2`:" 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/ownership/moved-strings-rust.md:31 +msgid "After move to `s2`:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:89 +#: src/ownership/moved-strings-rust.md:33 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" +"```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/exercises/day-1/iterators-and-ownership.md:103 -msgid "What is the type of `word` in each loop?" +#: src/ownership/double-free-modern-cpp.md:1 +#, fuzzy +msgid "Defensive Copies in Modern C++" +msgstr "آزاد سازی مضاعف در مدرن سی++" + +#: src/ownership/double-free-modern-cpp.md:3 +msgid "Modern C++ solves this differently:" msgstr "" -#: src/exercises/day-1/iterators-and-ownership.md:105 +#: src/ownership/double-free-modern-cpp.md:5 msgid "" -"Experiment with the code above and then consult the documentation for [`impl " -"IntoIterator for &Vec`](https://doc.rust-lang.org/std/vec/struct.Vec." -"html#impl-IntoIterator-for-%26'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." +"```c++\n" +"std::string s1 = \"Cpp\";\n" +"std::string s2 = s1; // Duplicate the data in s1.\n" +"```" msgstr "" -#: src/welcome-day-2.md:1 -msgid "Welcome to Day 2" +#: src/ownership/double-free-modern-cpp.md:10 +msgid "" +"The heap data from `s1` is duplicated and `s2` gets its own independent copy." msgstr "" -#: src/welcome-day-2.md:3 -msgid "Now that we have seen a fair amount of Rust, we will continue with:" +#: 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/welcome-day-2.md:5 -msgid "Structs, enums, methods." +#: src/ownership/double-free-modern-cpp.md:13 +msgid "Before copy-assignment:" msgstr "" -#: src/welcome-day-2.md:7 -msgid "Pattern matching: destructuring enums, structs, and arrays." +#: src/ownership/double-free-modern-cpp.md:30 +msgid "After copy-assignment:" msgstr "" -#: src/welcome-day-2.md:9 +#: src/ownership/double-free-modern-cpp.md:57 msgid "" -"Control flow constructs: `if`, `if let`, `while`, `while let`, `break`, and " -"`continue`." +"C++ has made a slightly different choice than Rust. Because `=` copies data, " +"the string data has to be cloned. Otherwise we would get a double-free when " +"either string goes out of scope." msgstr "" -#: src/welcome-day-2.md:12 +#: src/ownership/double-free-modern-cpp.md:61 msgid "" -"The Standard Library: `String`, `Option` and `Result`, `Vec`, `HashMap`, " -"`Rc` and `Arc`." +"C++ also has [`std::move`](https://en.cppreference.com/w/cpp/utility/move), " +"which is used to indicate when a value may be moved from. If the example had " +"been `s2 = std::move(s1)`, no heap allocation would take place. After the " +"move, `s1` would be in a valid but unspecified state. Unlike Rust, the " +"programmer is allowed to keep using `s1`." msgstr "" -#: src/welcome-day-2.md:15 -msgid "Modules: visibility, paths, and filesystem hierarchy." +#: src/ownership/double-free-modern-cpp.md:66 +msgid "" +"Unlike Rust, `=` in C++ can run arbitrary code as determined by the type " +"which is being copied or moved." msgstr "" -#: src/structs.md:3 -msgid "Like C and C++, Rust has support for custom structs:" +#: src/ownership/moves-function-calls.md:3 +msgid "" +"When you pass a value to a function, the value is assigned to the function " +"parameter. This transfers ownership:" msgstr "" -#: src/structs.md:5 +#: src/ownership/moves-function-calls.md:6 msgid "" "```rust,editable\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" +"fn say_hello(name: String) {\n" +" println!(\"Hello {name}\")\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 name = String::from(\"Alice\");\n" +" say_hello(name);\n" +" // say_hello(name);\n" "}\n" "```" msgstr "" -#: src/structs.md:31 src/enums.md:34 src/enums/sizes.md:28 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:43 -msgid "Key Points:" +#: 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/structs.md:33 -msgid "Structs work like in C or C++." +#: src/ownership/moves-function-calls.md:21 +msgid "" +"The heap memory allocated for `name` will be freed at the end of the " +"`say_hello` function." msgstr "" -#: src/structs.md:34 -msgid "Like in C++, and unlike in C, no typedef is needed to define a type." +#: src/ownership/moves-function-calls.md:22 +msgid "" +"`main` can retain ownership if it passes `name` as a reference (`&name`) and " +"if `say_hello` accepts a reference as a parameter." msgstr "" -#: src/structs.md:35 -msgid "Unlike in C++, there is no inheritance between structs." +#: src/ownership/moves-function-calls.md:23 +msgid "" +"Alternatively, `main` can pass a clone of `name` in the first call (`name." +"clone()`)." msgstr "" -#: src/structs.md:36 +#: src/ownership/moves-function-calls.md:24 msgid "" -"Methods are defined in an `impl` block, which we will see in following " -"slides." +"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/structs.md:37 +#: src/ownership/copy-clone.md:3 msgid "" -"This may be a good time to let people know there are different types of " -"structs. " +"While move semantics are the default, certain types are copied by default:" msgstr "" -#: 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. " +#: src/ownership/copy-clone.md:15 +msgid "These types implement the `Copy` trait." msgstr "" -#: src/structs.md:39 -msgid "" -"The next slide will introduce Tuple structs, used when the field names are " -"not important." +#: src/ownership/copy-clone.md:17 +msgid "You can opt-in your own types to use copy semantics:" msgstr "" -#: 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." +#: src/ownership/copy-clone.md:32 +msgid "After the assignment, both `p1` and `p2` own their own data." msgstr "" -#: src/structs/tuple-structs.md:3 -msgid "If the field names are unimportant, you can use a tuple struct:" +#: src/ownership/copy-clone.md:33 +msgid "We can also use `p1.clone()` to explicitly copy the data." msgstr "" -#: src/structs/tuple-structs.md:5 -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" -"}\n" -"```" +#: src/ownership/copy-clone.md:37 +msgid "Copying and cloning are not the same thing:" msgstr "" -#: src/structs/tuple-structs.md:14 -msgid "This is often used for single-field wrappers (called newtypes):" +#: src/ownership/copy-clone.md:39 +msgid "" +"Copying refers to bitwise copies of memory regions and does not work on " +"arbitrary objects." msgstr "" -#: src/structs/tuple-structs.md:16 +#: src/ownership/copy-clone.md:40 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" -"```" +"Copying does not allow for custom logic (unlike copy constructors in C++)." msgstr "" -#: src/structs/tuple-structs.md:37 +#: src/ownership/copy-clone.md:41 msgid "" -"Newtypes are a great way to encode additional information about the value in " -"a primitive type, for example:" +"Cloning is a more general operation and also allows for custom behavior by " +"implementing the `Clone` trait." msgstr "" -#: src/structs/tuple-structs.md:38 -msgid "The number is measured in some units: `Newtons` in the example above." +#: src/ownership/copy-clone.md:42 +msgid "Copying does not work on types that implement the `Drop` trait." msgstr "" -#: src/structs/tuple-structs.md:39 -msgid "" -"The value passed some validation when it was created, so you no longer have " -"to validate it again at every use: 'PhoneNumber(String)`or`OddNumber(u32)\\`." +#: src/ownership/copy-clone.md:44 src/ownership/lifetimes-function-calls.md:30 +msgid "In the above example, try the following:" msgstr "" -#: src/structs/tuple-structs.md:40 +#: src/ownership/copy-clone.md:46 msgid "" -"Demonstrate how to add a `f64` value to a `Newtons` type by accessing the " -"single field in the newtype." +"Add a `String` field to `struct Point`. It will not compile because `String` " +"is not a `Copy` type." msgstr "" -#: src/structs/tuple-structs.md:41 +#: src/ownership/copy-clone.md:47 msgid "" -"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " -"for instance using booleans as integers." +"Remove `Copy` from the `derive` attribute. The compiler error is now in the " +"`println!` for `p1`." msgstr "" -#: src/structs/tuple-structs.md:42 -msgid "Operator overloading is discussed on Day 3 (generics)." +#: src/ownership/copy-clone.md:48 +msgid "Show that it works if you clone `p1` instead." msgstr "" -#: src/structs/tuple-structs.md:43 +#: src/ownership/copy-clone.md:50 msgid "" -"The example is a subtle reference to the [Mars Climate Orbiter](https://en." -"wikipedia.org/wiki/Mars_Climate_Orbiter) failure." +"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/structs/field-shorthand.md:3 +#: src/ownership/borrowing.md:3 msgid "" -"If you already have variables with the right names, then you can create the " -"struct using a shorthand:" +"Instead of transferring ownership when calling a function, you can let a " +"function _borrow_ the value:" msgstr "" -#: src/structs/field-shorthand.md:6 -msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"\n" -"impl Person {\n" -" fn new(name: String, age: u8) -> Person {\n" -" Person { name, age }\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let peter = Person::new(String::from(\"Peter\"), 27);\n" -" println!(\"{peter:?}\");\n" -"}\n" -"```" +#: src/ownership/borrowing.md:23 +msgid "The `add` function _borrows_ two points and returns a new point." 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/ownership/borrowing.md:24 +msgid "The caller retains ownership of the inputs." 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/ownership/borrowing.md:28 +msgid "Notes on stack returns:" msgstr "" -#: src/structs/field-shorthand.md:41 +#: src/ownership/borrowing.md:29 msgid "" -"Implement the `Default` trait for the struct. Define some fields and use the " -"default values for the other fields." +"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/structs/field-shorthand.md:43 +#: src/ownership/borrowing.md:50 +msgid "The Rust compiler can do return value optimization (RVO)." +msgstr "" + +#: src/ownership/borrowing.md:51 msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Person {\n" -" name: String,\n" -" age: u8,\n" -"}\n" -"impl Default for Person {\n" -" fn default() -> Person {\n" -" Person {\n" -" name: \"Bot\".to_string(),\n" -" age: 0,\n" -" }\n" -" }\n" -"}\n" -"fn create_default() {\n" -" let tmp = Person {\n" -" ..Person::default()\n" -" };\n" -" let tmp = Person {\n" -" name: \"Sam\".to_string(),\n" -" ..Person::default()\n" -" };\n" -"}\n" -"```" +"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/structs/field-shorthand.md:68 -msgid "Methods are defined in the `impl` block." +#: src/ownership/shared-unique-borrows.md:3 +msgid "Rust puts constraints on the ways you can borrow values:" msgstr "" -#: src/structs/field-shorthand.md:69 +#: src/ownership/shared-unique-borrows.md:5 +msgid "You can have one or more `&T` values at any given time, _or_" +msgstr "" + +#: src/ownership/shared-unique-borrows.md:6 +msgid "You can have exactly one `&mut T` value." +msgstr "" + +#: src/ownership/shared-unique-borrows.md:26 msgid "" -"Use struct update syntax to define a new structure using `peter`. Note that " -"the variable `peter` will no longer be accessible afterwards." +"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/structs/field-shorthand.md:70 +#: src/ownership/shared-unique-borrows.md:27 msgid "" -"Use `{:#?}` when printing structs to request the `Debug` representation." +"Move the `println!` statement for `b` before the scope that introduces `c` " +"to make the code compile." msgstr "" -#: src/enums.md:3 +#: src/ownership/shared-unique-borrows.md:28 msgid "" -"The `enum` keyword allows the creation of a type which has a few different " -"variants:" +"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/enums.md:6 +#: src/ownership/lifetimes.md:3 +msgid "A borrowed value has a _lifetime_:" +msgstr "" + +#: src/ownership/lifetimes.md:5 +msgid "The lifetime can be implicit: `add(p1: &Point, p2: &Point) -> Point`." +msgstr "" + +#: src/ownership/lifetimes.md:6 +msgid "Lifetimes can also be explicit: `&'a Point`, `&'document str`." +msgstr "" + +#: src/ownership/lifetimes.md:7 src/ownership/lifetimes-function-calls.md:24 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" -"```" +"Read `&'a Point` as \"a borrowed `Point` which is valid for at least the " +"lifetime `a`\"." msgstr "" -#: src/enums.md:36 -msgid "Enumerations allow you to collect a set of values under one type" +#: src/ownership/lifetimes.md:9 +msgid "" +"Lifetimes are always inferred by the compiler: you cannot assign a lifetime " +"yourself." msgstr "" -#: src/enums.md:37 +#: src/ownership/lifetimes.md:11 msgid "" -"This page offers an enum type `CoinFlip` with two variants `Heads` and " -"`Tails`. You might note the namespace when using variants." +"Lifetime annotations create constraints; the compiler verifies that there is " +"a valid solution." msgstr "" -#: src/enums.md:38 -msgid "This might be a good time to compare Structs and Enums:" +#: src/ownership/lifetimes.md:13 +msgid "" +"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/enums.md:39 +#: src/ownership/lifetimes-function-calls.md:3 msgid "" -"In both, you can have a simple version without fields (unit struct) or one " -"with different types of fields (variant payloads). " +"In addition to borrowing its arguments, a function can return a borrowed " +"value:" msgstr "" -#: src/enums.md:40 -msgid "In both, associated functions are defined within an `impl` block." +#: src/ownership/lifetimes-function-calls.md:22 +msgid "`'a` is a generic parameter, it is inferred by the compiler." msgstr "" -#: src/enums.md:41 +#: src/ownership/lifetimes-function-calls.md:23 +msgid "Lifetimes start with `'` and `'a` is a typical default name." +msgstr "" + +#: src/ownership/lifetimes-function-calls.md:26 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. " +"The _at least_ part is important when parameters are in different scopes." msgstr "" -#: src/enums/variant-payloads.md:3 +#: src/ownership/lifetimes-function-calls.md:32 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:" +"Move the declaration of `p2` and `p3` into a new scope (`{ ... }`), " +"resulting in the following code:" msgstr "" -#: src/enums/variant-payloads.md:6 +#: src/ownership/lifetimes-function-calls.md:52 +msgid "Note how this does not compile since `p3` outlives `p2`." +msgstr "" + +#: src/ownership/lifetimes-function-calls.md:54 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/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/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/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/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/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. " +"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/enums/variant-payloads.md:40 -msgid "`match` inspects a hidden discriminant field in the `enum`." +#: src/ownership/lifetimes-function-calls.md:55 +msgid "Another way to explain it:" msgstr "" -#: src/enums/variant-payloads.md:41 +#: src/ownership/lifetimes-function-calls.md:56 msgid "" -"It is possible to retrieve the discriminant by calling `std::mem::" -"discriminant()`" +"Two references to two values are borrowed by a function and the function " +"returns another reference." msgstr "" -#: src/enums/variant-payloads.md:42 +#: src/ownership/lifetimes-function-calls.md:58 msgid "" -"This is useful, for example, if implementing `PartialEq` for structs where " -"comparing field values doesn't affect equality." +"It must have come from one of those two inputs (or from a global variable)." msgstr "" -#: src/enums/variant-payloads.md:43 +#: src/ownership/lifetimes-function-calls.md:59 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." +"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/enums/sizes.md:3 +#: src/ownership/lifetimes-data-structures.md:3 msgid "" -"Rust enums are packed tightly, taking constraints due to alignment into " -"account:" +"If a data type stores borrowed data, it must be annotated with a lifetime:" msgstr "" -#: src/enums/sizes.md:5 +#: src/ownership/lifetimes-data-structures.md:5 msgid "" "```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" +"#[derive(Debug)]\n" +"struct Highlight<'doc>(&'doc str);\n" "\n" -"enum Foo {\n" -" A,\n" -" B,\n" +"fn erase(text: String) {\n" +" println!(\"Bye {text}!\");\n" "}\n" "\n" "fn main() {\n" -" dbg_size::();\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/enums/sizes.md:24 +#: src/ownership/lifetimes-data-structures.md:25 msgid "" -"See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout." -"html)." +"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/enums/sizes.md:30 +#: src/ownership/lifetimes-data-structures.md:26 msgid "" -"Internally Rust is using a field (discriminant) to keep track of the enum " -"variant." +"If `text` is consumed before the end of the lifetime of `fox` (or `dog`), " +"the borrow checker throws an error." msgstr "" -#: src/enums/sizes.md:32 +#: src/ownership/lifetimes-data-structures.md:27 msgid "" -"You can control the discriminant if needed (e.g., for compatibility with C):" +"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/enums/sizes.md:34 +#: src/ownership/lifetimes-data-structures.md:28 +msgid "When possible, make data structures own their data directly." +msgstr "" + +#: src/ownership/lifetimes-data-structures.md:29 +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." +msgstr "" + +#: src/structs.md:3 +msgid "Like C and C++, Rust has support for custom structs:" +msgstr "" + +#: src/structs.md:5 msgid "" "```rust,editable\n" -"#[repr(u32)]\n" -"enum Bar {\n" -" A, // 0\n" -" B = 10000,\n" -" C, // 10001\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" "}\n" "\n" "fn main() {\n" -" println!(\"A: {}\", Bar::A as u32);\n" -" println!(\"B: {}\", Bar::B as u32);\n" -" println!(\"C: {}\", Bar::C as u32);\n" +" let mut peter = Person {\n" +" name: String::from(\"Peter\"),\n" +" age: 27,\n" +" };\n" +" println!(\"{} is {} years old\", peter.name, peter.age);\n" +" \n" +" peter.age = 28;\n" +" println!(\"{} is {} years old\", peter.name, peter.age);\n" +" \n" +" let jackie = Person {\n" +" name: String::from(\"Jackie\"),\n" +" ..peter\n" +" };\n" +" println!(\"{} is {} years old\", jackie.name, jackie.age);\n" "}\n" "```" msgstr "" -#: src/enums/sizes.md:49 -msgid "" -"Without `repr`, the discriminant type takes 2 bytes, because 10001 fits 2 " -"bytes." +#: src/structs.md:33 +msgid "Structs work like in C or C++." msgstr "" -#: src/enums/sizes.md:53 -msgid "Try out other types such as" +#: src/structs.md:34 +msgid "Like in C++, and unlike in C, no typedef is needed to define a type." msgstr "" -#: src/enums/sizes.md:55 -msgid "`dbg_size!(bool)`: size 1 bytes, align: 1 bytes," +#: src/structs.md:35 +msgid "Unlike in C++, there is no inheritance between structs." msgstr "" -#: src/enums/sizes.md:56 +#: src/structs.md:36 msgid "" -"`dbg_size!(Option)`: size 1 bytes, align: 1 bytes (niche optimization, " -"see below)," +"Methods are defined in an `impl` block, which we will see in following " +"slides." msgstr "" -#: src/enums/sizes.md:57 -msgid "`dbg_size!(&i32)`: size 8 bytes, align: 8 bytes (on a 64-bit machine)," +#: src/structs.md:37 +msgid "" +"This may be a good time to let people know there are different types of " +"structs. " msgstr "" -#: src/enums/sizes.md:58 +#: src/structs.md:38 msgid "" -"`dbg_size!(Option<&i32>)`: size 8 bytes, align: 8 bytes (null pointer " -"optimization, see below)." +"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/enums/sizes.md:60 +#: src/structs.md:39 msgid "" -"Niche optimization: Rust will merge unused bit patterns for the enum " -"discriminant." +"The next slide will introduce Tuple structs, used when the field names are " +"not important." msgstr "" -#: src/enums/sizes.md:63 +#: src/structs.md:40 msgid "" -"Null pointer optimization: For [some types](https://doc.rust-lang.org/std/" -"option/#representation), Rust guarantees that `size_of::()` equals " -"`size_of::>()`." +"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/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." +#: src/structs/tuple-structs.md:3 +msgid "If the field names are unimportant, you can use a tuple struct:" msgstr "" -#: src/enums/sizes.md:70 +#: src/structs/tuple-structs.md:5 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" +"struct Point(i32, i32);\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" +" let p = Point(17, 23);\n" +" println!(\"({}, {})\", p.0, p.1);\n" "}\n" "```" msgstr "" -#: src/enums/sizes.md:105 -msgid "" -"More complex example if you want to discuss what happens when we chain more " -"than 256 `Option`s together." +#: src/structs/tuple-structs.md:14 +msgid "This is often used for single-field wrappers (called newtypes):" msgstr "" -#: src/enums/sizes.md:107 +#: src/structs/tuple-structs.md:16 msgid "" -"```rust,editable\n" -"#![recursion_limit = \"1000\"]\n" -"\n" -"use std::mem::transmute;\n" +"```rust,editable,compile_fail\n" +"struct PoundsOfForce(f64);\n" +"struct Newtons(f64);\n" "\n" -"macro_rules! dbg_bits {\n" -" ($e:expr, $bit_type:ty) => {\n" -" println!(\"- {}: {:#x}\", stringify!($e), transmute::<_, " -"$bit_type>($e));\n" -" };\n" +"fn compute_thruster_force() -> PoundsOfForce {\n" +" todo!(\"Ask a rocket scientist at NASA\")\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" +"fn set_thruster_force(force: Newtons) {\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" +" let force = compute_thruster_force();\n" +" set_thruster_force(force);\n" "}\n" +"\n" "```" msgstr "" -#: src/methods.md:3 +#: src/structs/tuple-structs.md:37 msgid "" -"Rust allows you to associate functions with your new types. You do this with " -"an `impl` block:" +"Newtypes are a great way to encode additional information about the value in " +"a primitive type, for example:" msgstr "" -#: src/methods.md:6 -msgid "" +#: src/structs/tuple-structs.md:38 +msgid "The number is measured in some units: `Newtons` in the example above." +msgstr "" + +#: src/structs/tuple-structs.md:39 +msgid "" +"The value passed some validation when it was created, so you no longer have " +"to validate it again at every use: `PhoneNumber(String)` or `OddNumber(u32)`." +msgstr "" + +#: 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." +msgstr "" + +#: src/structs/tuple-structs.md:41 +msgid "" +"Rust generally doesn’t like inexplicit things, like automatic unwrapping or " +"for instance using booleans as integers." +msgstr "" + +#: src/structs/tuple-structs.md:42 +msgid "Operator overloading is discussed on Day 3 (generics)." +msgstr "" + +#: 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/structs/field-shorthand.md:3 +msgid "" +"If you already have variables with the right names, then you can create the " +"struct using a shorthand:" +msgstr "" + +#: src/structs/field-shorthand.md:6 +msgid "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" +"\n" +"impl Person {\n" +" fn new(name: String, age: u8) -> Person {\n" +" Person { name, age }\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let peter = Person::new(String::from(\"Peter\"), 27);\n" +" println!(\"{peter:?}\");\n" +"}\n" +"```" +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" +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." +msgstr "" + +#: src/structs/field-shorthand.md:43 +msgid "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Person {\n" +" name: String,\n" +" age: u8,\n" +"}\n" +"impl Default for Person {\n" +" fn default() -> Person {\n" +" Person {\n" +" name: \"Bot\".to_string(),\n" +" age: 0,\n" +" }\n" +" }\n" +"}\n" +"fn create_default() {\n" +" let tmp = Person {\n" +" ..Person::default()\n" +" };\n" +" let tmp = Person {\n" +" name: \"Sam\".to_string(),\n" +" ..Person::default()\n" +" };\n" +"}\n" +"```" +msgstr "" + +#: src/structs/field-shorthand.md:68 +msgid "Methods are defined in the `impl` block." +msgstr "" + +#: src/structs/field-shorthand.md:69 +msgid "" +"Use struct update syntax to define a new structure using `peter`. Note that " +"the variable `peter` will no longer be accessible afterwards." +msgstr "" + +#: src/structs/field-shorthand.md:70 +msgid "" +"Use `{:#?}` when printing structs to request the `Debug` representation." +msgstr "" + +#: src/methods.md:3 +msgid "" +"Rust allows you to associate functions with your new types. You do this with " +"an `impl` block:" +msgstr "" + +#: src/methods.md:6 +msgid "" "```rust,editable\n" "#[derive(Debug)]\n" "struct Person {\n" @@ -6318,296 +6339,135 @@ msgid "" "over. We describe vectors in more detail in the afternoon. " msgstr "" -#: 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/pattern-matching.md:6 -msgid "The patterns can be simple values, similarly to `switch` in C and C++:" -msgstr "" - -#: src/pattern-matching.md:8 -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" -"}\n" -"```" -msgstr "" - -#: src/pattern-matching.md:21 -msgid "The `_` pattern is a wildcard pattern which matches any value." -msgstr "" - -#: src/pattern-matching.md:26 -msgid "" -"You might point out how some specific characters are being used when in a " -"pattern" -msgstr "" - -#: src/pattern-matching.md:27 -msgid "`|` as an `or`" -msgstr "" - -#: src/pattern-matching.md:28 -msgid "`..` can expand as much as it needs to be" -msgstr "" - -#: src/pattern-matching.md:29 -msgid "`1..=5` represents an inclusive range" -msgstr "" - -#: src/pattern-matching.md:30 -msgid "`_` is a wild card" +#: src/exercises/day-2/morning.md:1 +msgid "Day 2: Morning Exercises" msgstr "" -#: src/pattern-matching.md:31 -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`." +#: src/exercises/day-2/morning.md:3 +msgid "We will look at implementing methods in two contexts:" msgstr "" -#: src/pattern-matching.md:32 -msgid "You can demonstrate matching on a reference." +#: src/exercises/day-2/morning.md:5 +msgid "Storing books and querying the collection" msgstr "" -#: src/pattern-matching.md:33 -msgid "" -"This might be a good time to bring up the concept of irrefutable patterns, " -"as the term can show up in error messages." +#: src/exercises/day-2/morning.md:7 +msgid "Keeping track of health statistics for patients" msgstr "" -#: src/pattern-matching/destructuring-enums.md:3 +#: src/exercises/day-2/book-library.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:" +"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/pattern-matching/destructuring-enums.md:6 +#: src/exercises/day-2/book-library.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" +" 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/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/pattern-matching/destructuring-enums.md:36 -msgid "" -"The `if`/`else` expression is returning an enum that is later unpacked with " -"a `match`." -msgstr "" - -#: src/pattern-matching/destructuring-enums.md:37 +#: src/exercises/day-2/book-library.md:18 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." -msgstr "" - -#: src/pattern-matching/destructuring-structs.md:3 -msgid "You can also destructure `structs`:" +"Use this to model a library's book collection. Copy the code below to " +" and update the types to make it compile:" msgstr "" -#: src/pattern-matching/destructuring-structs.md:5 +#: src/exercises/day-2/book-library.md:21 msgid "" -"```rust,editable\n" -"struct Foo {\n" -" x: (u32, u32),\n" -" y: u32,\n" +"```rust,should_panic\n" +"struct Library {\n" +" books: Vec,\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" +"struct Book {\n" +" title: String,\n" +" year: u16,\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." -msgstr "" - -#: src/pattern-matching/destructuring-arrays.md:3 -msgid "" -"You can destructure arrays, tuples, and slices by matching on their elements:" -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" +"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" -"```" -msgstr "" - -#: src/pattern-matching/destructuring-arrays.md:21 -msgid "" -"Destructuring of slices of unknown length also works with patterns of fixed " -"length." -msgstr "" - -#: src/pattern-matching/destructuring-arrays.md:24 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" inspect(&[0, -2, 3]);\n" -" inspect(&[0, -2, 3, 4]);\n" -"}\n" "\n" -"#[rustfmt::skip]\n" -"fn inspect(slice: &[i32]) {\n" -" println!(\"Tell me about {slice:?}\");\n" -" match slice {\n" -" &[0, y, z] => println!(\"First is 0, y = {y}, and z = {z}\"),\n" -" &[1, ..] => println!(\"First is 1 and the rest were ignored\"),\n" -" _ => println!(\"All elements were ignored\"),\n" +"// Implement the methods below. Notice how the `self` parameter\n" +"// changes type to indicate the method's required level of ownership\n" +"// over the object:\n" +"//\n" +"// - `&self` for shared read-only access,\n" +"// - `&mut self` for unique and mutable access,\n" +"// - `self` for unique access by value.\n" +"impl Library {\n" +" fn new() -> Library {\n" +" todo!(\"Initialize and return a `Library` value\")\n" +" }\n" +"\n" +" fn len(&self) -> usize {\n" +" todo!(\"Return the length of `self.books`\")\n" +" }\n" +"\n" +" fn is_empty(&self) -> bool {\n" +" todo!(\"Return `true` if `self.books` is empty\")\n" +" }\n" +"\n" +" fn add_book(&mut self, book: Book) {\n" +" todo!(\"Add a new book to `self.books`\")\n" +" }\n" +"\n" +" fn print_books(&self) {\n" +" todo!(\"Iterate over `self.books` and print each book's title and " +"year\")\n" +" }\n" +"\n" +" fn oldest_book(&self) -> Option<&Book> {\n" +" todo!(\"Return a reference to the oldest book (if any)\")\n" " }\n" "}\n" -"```" -msgstr "" - -#: 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 "" -"Point out that how `..` will expand to account for different number of " -"elements." -msgstr "" - -#: src/pattern-matching/destructuring-arrays.md:44 -msgid "Show matching against the tail with patterns `[.., b]` and `[a@..,b]`" -msgstr "" - -#: src/pattern-matching/match-guards.md:3 -msgid "" -"When matching, you can add a _guard_ to a pattern. This is an arbitrary " -"Boolean expression which will be executed if the pattern matches:" -msgstr "" - -#: src/pattern-matching/match-guards.md:6 -msgid "" -"```rust,editable\n" -"#[rustfmt::skip]\n" +"\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 library = Library::new();\n" +"\n" +" println!(\n" +" \"The library is empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\n" +"\n" +" println!(\n" +" \"The library is no longer empty: library.is_empty() -> {}\",\n" +" library.is_empty()\n" +" );\n" +"\n" +" library.print_books();\n" +"\n" +" match library.oldest_book() {\n" +" Some(book) => println!(\"The oldest book is {}\", book.title),\n" +" None => println!(\"The library is empty!\"),\n" " }\n" +"\n" +" println!(\"The library has {} books\", library.len());\n" +" library.print_books();\n" "}\n" "```" msgstr "" -#: src/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/pattern-matching/match-guards.md:24 -msgid "" -"They are not the same as separate `if` expression inside of the match arm. " -"An `if` expression inside of the branch block (after `=>`) happens after the " -"match arm is selected. Failing the `if` condition inside of that block won't " -"result in other arms of the original `match` expression being considered." -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-2/morning.md:1 -msgid "Day 2: Morning Exercises" -msgstr "" - -#: 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/health-statistics.md:3 msgid "" "You're working on implementing a health-monitoring system. As part of that, " @@ -6655,36 +6515,38 @@ msgid "" "\n" "impl User {\n" " pub fn new(name: String, age: u32, height: f32) -> Self {\n" -" unimplemented!()\n" +" todo!(\"Create a new User instance\")\n" " }\n" "\n" " pub fn name(&self) -> &str {\n" -" unimplemented!()\n" +" todo!(\"Return the user's name\")\n" " }\n" "\n" " pub fn age(&self) -> u32 {\n" -" unimplemented!()\n" +" todo!(\"Return the user's age\")\n" " }\n" "\n" " pub fn height(&self) -> f32 {\n" -" unimplemented!()\n" +" todo!(\"Return the user's height\")\n" " }\n" "\n" " pub fn doctor_visits(&self) -> u32 {\n" -" unimplemented!()\n" +" todo!(\"Return the number of time the user has visited the " +"doctor\")\n" " }\n" "\n" " pub fn set_age(&mut self, new_age: u32) {\n" -" unimplemented!()\n" +" todo!(\"Set the user's age\")\n" " }\n" "\n" " pub fn set_height(&mut self, new_height: f32) {\n" -" unimplemented!()\n" +" todo!(\"Set the user's height\")\n" " }\n" "\n" " pub fn visit_doctor(&mut self, measurements: Measurements) -> " "HealthReport {\n" -" unimplemented!()\n" +" todo!(\"Update a user's statistics based on measurements from a " +"visit to the doctor\")\n" " }\n" "}\n" "\n" @@ -6730,2889 +6592,2324 @@ msgid "" "```" msgstr "" -#: src/exercises/day-2/points-polygons.md:1 -msgid "Polygon Struct" +#: src/std.md:3 +msgid "" +"Rust comes with a standard library which helps establish a set of common " +"types used by Rust libraries 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:" msgstr "" -#: src/exercises/day-2/points-polygons.md:3 +#: src/std.md:9 msgid "" -"We will create a `Polygon` struct which contain some points. Copy the code " -"below to and fill in the missing methods to " -"make the tests pass:" +"[`Option` and `Result`](std/option-result.md) types: used for optional " +"values and [error handling](error-handling.md)." +msgstr "" + +#: src/std.md:12 +msgid "[`String`](std/string.md): the default string type used for owned data." +msgstr "" + +#: src/std.md:14 +msgid "[`Vec`](std/vec.md): a standard extensible vector." msgstr "" -#: src/exercises/day-2/points-polygons.md:7 +#: src/std.md:16 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub struct Point {\n" -" // add fields\n" -"}\n" -"\n" -"impl Point {\n" -" // add methods\n" -"}\n" -"\n" -"pub struct Polygon {\n" -" // add fields\n" -"}\n" -"\n" -"impl Polygon {\n" -" // add methods\n" -"}\n" -"\n" -"pub struct Circle {\n" -" // add fields\n" -"}\n" -"\n" -"impl Circle {\n" -" // add methods\n" -"}\n" -"\n" -"pub enum Shape {\n" -" Polygon(Polygon),\n" -" Circle(Circle),\n" -"}\n" -"\n" -"#[cfg(test)]\n" -"mod tests {\n" -" use super::*;\n" -"\n" -" fn round_two_digits(x: f64) -> f64 {\n" -" (x * 100.0).round() / 100.0\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_magnitude() {\n" -" let p1 = Point::new(12, 13);\n" -" assert_eq!(round_two_digits(p1.magnitude()), 17.69);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_dist() {\n" -" let p1 = Point::new(10, 10);\n" -" let p2 = Point::new(14, 13);\n" -" assert_eq!(round_two_digits(p1.dist(p2)), 5.00);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_point_add() {\n" -" let p1 = Point::new(16, 16);\n" -" let p2 = p1 + Point::new(-4, 3);\n" -" assert_eq!(p2, Point::new(12, 19));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_left_most_point() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -" assert_eq!(poly.left_most_point(), Some(p1));\n" -" }\n" -"\n" -" #[test]\n" -" fn test_polygon_iter() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" -"\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" -"\n" -" let points = poly.iter().cloned().collect::>();\n" -" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" -" }\n" -"\n" -" #[test]\n" -" fn test_shape_perimeters() {\n" -" let mut poly = Polygon::new();\n" -" poly.add_point(Point::new(12, 13));\n" -" poly.add_point(Point::new(17, 11));\n" -" poly.add_point(Point::new(16, 16));\n" -" let shapes = vec![\n" -" Shape::from(poly),\n" -" Shape::from(Circle::new(Point::new(10, 20), 5)),\n" -" ];\n" -" let perimeters = shapes\n" -" .iter()\n" -" .map(Shape::perimeter)\n" -" .map(round_two_digits)\n" -" .collect::>();\n" -" assert_eq!(perimeters, vec![15.48, 31.42]);\n" -" }\n" -"}\n" -"\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" -"```" +"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " +"algorithm." msgstr "" -#: src/exercises/day-2/points-polygons.md:117 -msgid "" -"Since the method signatures are missing from the problem statements, the key " -"part of the exercise is to specify those correctly. You don't have to modify " -"the tests." +#: src/std.md:19 +msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." msgstr "" -#: src/exercises/day-2/points-polygons.md:120 -msgid "Other interesting parts of the exercise:" +#: src/std.md:21 +msgid "" +"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " +"data." msgstr "" -#: src/exercises/day-2/points-polygons.md:122 +#: src/std.md:25 msgid "" -"Derive a `Copy` trait for some structs, as in tests the methods sometimes " -"don't borrow their arguments." +"In fact, Rust contains several layers of the Standard Library: `core`, " +"`alloc` and `std`. " msgstr "" -#: src/exercises/day-2/points-polygons.md:123 +#: src/std.md:26 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." +"`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/control-flow.md:3 +#: src/std.md:28 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." +"`alloc` includes types which require a global heap allocator, such as `Vec`, " +"`Box` and `Arc`." msgstr "" -#: src/control-flow/blocks.md:3 +#: src/std.md:29 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:" +"Embedded Rust applications often only use `core`, and sometimes `alloc`." +msgstr "" + +#: src/std/option-result.md:1 +msgid "`Option` and `Result`" +msgstr "" + +#: src/std/option-result.md:3 +msgid "The types represent optional data:" msgstr "" -#: src/control-flow/blocks.md:7 +#: src/std/option-result.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let x = {\n" -" let y = 10;\n" -" println!(\"y: {y}\");\n" -" let z = {\n" -" let w = {\n" -" 3 + 4\n" -" };\n" -" println!(\"w: {w}\");\n" -" y * w\n" -" };\n" -" println!(\"z: {z}\");\n" -" z - y\n" -" };\n" -" println!(\"x: {x}\");\n" +" let numbers = vec![10, 20, 30];\n" +" let first: Option<&i8> = numbers.first();\n" +" println!(\"first: {first:?}\");\n" +"\n" +" let arr: Result<[i8; 3], Vec> = numbers.try_into();\n" +" println!(\"arr: {arr:?}\");\n" "}\n" "```" msgstr "" -#: src/control-flow/blocks.md:26 -msgid "" -"If the last expression ends with `;`, then the resulting value and type is " -"`()`." +#: src/std/option-result.md:18 +msgid "`Option` and `Result` are widely used not just in the standard library." msgstr "" -#: src/control-flow/blocks.md:28 -msgid "" -"The same rule is used for functions: the value of the function body is the " -"return value:" +#: src/std/option-result.md:19 +msgid "`Option<&T>` has zero space overhead compared to `&T`." msgstr "" -#: src/control-flow/blocks.md:31 +#: src/std/option-result.md:20 msgid "" -"```rust,editable\n" -"fn double(x: i32) -> i32 {\n" -" x + x\n" -"}\n" -"\n" -"fn main() {\n" -" println!(\"doubled: {}\", double(7));\n" -"}\n" -"```" +"`Result` is the standard type to implement error handling as we will see on " +"Day 3." msgstr "" -#: src/control-flow/blocks.md:44 +#: src/std/option-result.md:21 msgid "" -"The point of this slide is to show that blocks have a type and value in " -"Rust. " +"`try_into` attempts to convert the vector into a fixed-sized array. This can " +"fail:" msgstr "" -#: src/control-flow/blocks.md:45 +#: src/std/option-result.md:22 msgid "" -"You can show how the value of the block changes by changing the last line in " -"the block. For instance, adding/removing a semicolon or using a `return`." +"If the vector has the right size, `Result::Ok` is returned with the array." msgstr "" -#: src/control-flow/if-expressions.md:1 -msgid "`if` expressions" +#: src/std/option-result.md:23 +msgid "Otherwise, `Result::Err` is returned with the original vector." msgstr "" -#: src/control-flow/if-expressions.md:3 +#: src/std/string.md:3 msgid "" -"You use [`if` expressions](https://doc.rust-lang.org/reference/expressions/" -"if-expr.html#if-expressions) exactly like `if` statements in other languages:" +"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " +"standard heap-allocated growable UTF-8 string buffer:" msgstr "" -#: src/control-flow/if-expressions.md:7 +#: src/std/string.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let mut x = 10;\n" -" if x % 2 == 0 {\n" -" x = x / 2;\n" -" } else {\n" -" x = 3 * x + 1;\n" -" }\n" +" 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/control-flow/if-expressions.md:18 +#: src/std/string.md:22 msgid "" -"In addition, you can use `if` as an expression. The last expression of each " -"block becomes the value of the `if` expression:" +"`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/control-flow/if-expressions.md:22 +#: src/std/string.md:30 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let mut x = 10;\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -"}\n" -"```" +"`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/control-flow/if-expressions.md:35 +#: src/std/string.md:31 msgid "" -"Because `if` is an expression and must have a particular type, both of its " -"branch blocks must have the same type. Consider showing what happens if you " -"add `;` after `x / 2` in the second example." +"`String::len` returns the size of the `String` in bytes (which can be " +"different from its length in characters)." msgstr "" -#: src/control-flow/if-let-expressions.md:1 -msgid "`if let` expressions" +#: src/std/string.md:32 +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)." msgstr "" -#: src/control-flow/if-let-expressions.md:3 +#: src/std/string.md:33 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:" +"When people refer to strings they could either be talking about `&str` or " +"`String`." msgstr "" -#: src/control-flow/if-let-expressions.md:7 +#: src/std/string.md:34 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let arg = std::env::args().next();\n" -" if let Some(value) = arg {\n" -" println!(\"Program name: {value}\");\n" -" } else {\n" -" println!(\"Missing name?\");\n" -" }\n" -"}\n" -"```" +"When a type implements `Deref`, the compiler will let you " +"transparently call methods from `T`." msgstr "" -#: src/control-flow/if-let-expressions.md:18 -#: src/control-flow/while-let-expressions.md:21 -#: src/control-flow/match-expressions.md:23 +#: src/std/string.md:35 msgid "" -"See [pattern matching](../pattern-matching.md) for more details on patterns " -"in Rust." +"`String` implements `Deref` which transparently gives it " +"access to `str`'s methods." msgstr "" -#: src/control-flow/if-let-expressions.md:23 +#: src/std/string.md:36 +msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1;`." +msgstr "" + +#: src/std/string.md:37 msgid "" -"Unlike `match`, `if let` does not have to cover all branches. This can make " -"it more concise than `match`." +"`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/control-flow/if-let-expressions.md:24 -msgid "A common usage is handling `Some` values when working with `Option`." +#: src/std/string.md:38 +msgid "Compare the different ways to index a `String`:" msgstr "" -#: src/control-flow/if-let-expressions.md:25 +#: src/std/string.md:39 msgid "" -"Unlike `match`, `if let` does not support guard clauses for pattern matching." +"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " +"out-of-bounds." msgstr "" -#: src/control-flow/if-let-expressions.md:26 +#: src/std/string.md:40 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, execute a block which is required to abort " -"normal control flow (with `panic`/`return`/`break`/`continue`):" +"To a substring by using `s3[0..4]`, where that slice is on character " +"boundaries or not." msgstr "" -#: src/control-flow/if-let-expressions.md:28 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" println!(\"{:?}\", second_word_to_upper(\"foo bar\"));\n" -"}\n" -" \n" -"fn second_word_to_upper(s: &str) -> Option {\n" -" let mut it = s.split(' ');\n" -" let (Some(_), Some(item)) = (it.next(), it.next()) else {\n" -" return None;\n" -" };\n" -" Some(item.to_uppercase())\n" -"}\n" -"```" -msgstr "" - -#: src/control-flow/while-expressions.md:1 -msgid "`while` loops" +#: src/std/vec.md:1 +msgid "`Vec`" msgstr "" -#: src/control-flow/while-expressions.md:3 +#: src/std/vec.md:3 msgid "" -"The [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-" -"expr.html#predicate-loops) works very similar to other languages:" +"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " +"resizable heap-allocated buffer:" msgstr "" -#: src/control-flow/while-expressions.md:6 +#: src/std/vec.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let mut x = 10;\n" -" while x != 1 {\n" -" x = if x % 2 == 0 {\n" -" x / 2\n" -" } else {\n" -" 3 * x + 1\n" -" };\n" -" }\n" -" println!(\"Final x: {x}\");\n" +" let mut v1 = Vec::new();\n" +" v1.push(42);\n" +" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\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" +"\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/control-flow/while-let-expressions.md:1 -msgid "`while let` loops" +#: src/std/vec.md:29 +msgid "" +"`Vec` implements [`Deref`](https://doc.rust-lang.org/std/vec/" +"struct.Vec.html#deref-methods-%5BT%5D), which means that you can call slice " +"methods on a `Vec`." msgstr "" -#: src/control-flow/while-let-expressions.md:3 +#: src/std/vec.md:37 msgid "" -"Like with `if let`, there is a [`while let`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#predicate-pattern-loops) variant which " -"repeatedly tests a value against a pattern:" +"`Vec` 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/control-flow/while-let-expressions.md:6 +#: src/std/vec.md:40 msgid "" -"```rust,editable\n" -"fn main() {\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" -"```" +"Notice how `Vec` is a generic type too, but you don't have to specify `T` " +"explicitly. As always with Rust type inference, the `T` was established " +"during the first `push` call." msgstr "" -#: src/control-flow/while-let-expressions.md:17 +#: src/std/vec.md:42 msgid "" -"Here the iterator returned by `v.into_iter()` will return a `Option` on " -"every call to `next()`. It returns `Some(x)` until it is done, after which " -"it will return `None`. The `while let` lets us keep iterating through all " -"items." +"`vec![...]` is a canonical macro to use instead of `Vec::new()` and it " +"supports adding initial elements to the vector." msgstr "" -#: src/control-flow/while-let-expressions.md:26 +#: src/std/vec.md:44 msgid "" -"Point out that the `while let` loop will keep going as long as the value " -"matches the pattern." +"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/control-flow/while-let-expressions.md:27 +#: src/std/vec.md:46 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." +"Show iterating over a vector and mutating the value: `for e in &mut v { *e " +"+= 50; }`" msgstr "" -#: src/control-flow/for-expressions.md:1 -msgid "`for` loops" +#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 +msgid "`HashMap`" 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-expressions.md). It will " -"automatically call `into_iter()` on the expression and then iterate over it:" +#: src/std/hashmap.md:3 +msgid "Standard hash map with protection against HashDoS attacks:" msgstr "" -#: src/control-flow/for-expressions.md:7 +#: src/std/hashmap.md:5 msgid "" "```rust,editable\n" +"use std::collections::HashMap;\n" +"\n" "fn main() {\n" -" let v = vec![10, 20, 30];\n" +" let mut page_counts = HashMap::new();\n" +" page_counts.insert(\"Adventures of Huckleberry Finn\".to_string(), " +"207);\n" +" page_counts.insert(\"Grimms' Fairy Tales\".to_string(), 751);\n" +" page_counts.insert(\"Pride and Prejudice\".to_string(), 303);\n" "\n" -" for x in v {\n" -" println!(\"x: {x}\");\n" +" 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 i in (0..10).step_by(2) {\n" -" println!(\"i: {i}\");\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/control-flow/for-expressions.md:21 -msgid "You can use `break` and `continue` here as usual." -msgstr "" - -#: src/control-flow/for-expressions.md:25 -msgid "Index iteration is not a special syntax in Rust for just that case." +#: src/std/hashmap.md:38 +msgid "" +"`HashMap` is not defined in the prelude and needs to be brought into scope." msgstr "" -#: src/control-flow/for-expressions.md:26 -msgid "`(0..10)` is a range that implements an `Iterator` trait. " +#: 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/control-flow/for-expressions.md:27 +#: src/std/hashmap.md:41 msgid "" -"`step_by` is a method that returns another `Iterator` that skips every other " -"element. " +"```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/control-flow/for-expressions.md:28 -msgid "" -"Modify the elements in the vector and explain the compiler errors. Change " -"vector `v` to be mutable and the for loop to `for x in v.iter_mut()`." +#: src/std/hashmap.md:49 +msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." msgstr "" -#: src/control-flow/loop-expressions.md:1 -msgid "`loop` expressions" +#: 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:" msgstr "" -#: src/control-flow/loop-expressions.md:3 +#: src/std/hashmap.md:52 msgid "" -"Finally, there is a [`loop` keyword](https://doc.rust-lang.org/reference/" -"expressions/loop-expr.html#infinite-loops) which creates an endless loop." +"```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/control-flow/loop-expressions.md:6 -msgid "Here you must either `break` or `return` to stop the loop:" +#: src/std/hashmap.md:59 +msgid "" +"Alternatively HashMap can be built from any `Iterator` which yields key-" +"value tuples." msgstr "" -#: src/control-flow/loop-expressions.md:8 +#: src/std/hashmap.md:60 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" -"```" +"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/control-flow/loop-expressions.md:27 -msgid "Break the `loop` with a value (e.g. `break 8`) and print it out." +#: src/std/hashmap.md:62 +msgid "" +"Try removing `to_string()` from the example above and see if it still " +"compiles. Where do you think we might run into issues?" msgstr "" -#: src/control-flow/loop-expressions.md:28 +#: src/std/hashmap.md:64 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)." +"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/control-flow/match-expressions.md:1 -msgid "`match` expressions" +#: src/std/box.md:1 +msgid "`Box`" msgstr "" -#: src/control-flow/match-expressions.md:3 +#: src/std/box.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:" +"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned " +"pointer to data on the heap:" msgstr "" -#: src/control-flow/match-expressions.md:7 +#: src/std/box.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" match std::env::args().next().as_deref() {\n" -" Some(\"cat\") => println!(\"Will do cat things\"),\n" -" Some(\"ls\") => println!(\"Will ls some files\"),\n" -" Some(\"mv\") => println!(\"Let's move some files\"),\n" -" Some(\"rm\") => println!(\"Uh, dangerous!\"),\n" -" None => println!(\"Hmm, no program name?\"),\n" -" _ => println!(\"Unknown program name!\"),\n" -" }\n" +" let five = Box::new(5);\n" +" println!(\"five: {}\", *five);\n" "}\n" "```" msgstr "" -#: src/control-flow/match-expressions.md:20 +#: src/std/box.md:26 msgid "" -"Like `if let`, each match arm must have the same type. The type is the last " -"expression of the block, if any. In the example above, the type is `()`." +"`Box` implements `Deref`, which means that you can [call " +"methods from `T` directly on a `Box`](https://doc.rust-lang.org/std/ops/" +"trait.Deref.html#more-on-deref-coercion)." msgstr "" -#: src/control-flow/match-expressions.md:28 -msgid "Save the match expression to a variable and print it out." +#: src/std/box.md:34 +msgid "" +"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " +"not null. " msgstr "" -#: src/control-flow/match-expressions.md:29 -msgid "Remove `.as_deref()` and explain the error." +#: 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/control-flow/match-expressions.md:30 -msgid "" -"`std::env::args().next()` returns an `Option`, but we cannot match " -"against `String`." +#: src/std/box.md:36 +msgid "A `Box` can be useful when you:" msgstr "" -#: src/control-flow/match-expressions.md:31 +#: src/std/box.md:37 msgid "" -"`as_deref()` transforms an `Option` to `Option<&T::Target>`. In our case, " -"this turns `Option` into `Option<&str>`." -msgstr "" - -#: src/control-flow/match-expressions.md:32 -msgid "" -"We can now use pattern matching to match against the `&str` inside `Option`." -msgstr "" - -#: src/control-flow/break-continue.md:1 -msgid "`break` and `continue`" +"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/control-flow/break-continue.md:3 +#: src/std/box.md:38 msgid "" -"If you want to exit a loop early, use [`break`](https://doc.rust-lang.org/" -"reference/expressions/loop-expr.html#break-expressions)," +"want to transfer ownership of a large amount of data. To avoid copying large " +"amounts of data on the stack, instead store the data on the heap in a `Box` " +"so only the pointer is moved." msgstr "" -#: src/control-flow/break-continue.md:4 -msgid "" -"If you want to immediately start the next iteration use [`continue`](https://" -"doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions)." +#: src/std/box-recursive.md:1 +msgid "Box with Recursive Data Structures" msgstr "" -#: src/control-flow/break-continue.md:7 +#: src/std/box-recursive.md:3 msgid "" -"Both `continue` and `break` can optionally take a label argument which is " -"used to break out of nested loops:" +"Recursive data types or data types with dynamic sizes need to use a `Box`:" msgstr "" -#: src/control-flow/break-continue.md:10 +#: 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 v = vec![10, 20, 30];\n" -" let mut iter = v.into_iter();\n" -" 'outer: while let Some(x) = iter.next() {\n" -" println!(\"x: {x}\");\n" -" let mut i = 0;\n" -" while i < x {\n" -" println!(\"x: {x}, i: {i}\");\n" -" i += 1;\n" -" if i == 3 {\n" -" break 'outer;\n" -" }\n" -" }\n" -" }\n" +" let list: List = List::Cons(1, Box::new(List::Cons(2, Box::" +"new(List::Nil))));\n" +" println!(\"{list:?}\");\n" "}\n" "```" msgstr "" -#: src/control-flow/break-continue.md:28 +#: src/std/box-recursive.md:18 msgid "" -"In this case we break the outer loop after 3 iterations of the inner loop." +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"- -.\n" +": : : :\n" +": " +"list : : :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": | Cons | 1 | o--+----+-----+--->| Cons | 2 | o--+--->| Nil | // | // " +"| :\n" +": +------+----+----+ : : +------+----+----+ +------+----+----" +"+ :\n" +": : : :\n" +": : : :\n" +"'- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"- -'\n" +"```" msgstr "" -#: src/std.md:3 +#: src/std/box-recursive.md:33 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:" +"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/std.md:9 +#: src/std/box-recursive.md:36 msgid "" -"[`Option` and `Result`](std/option-result.md) types: used for optional " -"values and [error handling](error-handling.md)." +"`Box` solves this problem as it has the same size as a regular pointer and " +"just points at the next element of the `List` in the heap." msgstr "" -#: src/std.md:12 -msgid "[`String`](std/string.md): the default string type used for owned data." +#: src/std/box-recursive.md:39 +msgid "" +"Remove the `Box` in the List definition and show the compiler error. " +"\"Recursive with indirection\" is a hint you might want to use a Box or " +"reference of some kind, instead of storing a value directly." msgstr "" -#: src/std.md:14 -msgid "[`Vec`](std/vec.md): a standard extensible vector." +#: 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/std.md:16 +#: src/std/box-niche.md:19 msgid "" -"[`HashMap`](std/hashmap.md): a hash map type with a configurable hashing " -"algorithm." +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " +"-.\n" +": : : :\n" +": " +"list : : :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " +"| :\n" +": +----+----+ : : +----+----+ +----+------" +"+ :\n" +": : : :\n" +": : : :\n" +"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " +"-'\n" +"```" msgstr "" -#: src/std.md:19 -msgid "[`Box`](std/box.md): an owned pointer for heap-allocated data." +#: src/std/rc.md:1 +msgid "`Rc`" msgstr "" -#: src/std.md:21 +#: src/std/rc.md:3 msgid "" -"[`Rc`](std/rc.md): a shared reference-counted pointer for heap-allocated " -"data." +"[`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/std.md:25 +#: src/std/rc.md:6 msgid "" -"In fact, Rust contains several layers of the Standard Library: `core`, " -"`alloc` and `std`. " +"```rust,editable\n" +"use std::rc::Rc;\n" +"\n" +"fn main() {\n" +" let mut a = Rc::new(10);\n" +" let mut b = Rc::clone(&a);\n" +"\n" +" println!(\"a: {a}\");\n" +" println!(\"b: {b}\");\n" +"}\n" +"```" msgstr "" -#: src/std.md:26 +#: src/std/rc.md:18 msgid "" -"`core` includes the most basic types and functions that don't depend on " -"`libc`, allocator or even the presence of an operating system. " +"See [`Arc`](../concurrency/shared_state/arc.md) and [`Mutex`](https://doc." +"rust-lang.org/std/sync/struct.Mutex.html) if you are in a multi-threaded " +"context." msgstr "" -#: src/std.md:28 +#: src/std/rc.md:19 msgid "" -"`alloc` includes types which require a global heap allocator, such as `Vec`, " -"`Box` and `Arc`." +"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/std.md:29 +#: src/std/rc.md:29 msgid "" -"Embedded Rust applications often only use `core`, and sometimes `alloc`." -msgstr "" - -#: src/std/option-result.md:1 -msgid "`Option` and `Result`" +"`Rc`'s count ensures that its contained value is valid for as long as there " +"are references." msgstr "" -#: src/std/option-result.md:3 -msgid "The types represent optional data:" +#: src/std/rc.md:30 +msgid "`Rc` in Rust is like `std::shared_ptr` in C++." msgstr "" -#: src/std/option-result.md:5 +#: src/std/rc.md:31 msgid "" -"```rust,editable\n" -"fn main() {\n" -" let numbers = vec![10, 20, 30];\n" -" let first: Option<&i8> = numbers.first();\n" -" println!(\"first: {first:?}\");\n" -"\n" -" let idx: Result = numbers.binary_search(&10);\n" -" println!(\"idx: {idx:?}\");\n" -"}\n" -"```" +"`Rc::clone` is cheap: it creates a pointer to the same allocation and " +"increases the reference count. Does not make a deep clone and can generally " +"be ignored when looking for performance issues in code." msgstr "" -#: src/std/option-result.md:18 -msgid "`Option` and `Result` are widely used not just in the standard library." +#: 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/std/option-result.md:19 -msgid "`Option<&T>` has zero space overhead compared to `&T`." +#: src/std/rc.md:33 +msgid "Use `Rc::strong_count` to check the reference count." msgstr "" -#: src/std/option-result.md:20 +#: src/std/rc.md:34 msgid "" -"`Result` is the standard type to implement error handling as we will see on " -"Day 3." -msgstr "" - -#: src/std/option-result.md:21 -msgid "`binary_search` returns `Result`." +"`Rc::downgrade` gives you a _weakly reference-counted_ object to create " +"cycles that will be dropped properly (likely in combination with `RefCell`, " +"on the next slide)." msgstr "" -#: src/std/option-result.md:22 -msgid "If found, `Result::Ok` holds the index where the element is found." +#: src/std/cell.md:1 +msgid "`Cell` and `RefCell`" msgstr "" -#: src/std/option-result.md:23 +#: src/std/cell.md:3 msgid "" -"Otherwise, `Result::Err` contains the index where such an element should be " -"inserted." +"[`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/std/string.md:3 +#: src/std/cell.md:8 msgid "" -"[`String`](https://doc.rust-lang.org/std/string/struct.String.html) is the " -"standard heap-allocated growable UTF-8 string buffer:" +"`Cell` is typically used for simple types, as it requires copying or moving " +"values. More complex interior types typically use `RefCell`, which tracks " +"shared and exclusive references at runtime and panics if they are misused." msgstr "" -#: src/std/string.md:5 +#: src/std/cell.md:12 msgid "" "```rust,editable\n" -"fn main() {\n" -" let mut s1 = String::new();\n" -" s1.push_str(\"Hello\");\n" -" println!(\"s1: len = {}, capacity = {}\", s1.len(), s1.capacity());\n" +"use std::cell::RefCell;\n" +"use std::rc::Rc;\n" "\n" -" let mut s2 = String::with_capacity(s1.len() + 1);\n" -" s2.push_str(&s1);\n" -" s2.push('!');\n" -" println!(\"s2: len = {}, capacity = {}\", s2.len(), s2.capacity());\n" +"#[derive(Debug, Default)]\n" +"struct Node {\n" +" value: i64,\n" +" children: Vec>>,\n" +"}\n" "\n" -" let s3 = String::from(\"🇨🇭\");\n" -" println!(\"s3: len = {}, number of chars = {}\", s3.len(),\n" -" s3.chars().count());\n" +"impl Node {\n" +" fn new(value: i64) -> Rc> {\n" +" Rc::new(RefCell::new(Node { value, ..Node::default() }))\n" +" }\n" +"\n" +" fn sum(&self) -> i64 {\n" +" self.value + self.children.iter().map(|c| c.borrow().sum()).sum::" +"()\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let root = Node::new(1);\n" +" root.borrow_mut().children.push(Node::new(5));\n" +" let subtree = Node::new(10);\n" +" subtree.borrow_mut().children.push(Node::new(11));\n" +" subtree.borrow_mut().children.push(Node::new(12));\n" +" root.borrow_mut().children.push(subtree);\n" +"\n" +" println!(\"graph: {root:#?}\");\n" +" println!(\"graph sum: {}\", root.borrow().sum());\n" "}\n" "```" msgstr "" -#: src/std/string.md:22 +#: src/std/cell.md:47 msgid "" -"`String` implements [`Deref`](https://doc.rust-lang.org/std/" -"string/struct.String.html#deref-methods-str), which means that you can call " -"all `str` methods on a `String`." +"If we were using `Cell` instead of `RefCell` in this example, we would have " +"to move the `Node` out of the `Rc` to push children, then move it back in. " +"This is safe because there's always one, un-referenced value in the cell, " +"but it's not ergonomic." msgstr "" -#: src/std/string.md:30 +#: src/std/cell.md:48 msgid "" -"`String::new` returns a new empty string, use `String::with_capacity` when " -"you know how much data you want to push to the string." +"To do anything with a Node, you must call a `RefCell` method, usually " +"`borrow` or `borrow_mut`." msgstr "" -#: src/std/string.md:31 +#: src/std/cell.md:49 msgid "" -"`String::len` returns the size of the `String` in bytes (which can be " -"different from its length in characters)." +"Demonstrate that reference loops can be created by adding `root` to `subtree." +"children` (don't try to print it!)." msgstr "" -#: src/std/string.md:32 +#: src/std/cell.md:50 msgid "" -"`String::chars` returns an iterator over the actual characters. Note that a " -"`char` can be different from what a human will consider a \"character\" due " -"to [grapheme clusters](https://docs.rs/unicode-segmentation/latest/" -"unicode_segmentation/struct.Graphemes.html)." +"To demonstrate a runtime panic, add a `fn inc(&mut self)` that increments " +"`self.value` and calls the same method on its children. This will panic in " +"the presence of the reference loop, with `thread 'main' panicked at 'already " +"borrowed: BorrowMutError'`." msgstr "" -#: src/std/string.md:33 -msgid "" -"When people refer to strings they could either be talking about `&str` or " -"`String`." +#: src/modules.md:3 +msgid "We have seen how `impl` blocks let us namespace functions to a type." msgstr "" -#: src/std/string.md:34 -msgid "" -"When a type implements `Deref`, the compiler will let you " -"transparently call methods from `T`." +#: src/modules.md:5 +msgid "Similarly, `mod` lets us namespace types and functions:" msgstr "" -#: src/std/string.md:35 +#: src/modules.md:7 msgid "" -"`String` implements `Deref` which transparently gives it " -"access to `str`'s methods." -msgstr "" - -#: src/std/string.md:36 -msgid "Write and compare `let s3 = s1.deref();` and `let s3 = &*s1`;." +"```rust,editable\n" +"mod foo {\n" +" pub fn do_something() {\n" +" println!(\"In the foo module\");\n" +" }\n" +"}\n" +"\n" +"mod bar {\n" +" pub fn do_something() {\n" +" println!(\"In the bar module\");\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" foo::do_something();\n" +" bar::do_something();\n" +"}\n" +"```" msgstr "" -#: src/std/string.md:37 +#: src/modules.md:28 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." +"Packages provide functionality and include a `Cargo.toml` file that " +"describes how to build a bundle of 1+ crates." msgstr "" -#: src/std/string.md:38 -msgid "Compare the different ways to index a `String`:" +#: src/modules.md:29 +msgid "" +"Crates are a tree of modules, where a binary crate creates an executable and " +"a library crate compiles to a library." msgstr "" -#: src/std/string.md:39 -msgid "" -"To a character by using `s3.chars().nth(i).unwrap()` where `i` is in-bound, " -"out-of-bounds." +#: src/modules.md:30 +msgid "Modules define organization, scope, and are the focus of this section." msgstr "" -#: src/std/string.md:40 -msgid "" -"To a substring by using `s3[0..4]`, where that slice is on character " -"boundaries or not." +#: src/modules/visibility.md:3 +msgid "Modules are a privacy boundary:" msgstr "" -#: src/std/vec.md:1 -msgid "`Vec`" +#: src/modules/visibility.md:5 +msgid "Module items are private by default (hides implementation details)." msgstr "" -#: src/std/vec.md:3 +#: src/modules/visibility.md:6 +msgid "Parent and sibling items are always visible." +msgstr "" + +#: src/modules/visibility.md:7 msgid "" -"[`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) is the standard " -"resizable heap-allocated buffer:" +"In other words, if an item is visible in module `foo`, it's visible in all " +"the descendants of `foo`." msgstr "" -#: src/std/vec.md:5 +#: src/modules/visibility.md:10 msgid "" "```rust,editable\n" -"fn main() {\n" -" let mut v1 = Vec::new();\n" -" v1.push(42);\n" -" println!(\"v1: len = {}, capacity = {}\", v1.len(), v1.capacity());\n" +"mod outer {\n" +" fn private() {\n" +" println!(\"outer::private\");\n" +" }\n" "\n" -" let mut v2 = Vec::with_capacity(v1.len() + 1);\n" -" v2.extend(v1.iter());\n" -" v2.push(9999);\n" -" println!(\"v2: len = {}, capacity = {}\", v2.len(), v2.capacity());\n" +" pub fn public() {\n" +" println!(\"outer::public\");\n" +" }\n" "\n" -" // Canonical macro to initialize a vector with elements.\n" -" let mut v3 = vec![0, 0, 1, 2, 3, 4];\n" +" mod inner {\n" +" fn private() {\n" +" println!(\"outer::inner::private\");\n" +" }\n" "\n" -" // Retain only the even elements.\n" -" v3.retain(|x| x % 2 == 0);\n" -" println!(\"{v3:?}\");\n" +" pub fn public() {\n" +" println!(\"outer::inner::public\");\n" +" super::private();\n" +" }\n" +" }\n" +"}\n" "\n" -" // Remove consecutive duplicates.\n" -" v3.dedup();\n" -" println!(\"{v3:?}\");\n" +"fn main() {\n" +" outer::public();\n" "}\n" "```" msgstr "" -#: src/std/vec.md:29 -msgid "" -"`Vec` implements [`Deref`](https://doc.rust-lang.org/std/vec/" -"struct.Vec.html#deref-methods-%5BT%5D), which means that you can call slice " -"methods on a `Vec`." +#: src/modules/visibility.md:39 +msgid "Use the `pub` keyword to make modules public." msgstr "" -#: src/std/vec.md:37 +#: src/modules/visibility.md:41 msgid "" -"`Vec` is a type of collection, along with `String` and `HashMap`. The data " -"it contains is stored on the heap. This means the amount of data doesn't " -"need to be known at compile time. It can grow or shrink at runtime." +"Additionally, there are advanced `pub(...)` specifiers to restrict the scope " +"of public visibility." msgstr "" -#: src/std/vec.md:40 +#: src/modules/visibility.md:43 msgid "" -"Notice how `Vec` is a generic type too, but you don't have to specify `T` " -"explicitly. As always with Rust type inference, the `T` was established " -"during the first `push` call." +"See the [Rust Reference](https://doc.rust-lang.org/reference/visibility-and-" +"privacy.html#pubin-path-pubcrate-pubsuper-and-pubself)." msgstr "" -#: src/std/vec.md:42 -msgid "" -"`vec![...]` is a canonical macro to use instead of `Vec::new()` and it " -"supports adding initial elements to the vector." +#: src/modules/visibility.md:44 +msgid "Configuring `pub(crate)` visibility is a common pattern." msgstr "" -#: src/std/vec.md:44 -msgid "" -"To index the vector you use `[` `]`, but they will panic if out of bounds. " -"Alternatively, using `get` will return an `Option`. The `pop` function will " -"remove the last element." +#: src/modules/visibility.md:45 +msgid "Less commonly, you can give visibility to a specific path." msgstr "" -#: src/std/vec.md:46 +#: src/modules/visibility.md:46 msgid "" -"Show iterating over a vector and mutating the value: `for e in &mut v { *e " -"+= 50; }`" +"In any case, visibility must be granted to an ancestor module (and all of " +"its descendants)." msgstr "" -#: src/std/hashmap.md:1 src/bare-metal/no_std.md:46 -msgid "`HashMap`" +#: src/modules/paths.md:3 +msgid "Paths are resolved as follows:" msgstr "" -#: src/std/hashmap.md:3 -msgid "Standard hash map with protection against HashDoS attacks:" +#: src/modules/paths.md:5 +msgid "As a relative path:" msgstr "" -#: src/std/hashmap.md:5 -msgid "" -"```rust,editable\n" -"use std::collections::HashMap;\n" -"\n" -"fn main() {\n" -" let mut page_counts = HashMap::new();\n" -" page_counts.insert(\"Adventures of Huckleberry Finn\".to_string(), " -"207);\n" -" page_counts.insert(\"Grimms' Fairy Tales\".to_string(), 751);\n" -" page_counts.insert(\"Pride and Prejudice\".to_string(), 303);\n" -"\n" -" if !page_counts.contains_key(\"Les Misérables\") {\n" -" println!(\"We know about {} books, but not Les Misérables.\",\n" -" page_counts.len());\n" -" }\n" -"\n" -" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " -"Wonderland\"] {\n" -" match page_counts.get(book) {\n" -" Some(count) => println!(\"{book}: {count} pages\"),\n" -" None => println!(\"{book} is unknown.\")\n" -" }\n" -" }\n" -"\n" -" // Use the .entry() method to insert a value if nothing is found.\n" -" for book in [\"Pride and Prejudice\", \"Alice's Adventure in " -"Wonderland\"] {\n" -" let page_count: &mut i32 = page_counts.entry(book.to_string())." -"or_insert(0);\n" -" *page_count += 1;\n" -" }\n" -"\n" -" println!(\"{page_counts:#?}\");\n" -"}\n" -"```" +#: src/modules/paths.md:6 +msgid "`foo` or `self::foo` refers to `foo` in the current module," msgstr "" -#: src/std/hashmap.md:38 -msgid "" -"`HashMap` is not defined in the prelude and needs to be brought into scope." +#: src/modules/paths.md:7 +msgid "`super::foo` refers to `foo` in the parent module." msgstr "" -#: src/std/hashmap.md:39 -msgid "" -"Try the following lines of code. The first line will see if a book is in the " -"hashmap and if not return an alternative value. The second line will insert " -"the alternative value in the hashmap if the book is not found." +#: src/modules/paths.md:9 +msgid "As an absolute path:" msgstr "" -#: src/std/hashmap.md:41 -msgid "" -"```rust,ignore\n" -" let pc1 = page_counts\n" -" .get(\"Harry Potter and the Sorcerer's Stone \")\n" -" .unwrap_or(&336);\n" -" let pc2 = page_counts\n" -" .entry(\"The Hunger Games\".to_string())\n" -" .or_insert(374);\n" -"```" +#: src/modules/paths.md:10 +msgid "`crate::foo` refers to `foo` in the root of the current crate," msgstr "" -#: src/std/hashmap.md:49 -msgid "Unlike `vec!`, there is unfortunately no standard `hashmap!` macro." +#: src/modules/paths.md:11 +msgid "`bar::foo` refers to `foo` in the `bar` crate." msgstr "" -#: src/std/hashmap.md:50 +#: src/modules/paths.md:13 msgid "" -"Although, since Rust 1.56, HashMap implements [`From<[(K, V); N]>`](https://" -"doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#impl-" -"From%3C%5B(K,+V);+N%5D%3E-for-HashMap%3CK,+V,+RandomState%3E), which allows " -"us to easily initialize a hash map from a literal array:" +"A module can bring symbols from another module into scope with `use`. You " +"will typically see something like this at the top of each module:" msgstr "" -#: src/std/hashmap.md:52 +#: src/modules/filesystem.md:3 msgid "" -"```rust,ignore\n" -" let page_counts = HashMap::from([\n" -" (\"Harry Potter and the Sorcerer's Stone\".to_string(), 336),\n" -" (\"The Hunger Games\".to_string(), 374),\n" -" ]);\n" -"```" +"Omitting the module content will tell Rust to look for it in another file:" msgstr "" -#: src/std/hashmap.md:59 +#: src/modules/filesystem.md:9 msgid "" -"Alternatively HashMap can be built from any `Iterator` which yields key-" -"value tuples." +"This tells rust that the `garden` module content is found at `src/garden." +"rs`. Similarly, a `garden::vegetables` module can be found at `src/garden/" +"vegetables.rs`." msgstr "" -#: src/std/hashmap.md:60 -msgid "" -"We are showing `HashMap`, and avoid using `&str` as key to make " -"examples easier. Using references in collections can, of course, be done, " -"but it can lead into complications with the borrow checker." +#: src/modules/filesystem.md:12 +msgid "The `crate` root is in:" msgstr "" -#: src/std/hashmap.md:62 -msgid "" -"Try removing `to_string()` from the example above and see if it still " -"compiles. Where do you think we might run into issues?" +#: src/modules/filesystem.md:14 +msgid "`src/lib.rs` (for a library crate)" msgstr "" -#: src/std/box.md:1 -msgid "`Box`" +#: src/modules/filesystem.md:15 +msgid "`src/main.rs` (for a binary crate)" msgstr "" -#: src/std/box.md:3 +#: src/modules/filesystem.md:17 msgid "" -"[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) is an owned " -"pointer to data on the heap:" +"Modules defined in files can be documented, too, using \"inner doc " +"comments\". These document the item that contains them -- in this case, a " +"module." msgstr "" -#: src/std/box.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let five = Box::new(5);\n" -" println!(\"five: {}\", *five);\n" -"}\n" -"```" -msgstr "" - -#: src/std/box.md:13 +#: src/modules/filesystem.md:20 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - -. .- - - - - - -.\n" -": : : :\n" -": five : : :\n" -": +-----+ : : +-----+ :\n" -": | o---|---+-----+-->| 5 | :\n" -": +-----+ : : +-----+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - -' `- - - - - - -'\n" +"```rust,editable,compile_fail\n" +"//! This module implements the garden, including a highly performant " +"germination\n" +"//! implementation.\n" +"\n" +"// Re-export types from this module.\n" +"pub use seeds::SeedPacket;\n" +"pub use garden::Garden;\n" +"\n" +"/// Sow the given seed packets.\n" +"pub fn sow(seeds: Vec) { todo!() }\n" +"\n" +"/// Harvest the produce in the garden that is ready.\n" +"pub fn harvest(garden: &mut Garden) { todo!() }\n" "```" msgstr "" -#: src/std/box.md:26 -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)." -msgstr "" - -#: src/std/box.md:34 +#: src/modules/filesystem.md:37 msgid "" -"`Box` is like `std::unique_ptr` in C++, except that it's guaranteed to be " -"not null. " +"Before Rust 2018, modules needed to be located at `module/mod.rs` instead of " +"`module.rs`, and this is still a working alternative for editions after 2018." msgstr "" -#: src/std/box.md:35 +#: src/modules/filesystem.md:39 msgid "" -"In the above example, you can even leave out the `*` in the `println!` " -"statement thanks to `Deref`. " +"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/std/box.md:36 -msgid "A `Box` can be useful when you:" +#: src/modules/filesystem.md:42 +msgid "Deeper nesting can use folders, even if the main module is a file:" msgstr "" -#: src/std/box.md:37 +#: src/modules/filesystem.md:52 msgid "" -"have a type whose size that can't be known at compile time, but the Rust " -"compiler wants to know an exact size." +"The place rust will look for modules can be changed with a compiler " +"directive:" msgstr "" -#: src/std/box.md:38 +#: src/modules/filesystem.md:54 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" +"```rust,ignore\n" +"#[path = \"some/path.rs\"]\n" +"mod some_module;\n" +"```" msgstr "" -#: src/std/box-recursive.md:3 +#: src/modules/filesystem.md:59 msgid "" -"Recursive data types or data types with dynamic sizes need to use a `Box`:" +"This is useful, for example, if you would like to place tests for a module " +"in a file named `some_module_test.rs`, similar to the convention in Go." msgstr "" -#: src/std/box-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" -"```" +#: src/exercises/day-2/afternoon.md:1 +msgid "Day 2: Afternoon Exercises" msgstr "" -#: 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" -"```" +#: src/exercises/day-2/afternoon.md:3 +msgid "The exercises for this afternoon will focus on strings and iterators." msgstr "" -#: src/std/box-recursive.md:33 +#: src/exercises/day-2/iterators-and-ownership.md:3 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)." +"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/std/box-recursive.md:36 -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." +#: src/exercises/day-2/iterators-and-ownership.md:8 src/bare-metal/no_std.md:28 +msgid "`Iterator`" msgstr "" -#: src/std/box-recursive.md:39 +#: src/exercises/day-2/iterators-and-ownership.md:10 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." +"Traits are like interfaces: they describe behavior (methods) for a type. The " +"`Iterator` trait simply says that you can call `next` until you get `None` " +"back:" msgstr "" -#: src/std/box-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:" +#: src/exercises/day-2/iterators-and-ownership.md:20 +msgid "You use this trait like this:" msgstr "" -#: src/std/box-niche.md:19 +#: src/exercises/day-2/iterators-and-ownership.md:22 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - " -"-.\n" -": : : :\n" -": " -"list : : :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": | 1 | o--+-----------+-----+--->| 2 | o--+--->| // | null " -"| :\n" -": +----+----+ : : +----+----+ +----+------" -"+ :\n" -": : : :\n" -": : : :\n" -"`- - - - - - - - - - - - -' '- - - - - - - - - - - - - - - - - - - - - - " -"-'\n" +"```rust,editable\n" +"fn main() {\n" +" let v: Vec = vec![10, 20, 30];\n" +" let mut iter = v.iter();\n" +"\n" +" println!(\"v[0]: {:?}\", iter.next());\n" +" println!(\"v[1]: {:?}\", iter.next());\n" +" println!(\"v[2]: {:?}\", iter.next());\n" +" println!(\"No more items: {:?}\", iter.next());\n" +"}\n" "```" msgstr "" -#: src/std/rc.md:1 -msgid "`Rc`" -msgstr "" - -#: 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:" +#: src/exercises/day-2/iterators-and-ownership.md:34 +msgid "What is the type returned by the iterator? Test your answer here:" msgstr "" -#: src/std/rc.md:6 +#: src/exercises/day-2/iterators-and-ownership.md:36 msgid "" -"```rust,editable\n" -"use std::rc::Rc;\n" -"\n" +"```rust,editable,compile_fail\n" "fn main() {\n" -" let mut a = Rc::new(10);\n" -" let mut b = Rc::clone(&a);\n" +" let v: Vec = vec![10, 20, 30];\n" +" let mut iter = v.iter();\n" "\n" -" println!(\"a: {a}\");\n" -" println!(\"b: {b}\");\n" +" let v0: Option<..> = iter.next();\n" +" println!(\"v0: {v0:?}\");\n" "}\n" "```" msgstr "" -#: src/std/rc.md:18 -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)." +#: src/exercises/day-2/iterators-and-ownership.md:46 +msgid "Why is this type used?" msgstr "" -#: src/std/rc.md:20 -msgid "" -"See [`Arc`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) and " -"[`Mutex`](https://doc.rust-lang.org/std/rc/struct.Weak.html) if you are in a " -"multi-threaded context." +#: src/exercises/day-2/iterators-and-ownership.md:48 +msgid "`IntoIterator`" msgstr "" -#: src/std/rc.md:21 +#: src/exercises/day-2/iterators-and-ownership.md:50 msgid "" -"You can _downgrade_ a shared pointer into a \\[`Weak`\\]\\[5\\] pointer to " -"create cycles that will get dropped." +"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/std/rc.md:32 +#: src/exercises/day-2/iterators-and-ownership.md:62 msgid "" -"`Rc`'s count ensures that its contained value is valid for as long as there " -"are references." +"The syntax here means that every implementation of `IntoIterator` must " +"declare two types:" msgstr "" -#: src/std/rc.md:33 -msgid "`Rc` in Rust is like `std::shared_ptr` in C++." +#: src/exercises/day-2/iterators-and-ownership.md:65 +msgid "`Item`: the type we iterate over, such as `i8`," msgstr "" -#: src/std/rc.md:34 -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." +#: src/exercises/day-2/iterators-and-ownership.md:66 +msgid "`IntoIter`: the `Iterator` type returned by the `into_iter` method." msgstr "" -#: src/std/rc.md:35 +#: src/exercises/day-2/iterators-and-ownership.md:68 msgid "" -"`make_mut` actually clones the inner value if necessary (\"clone-on-write\") " -"and returns a mutable reference." +"Note that `IntoIter` and `Item` are linked: the iterator must have the same " +"`Item` type, which means that it returns `Option`" msgstr "" -#: src/std/rc.md:36 -msgid "Use `Rc::strong_count` to check the reference count." +#: src/exercises/day-2/iterators-and-ownership.md:71 +msgid "Like before, what is the type returned by the iterator?" msgstr "" -#: src/std/rc.md:37 +#: src/exercises/day-2/iterators-and-ownership.md:73 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." +"```rust,editable,compile_fail\n" +"fn main() {\n" +" let v: Vec = vec![String::from(\"foo\"), String::" +"from(\"bar\")];\n" +" let mut iter = v.into_iter();\n" +"\n" +" let v0: Option<..> = iter.next();\n" +" println!(\"v0: {v0:?}\");\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-2/iterators-and-ownership.md:83 +msgid "`for` Loops" msgstr "" -#: src/std/rc.md:38 +#: src/exercises/day-2/iterators-and-ownership.md:85 msgid "" -"`Rc::downgrade` gives you a _weakly reference-counted_ object to create " -"cycles that will be dropped properly (likely in combination with `RefCell`)." +"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/std/rc.md:42 +#: src/exercises/day-2/iterators-and-ownership.md:89 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" -"\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 v: Vec = vec![String::from(\"foo\"), String::" +"from(\"bar\")];\n" "\n" -" println!(\"graph: {root:#?}\");\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/modules.md:3 -msgid "We have seen how `impl` blocks let us namespace functions to a type." +#: src/exercises/day-2/iterators-and-ownership.md:103 +msgid "What is the type of `word` in each loop?" msgstr "" -#: src/modules.md:5 -msgid "Similarly, `mod` lets us namespace types and functions:" +#: src/exercises/day-2/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'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/modules.md:7 +#: src/exercises/day-2/strings-iterators.md:3 msgid "" -"```rust,editable\n" -"mod foo {\n" -" pub fn do_something() {\n" -" println!(\"In the foo module\");\n" -" }\n" +"In this exercise, you are implementing a routing component of a web server. " +"The server is configured with a number of _path prefixes_ which are matched " +"against _request paths_. The path prefixes can contain a wildcard character " +"which matches a full segment. See the unit tests below." +msgstr "" + +#: src/exercises/day-2/strings-iterators.md:8 +msgid "" +"Copy the following code to and make the tests " +"pass. Try avoiding allocating a `Vec` for your intermediate results:" +msgstr "" + +#: src/exercises/day-2/strings-iterators.md:12 +msgid "" +"```rust\n" +"// TODO: remove this when you're done with your implementation.\n" +"#![allow(unused_variables, dead_code)]\n" +"\n" +"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" +" unimplemented!()\n" "}\n" "\n" -"mod bar {\n" -" pub fn do_something() {\n" -" println!(\"In the bar module\");\n" -" }\n" +"#[test]\n" +"fn test_matches_without_wildcard() {\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/" +"abc-123\"));\n" +" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/" +"books\"));\n" +"\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n" +" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/" +"publishers\"));\n" "}\n" "\n" -"fn main() {\n" -" foo::do_something();\n" -" bar::do_something();\n" +"#[test]\n" +"fn test_matches_with_wildcard() {\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/books\"\n" +" ));\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/bar/books\"\n" +" ));\n" +" assert!(prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/books/book1\"\n" +" ));\n" +"\n" +" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/" +"publishers\"));\n" +" assert!(!prefix_matches(\n" +" \"/v1/publishers/*/books\",\n" +" \"/v1/publishers/foo/booksByAuthor\"\n" +" ));\n" "}\n" "```" msgstr "" -#: src/modules.md:28 -msgid "" -"Packages provide functionality and include a `Cargo.toml` file that " -"describes how to build a bundle of 1+ crates." +#: src/welcome-day-3.md:1 +msgid "Welcome to Day 3" msgstr "" -#: src/modules.md:29 +#: src/welcome-day-3.md:3 +msgid "Today, we will cover some more advanced topics of Rust:" +msgstr "" + +#: src/welcome-day-3.md:5 msgid "" -"Crates are a tree of modules, where a binary crate creates an executable and " -"a library crate compiles to a library." +"Traits: deriving traits, default methods, and important standard library " +"traits." msgstr "" -#: src/modules.md:30 -msgid "Modules define organization, scope, and are the focus of this section." +#: src/welcome-day-3.md:8 +msgid "" +"Generics: generic data types, generic methods, monomorphization, and trait " +"objects." msgstr "" -#: src/modules/visibility.md:3 -msgid "Modules are a privacy boundary:" +#: src/welcome-day-3.md:11 +msgid "Error handling: panics, `Result`, and the try operator `?`." msgstr "" -#: src/modules/visibility.md:5 -msgid "Module items are private by default (hides implementation details)." +#: src/welcome-day-3.md:13 +msgid "Testing: unit tests, documentation tests, and integration tests." msgstr "" -#: src/modules/visibility.md:6 -msgid "Parent and sibling items are always visible." +#: src/welcome-day-3.md:15 +msgid "" +"Unsafe Rust: raw pointers, static variables, unsafe functions, and extern " +"functions." msgstr "" -#: src/modules/visibility.md:7 +#: src/generics.md:3 msgid "" -"In other words, if an item is visible in module `foo`, it's visible in all " -"the descendants of `foo`." +"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/modules/visibility.md:10 +#: src/generics/data-types.md:3 +msgid "You can use generics to abstract over the concrete field type:" +msgstr "" + +#: src/generics/data-types.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" +"#[derive(Debug)]\n" +"struct Point {\n" +" x: T,\n" +" y: T,\n" "}\n" "\n" "fn main() {\n" -" outer::public();\n" +" let integer = Point { x: 5, y: 10 };\n" +" let float = Point { x: 1.0, y: 4.0 };\n" +" println!(\"{integer:?} and {float:?}\");\n" "}\n" "```" msgstr "" -#: src/modules/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." +#: src/generics/data-types.md:21 +msgid "Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`." 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)." +#: src/generics/data-types.md:23 +msgid "Fix the code to allow points that have elements of different types." msgstr "" -#: src/modules/visibility.md:44 -msgid "Configuring `pub(crate)` visibility is a common pattern." +#: src/generics/methods.md:3 +msgid "You can declare a generic type on your `impl` block:" msgstr "" -#: src/modules/visibility.md:45 -msgid "Less commonly, you can give visibility to a specific path." +#: src/generics/methods.md:5 +msgid "" +"```rust,editable\n" +"#[derive(Debug)]\n" +"struct Point(T, T);\n" +"\n" +"impl Point {\n" +" fn x(&self) -> &T {\n" +" &self.0 // + 10\n" +" }\n" +"\n" +" // fn set_x(&mut self, x: T)\n" +"}\n" +"\n" +"fn main() {\n" +" let p = Point(5, 10);\n" +" println!(\"p.x = {}\", p.x());\n" +"}\n" +"```" msgstr "" -#: src/modules/visibility.md:46 +#: src/generics/methods.md:25 msgid "" -"In any case, visibility must be granted to an ancestor module (and all of " -"its descendants)." +"_Q:_ Why `T` is specified twice in `impl Point {}`? Isn't that " +"redundant?" msgstr "" -#: src/modules/paths.md:3 -msgid "Paths are resolved as follows:" +#: src/generics/methods.md:26 +msgid "" +"This is because it is a generic implementation section for generic type. " +"They are independently generic." msgstr "" -#: src/modules/paths.md:5 -msgid "As a relative path:" +#: src/generics/methods.md:27 +msgid "It means these methods are defined for any `T`." msgstr "" -#: src/modules/paths.md:6 -msgid "`foo` or `self::foo` refers to `foo` in the current module," +#: src/generics/methods.md:28 +msgid "It is possible to write `impl Point { .. }`. " msgstr "" -#: src/modules/paths.md:7 -msgid "`super::foo` refers to `foo` in the parent module." +#: src/generics/methods.md:29 +msgid "" +"`Point` is still generic and you can use `Point`, but methods in this " +"block will only be available for `Point`." msgstr "" -#: src/modules/paths.md:9 -msgid "As an absolute path:" +#: src/generics/monomorphization.md:3 +msgid "Generic code is turned into non-generic code based on the call sites:" msgstr "" -#: src/modules/paths.md:10 -msgid "`crate::foo` refers to `foo` in the root of the current crate," +#: src/generics/monomorphization.md:12 +msgid "behaves as if you wrote" msgstr "" -#: src/modules/paths.md:11 -msgid "`bar::foo` refers to `foo` in the `bar` crate." +#: src/generics/monomorphization.md:31 +msgid "" +"This is a zero-cost abstraction: you get exactly the same result as if you " +"had hand-coded the data structures without the abstraction." msgstr "" -#: src/modules/paths.md:13 +#: src/traits.md:3 msgid "" -"A module can bring symbols from another module into scope with `use`. You " -"will typically see something like this at the top of each module:" +"Rust lets you abstract over types with traits. They're similar to interfaces:" msgstr "" -#: src/modules/paths.md:16 +#: src/traits.md:5 msgid "" "```rust,editable\n" -"use std::collections::HashSet;\n" -"use std::mem::transmute;\n" +"struct Dog { name: String, age: i8 }\n" +"struct Cat { lives: i8 } // No name needed, cats won't respond anyway.\n" +"\n" +"trait Pet {\n" +" fn talk(&self) -> String;\n" +"}\n" +"\n" +"impl Pet for Dog {\n" +" fn talk(&self) -> String { format!(\"Woof, my name is {}!\", self." +"name) }\n" +"}\n" +"\n" +"impl Pet for Cat {\n" +" fn talk(&self) -> String { String::from(\"Miau!\") }\n" +"}\n" +"\n" +"fn greet(pet: &P) {\n" +" println!(\"Oh you're a cutie! What's your name? {}\", pet.talk());\n" +"}\n" +"\n" +"fn main() {\n" +" let captain_floof = Cat { lives: 9 };\n" +" let fido = Dog { name: String::from(\"Fido\"), age: 5 };\n" +"\n" +" greet(&captain_floof);\n" +" greet(&fido);\n" +"}\n" "```" msgstr "" -#: src/modules/filesystem.md:3 +#: src/traits/trait-objects.md:3 msgid "" -"Omitting the module content will tell Rust to look for it in another file:" +"Trait objects allow for values of different types, for instance in a " +"collection:" msgstr "" -#: src/modules/filesystem.md:5 +#: src/traits/trait-objects.md:5 msgid "" -"```rust,editable,compile_fail\n" -"mod garden;\n" +"```rust,editable\n" +"struct Dog { name: String, age: i8 }\n" +"struct Cat { lives: i8 } // No name needed, cats won't respond anyway.\n" +"\n" +"trait Pet {\n" +" fn talk(&self) -> String;\n" +"}\n" +"\n" +"impl Pet for Dog {\n" +" fn talk(&self) -> String { format!(\"Woof, my name is {}!\", self." +"name) }\n" +"}\n" +"\n" +"impl Pet for Cat {\n" +" fn talk(&self) -> String { String::from(\"Miau!\") }\n" +"}\n" +"\n" +"fn main() {\n" +" let pets: Vec> = vec![\n" +" Box::new(Cat { lives: 9 }),\n" +" Box::new(Dog { name: String::from(\"Fido\"), age: 5 }),\n" +" ];\n" +" for pet in pets {\n" +" println!(\"Hello, who are you? {}\", pet.talk());\n" +" }\n" +"}\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:" +#: src/traits/trait-objects.md:32 +msgid "Memory layout after allocating `pets`:" msgstr "" -#: src/modules/filesystem.md:14 -msgid "`src/lib.rs` (for a library crate)" +#: src/traits/trait-objects.md:34 +msgid "" +"```bob\n" +" Stack Heap\n" +".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - " +"- -.\n" +": : : :\n" +": pets : : +----+----+----+----" +"+ :\n" +": +-----------+-------+ : : +-----+-----+ .->| F | i | d | o " +"| :\n" +": | ptr | o---+---+-----+-->| o o | o o | | +----+----+----+----" +"+ :\n" +": | len | 2 | : : +-|-|-+-|-|-+ " +"`---------. :\n" +": | capacity | 2 | : : | | | | data " +"| :\n" +": +-----------+-------+ : : | | | | +-------+--|-------" +"+ :\n" +": : : | | | '-->| name | o, 4, 4 " +"| :\n" +": : : | | | | age | 5 " +"| :\n" +"`- - - - - - - - - - - - - -' : | | | +-------+----------" +"+ :\n" +" : | | " +"| :\n" +" : | | | " +"vtable :\n" +" : | | | +----------------------" +"+ :\n" +" : | | '---->| \"::talk\" " +"| :\n" +" : | | +----------------------" +"+ :\n" +" : | " +"| :\n" +" : | | " +"data :\n" +" : | | +-------+-------" +"+ :\n" +" : | '-->| lives | 9 " +"| :\n" +" : | +-------+-------" +"+ :\n" +" : " +"| :\n" +" : | " +"vtable :\n" +" : | +----------------------" +"+ :\n" +" : '---->| \"::talk\" " +"| :\n" +" : +----------------------" +"+ :\n" +" : :\n" +" '- - - - - - - - - - - - - - - - - - - - - " +"- -'\n" +"```" msgstr "" -#: src/modules/filesystem.md:15 -msgid "`src/main.rs` (for a binary crate)" +#: src/traits/trait-objects.md:68 +msgid "" +"Types that implement a given trait may be of different sizes. This makes it " +"impossible to have things like `Vec` in the example above." msgstr "" -#: src/modules/filesystem.md:17 +#: src/traits/trait-objects.md:70 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." +"`dyn Pet` is a way to tell the compiler about a dynamically sized type that " +"implements `Pet`." msgstr "" -#: src/modules/filesystem.md:20 +#: src/traits/trait-objects.md:72 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" -"```" +"In the example, `pets` is allocated on the stack and the vector data is on " +"the heap. The two vector elements are _fat pointers_:" msgstr "" -#: src/modules/filesystem.md:37 +#: src/traits/trait-objects.md:74 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." +"A fat pointer is a double-width pointer. It has two components: a pointer to " +"the actual object and a pointer to the [virtual method table](https://en." +"wikipedia.org/wiki/Virtual_method_table) (vtable) for the `Pet` " +"implementation of that particular object." msgstr "" -#: src/modules/filesystem.md:39 +#: src/traits/trait-objects.md:77 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." +"The data for the `Dog` named Fido is the `name` and `age` fields. The `Cat` " +"has a `lives` field." msgstr "" -#: src/modules/filesystem.md:42 -msgid "Deeper nesting can use folders, even if the main module is a file:" +#: src/traits/trait-objects.md:79 +msgid "Compare these outputs in the above example:" msgstr "" -#: src/modules/filesystem.md:44 +#: src/traits/trait-objects.md:80 msgid "" -"```ignore\n" -"src/\n" -"├── main.rs\n" -"├── top_module.rs\n" -"└── top_module/\n" -" └── sub_module.rs\n" +"```rust,ignore\n" +" println!(\"{} {}\", std::mem::size_of::(), std::mem::size_of::" +"());\n" +" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::" +"<&Cat>());\n" +" println!(\"{}\", std::mem::size_of::<&dyn Pet>());\n" +" println!(\"{}\", std::mem::size_of::>());\n" "```" msgstr "" -#: src/modules/filesystem.md:52 +#: src/traits/deriving-traits.md:3 msgid "" -"The place rust will look for modules can be changed with a compiler " -"directive:" +"Rust derive macros work by automatically generating code that implements the " +"specified traits for a data structure." msgstr "" -#: src/modules/filesystem.md:54 -msgid "" -"```rust,ignore\n" -"#[path = \"some/path.rs\"]\n" -"mod some_module;\n" -"```" +#: src/traits/deriving-traits.md:5 +msgid "You can let the compiler derive a number of traits as follows:" msgstr "" -#: src/modules/filesystem.md:59 +#: src/traits/deriving-traits.md:7 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." +"```rust,editable\n" +"#[derive(Debug, Clone, PartialEq, Eq, Default)]\n" +"struct Player {\n" +" name: String,\n" +" strength: u8,\n" +" hit_points: u8,\n" +"}\n" +"\n" +"fn main() {\n" +" let p1 = Player::default();\n" +" let p2 = p1.clone();\n" +" println!(\"Is {:?}\\nequal to {:?}?\\nThe answer is {}!\", &p1, &p2,\n" +" if p1 == p2 { \"yes\" } else { \"no\" });\n" +"}\n" +"```" msgstr "" -#: src/exercises/day-2/afternoon.md:1 -msgid "Day 2: Afternoon Exercises" +#: src/traits/default-methods.md:3 +msgid "Traits can implement behavior in terms of other trait methods:" msgstr "" -#: src/exercises/day-2/afternoon.md:3 -msgid "The exercises for this afternoon will focus on strings and iterators." +#: src/traits/default-methods.md:5 +msgid "" +"```rust,editable\n" +"trait Equals {\n" +" fn equals(&self, other: &Self) -> bool;\n" +" fn not_equals(&self, other: &Self) -> bool {\n" +" !self.equals(other)\n" +" }\n" +"}\n" +"\n" +"#[derive(Debug)]\n" +"struct Centimeter(i16);\n" +"\n" +"impl Equals for Centimeter {\n" +" fn equals(&self, other: &Centimeter) -> bool {\n" +" self.0 == other.0\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let a = Centimeter(10);\n" +" let b = Centimeter(20);\n" +" println!(\"{a:?} equals {b:?}: {}\", a.equals(&b));\n" +" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equals(&b));\n" +"}\n" +"```" msgstr "" -#: src/exercises/day-2/luhn.md:3 +#: src/traits/default-methods.md:32 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:" +"Traits may specify pre-implemented (default) methods and methods that users " +"are required to implement themselves. Methods with default implementations " +"can rely on required methods." msgstr "" -#: src/exercises/day-2/luhn.md:7 -msgid "Ignore all spaces. Reject number with less than two digits." +#: src/traits/default-methods.md:35 +msgid "Move method `not_equals` to a new trait `NotEquals`." msgstr "" -#: src/exercises/day-2/luhn.md:9 -msgid "" -"Moving from right to left, double every second digit: for the number `1234`, " -"we double `3` and `1`." +#: src/traits/default-methods.md:37 +msgid "Make `Equals` a super trait for `NotEquals`." 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/traits/default-methods.md:46 +msgid "Provide a blanket implementation of `NotEquals` for `Equals`." msgstr "" -#: src/exercises/day-2/luhn.md:15 -msgid "Sum all the undoubled and doubled digits." +#: src/traits/default-methods.md:58 +msgid "" +"With the blanket implementation, you no longer need `Equals` as a super " +"trait for `NotEqual`." msgstr "" -#: src/exercises/day-2/luhn.md:17 -msgid "The credit card number is valid if the sum ends with `0`." +#: src/traits/trait-bounds.md:3 +msgid "" +"When working with generics, you often want to require the types to implement " +"some trait, so that you can call this trait's methods." msgstr "" -#: src/exercises/day-2/luhn.md:19 -msgid "" -"Copy the following code to and implement the " -"function:" +#: src/traits/trait-bounds.md:6 +msgid "You can do this with `T: Trait` or `impl Trait`:" msgstr "" -#: src/exercises/day-2/luhn.md:23 +#: src/traits/trait-bounds.md:8 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub fn luhn(cc_number: &str) -> bool {\n" -" unimplemented!()\n" -"}\n" -"\n" -"#[test]\n" -"fn test_non_digit_cc_number() {\n" -" assert!(!luhn(\"foo\"));\n" -"}\n" -"\n" -"#[test]\n" -"fn test_empty_cc_number() {\n" -" assert!(!luhn(\"\"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" +"```rust,editable\n" +"fn duplicate(a: T) -> (T, T) {\n" +" (a.clone(), a.clone())\n" "}\n" "\n" -"#[test]\n" -"fn test_single_digit_cc_number() {\n" -" assert!(!luhn(\"0\"));\n" +"// Syntactic sugar for:\n" +"// fn add_42_millions>(x: T) -> i32 {\n" +"fn add_42_millions(x: impl Into) -> i32 {\n" +" x.into() + 42_000_000\n" "}\n" "\n" -"#[test]\n" -"fn test_two_digit_cc_number() {\n" -" assert!(luhn(\" 0 0 \"));\n" -"}\n" +"// struct NotClonable;\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" +"fn main() {\n" +" let foo = String::from(\"foo\");\n" +" let pair = duplicate(foo);\n" +" println!(\"{pair:?}\");\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" +" let many = add_42_millions(42_i8);\n" +" println!(\"{many}\");\n" +" let many_more = add_42_millions(10_000_000);\n" +" println!(\"{many_more}\");\n" "}\n" -"\n" -"#[allow(dead_code)]\n" -"fn main() {}\n" "```" msgstr "" -#: src/exercises/day-2/strings-iterators.md:3 -msgid "" -"In this exercise, you are implementing a routing component of a web server. " -"The server is configured with a number of _path prefixes_ which are matched " -"against _request paths_. The path prefixes can contain a wildcard character " -"which matches a full segment. See the unit tests below." +#: src/traits/trait-bounds.md:35 +msgid "Show a `where` clause, students will encounter it when reading code." msgstr "" -#: src/exercises/day-2/strings-iterators.md:8 +#: src/traits/trait-bounds.md:46 +msgid "It declutters the function signature if you have many parameters." +msgstr "" + +#: src/traits/trait-bounds.md:47 +msgid "It has additional features making it more powerful." +msgstr "" + +#: src/traits/trait-bounds.md:48 msgid "" -"Copy the following code to and make the tests " -"pass. Try avoiding allocating a `Vec` for your intermediate results:" +"If someone asks, the extra feature is that the type on the left of \":\" can " +"be arbitrary, like `Option`." msgstr "" -#: src/exercises/day-2/strings-iterators.md:12 +#: src/traits/impl-trait.md:1 +msgid "`impl Trait`" +msgstr "" + +#: src/traits/impl-trait.md:3 msgid "" -"```rust\n" -"// TODO: remove this when you're done with your implementation.\n" -"#![allow(unused_variables, dead_code)]\n" -"\n" -"pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" -" unimplemented!()\n" -"}\n" -"\n" -"#[test]\n" -"fn test_matches_without_wildcard() {\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/" -"abc-123\"));\n" -" assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers/abc/" -"books\"));\n" +"Similar to trait bounds, an `impl Trait` syntax can be used in function " +"arguments and return values:" +msgstr "" + +#: src/traits/impl-trait.md:6 +msgid "" +"```rust,editable\n" +"use std::fmt::Display;\n" "\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1\"));\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/publishersBooks\"));\n" -" assert!(!prefix_matches(\"/v1/publishers\", \"/v1/parent/" -"publishers\"));\n" +"fn get_x(name: impl Display) -> impl Display {\n" +" format!(\"Hello {name}\")\n" "}\n" "\n" -"#[test]\n" -"fn test_matches_with_wildcard() {\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/books\"\n" -" ));\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/bar/books\"\n" -" ));\n" -" assert!(prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/books/book1\"\n" -" ));\n" -"\n" -" assert!(!prefix_matches(\"/v1/publishers/*/books\", \"/v1/" -"publishers\"));\n" -" assert!(!prefix_matches(\n" -" \"/v1/publishers/*/books\",\n" -" \"/v1/publishers/foo/booksByAuthor\"\n" -" ));\n" +"fn main() {\n" +" let x = get_x(\"foo\");\n" +" println!(\"{x}\");\n" "}\n" "```" msgstr "" -#: src/welcome-day-3.md:1 -msgid "Welcome to Day 3" +#: src/traits/impl-trait.md:19 +msgid "`impl Trait` allows you to work with types which you cannot name." msgstr "" -#: src/welcome-day-3.md:3 -msgid "Today, we will cover some more advanced topics of Rust:" +#: src/traits/impl-trait.md:23 +msgid "" +"The meaning of `impl Trait` is a bit different in the different positions." msgstr "" -#: src/welcome-day-3.md:5 +#: src/traits/impl-trait.md:25 msgid "" -"Traits: deriving traits, default methods, and important standard library " -"traits." +"For a parameter, `impl Trait` is like an anonymous generic parameter with a " +"trait bound." msgstr "" -#: src/welcome-day-3.md:8 +#: src/traits/impl-trait.md:27 msgid "" -"Generics: generic data types, generic methods, monomorphization, and trait " -"objects." +"For a return type, it means that the return type is some concrete type that " +"implements the trait, without naming the type. This can be useful when you " +"don't want to expose the concrete type in a public API." msgstr "" -#: src/welcome-day-3.md:11 -msgid "Error handling: panics, `Result`, and the try operator `?`." +#: src/traits/impl-trait.md:31 +msgid "" +"Inference is hard in return position. A function returning `impl Foo` picks " +"the concrete type it returns, without writing it out in the source. A " +"function returning a generic type like `collect() -> B` can return any " +"type satisfying `B`, and the caller may need to choose one, such as with " +"`let x: Vec<_> = foo.collect()` or with the turbofish, `foo.collect::" +">()`." msgstr "" -#: src/welcome-day-3.md:13 -msgid "Testing: unit tests, documentation tests, and integration tests." +#: src/traits/impl-trait.md:37 +msgid "" +"This example is great, because it uses `impl Display` twice. It helps to " +"explain that nothing here enforces that it is _the same_ `impl Display` " +"type. If we used a single `T: Display`, it would enforce the constraint " +"that input `T` and return `T` type are the same type. It would not work for " +"this particular function, as the type we expect as input is likely not what " +"`format!` returns. If we wanted to do the same via `: Display` syntax, we'd " +"need two independent generic parameters." msgstr "" -#: src/welcome-day-3.md:15 +#: src/traits/important-traits.md:3 msgid "" -"Unsafe Rust: raw pointers, static variables, unsafe functions, and extern " -"functions." +"We will now look at some of the most common traits of the Rust standard " +"library:" msgstr "" -#: src/generics.md:3 +#: src/traits/important-traits.md:5 msgid "" -"Rust support generics, which lets you abstract algorithms or data structures " -"(such as sorting or a binary tree) over the types used or stored." +"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " +"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " +"used in `for` loops," msgstr "" -#: src/generics/data-types.md:3 -msgid "You can use generics to abstract over the concrete field type:" +#: src/traits/important-traits.md:6 +msgid "" +"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) and [`Into`]" +"(https://doc.rust-lang.org/std/convert/trait.Into.html) used to convert " +"values," msgstr "" -#: src/generics/data-types.md:5 +#: src/traits/important-traits.md:7 msgid "" -"```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point {\n" -" x: T,\n" -" y: T,\n" -"}\n" -"\n" -"fn main() {\n" -" let integer = Point { x: 5, y: 10 };\n" -" let float = Point { x: 1.0, y: 4.0 };\n" -" println!(\"{integer:?} and {float:?}\");\n" -"}\n" -"```" +"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and [`Write`]" +"(https://doc.rust-lang.org/std/io/trait.Write.html) used for IO," msgstr "" -#: src/generics/data-types.md:21 -msgid "Try declaring a new variable `let p = Point { x: 5, y: 10.0 };`." +#: src/traits/important-traits.md:8 +msgid "" +"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html), [`Mul`](https://" +"doc.rust-lang.org/std/ops/trait.Mul.html), ... used for operator " +"overloading, and" msgstr "" -#: src/generics/data-types.md:23 -msgid "Fix the code to allow points that have elements of different types." +#: src/traits/important-traits.md:9 +msgid "" +"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) used for " +"defining destructors." msgstr "" -#: src/generics/methods.md:3 -msgid "You can declare a generic type on your `impl` block:" +#: src/traits/important-traits.md:10 +msgid "" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) used " +"to construct a default instance of a type." msgstr "" -#: src/generics/methods.md:5 +#: src/traits/iterator.md:1 +msgid "Iterators" +msgstr "" + +#: src/traits/iterator.md:3 +msgid "" +"You can implement the [`Iterator`](https://doc.rust-lang.org/std/iter/trait." +"Iterator.html) trait on your own types:" +msgstr "" + +#: src/traits/iterator.md:5 msgid "" "```rust,editable\n" -"#[derive(Debug)]\n" -"struct Point(T, T);\n" +"struct Fibonacci {\n" +" curr: u32,\n" +" next: u32,\n" +"}\n" "\n" -"impl Point {\n" -" fn x(&self) -> &T {\n" -" &self.0 // + 10\n" -" }\n" +"impl Iterator for Fibonacci {\n" +" type Item = u32;\n" "\n" -" // fn set_x(&mut self, x: T)\n" +" fn next(&mut self) -> Option {\n" +" let new_next = self.curr + self.next;\n" +" self.curr = self.next;\n" +" self.next = new_next;\n" +" Some(self.curr)\n" +" }\n" "}\n" "\n" "fn main() {\n" -" let p = Point(5, 10);\n" -" println!(\"p.x = {}\", p.x());\n" +" let fib = Fibonacci { curr: 0, next: 1 };\n" +" for (i, n) in fib.enumerate().take(5) {\n" +" println!(\"fib({i}): {n}\");\n" +" }\n" "}\n" "```" msgstr "" -#: src/generics/methods.md:25 +#: src/traits/iterator.md:32 msgid "" -"_Q:_ Why `T` is specified twice in `impl Point {}`? Isn't that " -"redundant?" +"The `Iterator` trait implements many common functional programming " +"operations over collections (e.g. `map`, `filter`, `reduce`, etc). This is " +"the trait where you can find all the documentation about them. In Rust these " +"functions should produce the code as efficient as equivalent imperative " +"implementations." msgstr "" -#: src/generics/methods.md:26 +#: src/traits/iterator.md:37 msgid "" -"This is because it is a generic implementation section for generic type. " -"They are independently generic." -msgstr "" - -#: src/generics/methods.md:27 -msgid "It means these methods are defined for any `T`." -msgstr "" - -#: src/generics/methods.md:28 -msgid "It is possible to write `impl Point { .. }`. " +"`IntoIterator` is the trait that makes for loops work. It is implemented by " +"collection types such as `Vec` and references to them such as `&Vec` " +"and `&[T]`. Ranges also implement it. This is why you can iterate over a " +"vector with `for i in some_vec { .. }` but `some_vec.next()` doesn't exist." msgstr "" -#: src/generics/methods.md:29 +#: src/traits/from-iterator.md:3 msgid "" -"`Point` is still generic and you can use `Point`, but methods in this " -"block will only be available for `Point`." -msgstr "" - -#: src/generics/monomorphization.md:3 -msgid "Generic code is turned into non-generic code based on the call sites:" +"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " +"lets you build a collection from an [`Iterator`](https://doc.rust-lang.org/" +"std/iter/trait.Iterator.html)." msgstr "" -#: src/generics/monomorphization.md:5 +#: src/traits/from-iterator.md:5 msgid "" "```rust,editable\n" "fn main() {\n" -" let integer = Some(5);\n" -" let float = Some(5.0);\n" +" let primes = vec![2, 3, 5, 7];\n" +" let prime_squares = primes\n" +" .into_iter()\n" +" .map(|prime| prime * prime)\n" +" .collect::>();\n" +" println!(\"prime_squares: {prime_squares:?}\");\n" "}\n" "```" msgstr "" -#: src/generics/monomorphization.md:12 -msgid "behaves as if you wrote" +#: src/traits/from-iterator.md:18 +msgid "" +"`Iterator` implements `fn collect(self) -> B where B: FromIterator, Self: Sized`" +msgstr "" + +#: src/traits/from-iterator.md:24 +msgid "" +"There are also implementations which let you do cool things like convert an " +"`Iterator>` into a `Result, E>`." +msgstr "" + +#: src/traits/from-into.md:1 +msgid "`From` and `Into`" +msgstr "" + +#: src/traits/from-into.md:3 +msgid "" +"Types implement [`From`](https://doc.rust-lang.org/std/convert/trait.From." +"html) and [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) to " +"facilitate type conversions:" msgstr "" -#: src/generics/monomorphization.md:14 +#: src/traits/from-into.md:5 msgid "" "```rust,editable\n" -"enum Option_i32 {\n" -" Some(i32),\n" -" None,\n" -"}\n" -"\n" -"enum Option_f64 {\n" -" Some(f64),\n" -" None,\n" +"fn main() {\n" +" let s = String::from(\"hello\");\n" +" let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);\n" +" let one = i16::from(true);\n" +" let bigger = i32::from(123i16);\n" +" println!(\"{s}, {addr}, {one}, {bigger}\");\n" "}\n" -"\n" +"```" +msgstr "" + +#: src/traits/from-into.md:15 +msgid "" +"[`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) is " +"automatically implemented when [`From`](https://doc.rust-lang.org/std/" +"convert/trait.From.html) is implemented:" +msgstr "" + +#: src/traits/from-into.md:17 +msgid "" +"```rust,editable\n" "fn main() {\n" -" let integer = Option_i32::Some(5);\n" -" let float = Option_f64::Some(5.0);\n" +" let s: String = \"hello\".into();\n" +" let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();\n" +" let one: i16 = true.into();\n" +" let bigger: i32 = 123i16.into();\n" +" println!(\"{s}, {addr}, {one}, {bigger}\");\n" "}\n" "```" msgstr "" -#: src/generics/monomorphization.md:31 +#: src/traits/from-into.md:29 msgid "" -"This is a zero-cost abstraction: you get exactly the same result as if you " -"had hand-coded the data structures without the abstraction." +"That's why it is common to only implement `From`, as your type will get " +"`Into` implementation too." msgstr "" -#: src/traits.md:3 +#: src/traits/from-into.md:30 msgid "" -"Rust lets you abstract over types with traits. They're similar to interfaces:" +"When declaring a function argument input type like \"anything that can be " +"converted into a `String`\", the rule is opposite, you should use `Into`. " +"Your function will accept types that implement `From` and those that _only_ " +"implement `Into`." msgstr "" -#: src/traits.md:5 +#: src/traits/read-write.md:1 +msgid "`Read` and `Write`" +msgstr "" + +#: src/traits/read-write.md:3 +msgid "" +"Using [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and " +"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html), you can " +"abstract over `u8` sources:" +msgstr "" + +#: src/traits/read-write.md:5 msgid "" "```rust,editable\n" -"trait Pet {\n" -" fn name(&self) -> String;\n" -"}\n" +"use std::io::{BufRead, BufReader, Read, Result};\n" "\n" -"struct Dog {\n" -" name: String,\n" +"fn count_lines(reader: R) -> usize {\n" +" let buf_reader = BufReader::new(reader);\n" +" buf_reader.lines().count()\n" "}\n" "\n" -"struct Cat;\n" -"\n" -"impl Pet for Dog {\n" -" fn name(&self) -> String {\n" -" self.name.clone()\n" -" }\n" -"}\n" +"fn main() -> Result<()> {\n" +" let slice: &[u8] = b\"foo\\nbar\\nbaz\\n\";\n" +" println!(\"lines in slice: {}\", count_lines(slice));\n" "\n" -"impl Pet for Cat {\n" -" fn name(&self) -> String {\n" -" String::from(\"The cat\") // No name, cats won't respond to it " -"anyway.\n" -" }\n" +" let file = std::fs::File::open(std::env::current_exe()?)?;\n" +" println!(\"lines in file: {}\", count_lines(file));\n" +" Ok(())\n" "}\n" +"```" +msgstr "" + +#: src/traits/read-write.md:23 +msgid "" +"Similarly, [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) lets " +"you abstract over `u8` sinks:" +msgstr "" + +#: src/traits/read-write.md:25 +msgid "" +"```rust,editable\n" +"use std::io::{Result, Write};\n" "\n" -"fn greet(pet: &P) {\n" -" println!(\"Who's a cutie? {} is!\", pet.name());\n" +"fn log(writer: &mut W, msg: &str) -> Result<()> {\n" +" writer.write_all(msg.as_bytes())?;\n" +" writer.write_all(\"\\n\".as_bytes())\n" "}\n" "\n" -"fn main() {\n" -" let fido = Dog { name: \"Fido\".into() };\n" -" greet(&fido);\n" -"\n" -" let captain_floof = Cat;\n" -" greet(&captain_floof);\n" +"fn main() -> Result<()> {\n" +" let mut buffer = Vec::new();\n" +" log(&mut buffer, \"Hello\")?;\n" +" log(&mut buffer, \"World\")?;\n" +" println!(\"Logged: {:?}\", buffer);\n" +" Ok(())\n" "}\n" "```" msgstr "" -#: src/traits/trait-objects.md:3 +#: src/traits/drop.md:1 +msgid "The `Drop` Trait" +msgstr "" + +#: src/traits/drop.md:3 msgid "" -"Trait objects allow for values of different types, for instance in a " -"collection:" +"Values which implement [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop." +"html) can specify code to run when they go out of scope:" msgstr "" -#: src/traits/trait-objects.md:5 +#: src/traits/drop.md:5 msgid "" "```rust,editable\n" -"trait Pet {\n" -" fn name(&self) -> String;\n" -"}\n" -"\n" -"struct Dog {\n" -" name: String,\n" -"}\n" -"\n" -"struct Cat;\n" -"\n" -"impl Pet for Dog {\n" -" fn name(&self) -> String {\n" -" self.name.clone()\n" -" }\n" +"struct Droppable {\n" +" name: &'static str,\n" "}\n" "\n" -"impl Pet for Cat {\n" -" fn name(&self) -> String {\n" -" String::from(\"The cat\") // No name, cats won't respond to it " -"anyway.\n" +"impl Drop for Droppable {\n" +" fn drop(&mut self) {\n" +" println!(\"Dropping {}\", self.name);\n" " }\n" "}\n" "\n" "fn main() {\n" -" let pets: Vec> = vec![\n" -" Box::new(Cat),\n" -" Box::new(Dog { name: String::from(\"Fido\") }),\n" -" ];\n" -" for pet in pets {\n" -" println!(\"Hello {}!\", pet.name());\n" +" let a = Droppable { name: \"a\" };\n" +" {\n" +" let b = Droppable { name: \"b\" };\n" +" {\n" +" let c = Droppable { name: \"c\" };\n" +" let d = Droppable { name: \"d\" };\n" +" println!(\"Exiting block B\");\n" +" }\n" +" println!(\"Exiting block A\");\n" " }\n" +" drop(a);\n" +" println!(\"Exiting main\");\n" "}\n" "```" msgstr "" -#: src/traits/trait-objects.md:40 -msgid "Memory layout after allocating `pets`:" +#: src/traits/drop.md:34 +msgid "Note that `std::mem::drop` is not the same as `std::ops::Drop::drop`." +msgstr "" + +#: src/traits/drop.md:35 +msgid "Values are automatically dropped when they go out of scope." msgstr "" -#: src/traits/trait-objects.md:42 +#: src/traits/drop.md:36 msgid "" -"```bob\n" -" Stack Heap\n" -".- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - " -"- -.\n" -": : : :\n" -": " -"pets : : :\n" -": +-----------+-------+ : : +-----+-----" -"+ :\n" -": | ptr | o---+---+-----+-->| o o | o o " -"| :\n" -": | len | 2 | : : +-|-|-+-|-|-" -"+ :\n" -": | capacity | 2 | : : | | | | +---------------" -"+ :\n" -": +-----------+-------+ : : | | | '-->| name: \"Fido\" " -"| :\n" -": : : | | | +---------------" -"+ :\n" -"`- - - - - - - - - - - - - -' : | | " -"| :\n" -" : | | | +----------------------" -"+ : \n" -" : | | '---->| \"::name\" " -"| :\n" -" : | | +----------------------" -"+ : \n" -" : | " -"| : \n" -" : | | +-" -"+ : \n" -" : | '-->|" -"\\| : \n" -" : | +-" -"+ : \n" -" : " -"| : \n" -" : | +----------------------" -"+ : \n" -" : '---->| \"::name\" " -"| : \n" -" : +----------------------" -"+ :\n" -" : :\n" -" '- - - - - - - - - - - - - - - - - - - - - " -"- -'\n" -"\n" -"```" +"When a value is dropped, if it implements `std::ops::Drop` then its `Drop::" +"drop` implementation will be called." msgstr "" -#: src/traits/trait-objects.md:72 +#: src/traits/drop.md:38 msgid "" -"Types that implement a given trait may be of different sizes. This makes it " -"impossible to have things like `Vec` in the example above." +"All its fields will then be dropped too, whether or not it implements `Drop`." msgstr "" -#: src/traits/trait-objects.md:73 +#: src/traits/drop.md:39 msgid "" -"`dyn Pet` is a way to tell the compiler about a dynamically sized type that " -"implements `Pet`." +"`std::mem::drop` is just an empty function that takes any value. The " +"significance is that it takes ownership of the value, so at the end of its " +"scope it gets dropped. This makes it a convenient way to explicitly drop " +"values earlier than they would otherwise go out of scope." msgstr "" -#: src/traits/trait-objects.md:74 +#: src/traits/drop.md:42 msgid "" -"In the example, `pets` holds _fat pointers_ to objects that implement `Pet`. " -"The fat pointer consists of two components, a pointer to the actual object " -"and a pointer to the virtual method table for the `Pet` implementation of " -"that particular object." +"This can be useful for objects that do some work on `drop`: releasing locks, " +"closing files, etc." msgstr "" -#: src/traits/trait-objects.md:75 -msgid "Compare these outputs in the above example:" +#: src/traits/drop.md:45 src/traits/operators.md:26 +msgid "Discussion points:" msgstr "" -#: src/traits/trait-objects.md:76 -msgid "" -"```rust,ignore\n" -" println!(\"{} {}\", std::mem::size_of::(), std::mem::size_of::" -"());\n" -" println!(\"{} {}\", std::mem::size_of::<&Dog>(), std::mem::size_of::" -"<&Cat>());\n" -" println!(\"{}\", std::mem::size_of::<&dyn Pet>());\n" -" println!(\"{}\", std::mem::size_of::>());\n" -"```" +#: src/traits/drop.md:47 +msgid "Why doesn't `Drop::drop` take `self`?" msgstr "" -#: src/traits/deriving-traits.md:3 +#: src/traits/drop.md:48 msgid "" -"Rust derive macros work by automatically generating code that implements the " -"specified traits for a data structure." +"Short-answer: If it did, `std::mem::drop` would be called at the end of the " +"block, resulting in another call to `Drop::drop`, and a stack overflow!" msgstr "" -#: src/traits/deriving-traits.md:5 -msgid "You can let the compiler derive a number of traits as follows:" +#: src/traits/drop.md:51 +msgid "Try replacing `drop(a)` with `a.drop()`." msgstr "" -#: src/traits/deriving-traits.md:7 -msgid "" -"```rust,editable\n" -"#[derive(Debug, Clone, PartialEq, Eq, Default)]\n" -"struct Player {\n" -" name: String,\n" -" strength: u8,\n" -" hit_points: u8,\n" -"}\n" -"\n" -"fn main() {\n" -" let p1 = Player::default();\n" -" let p2 = p1.clone();\n" -" println!(\"Is {:?}\\nequal to {:?}?\\nThe answer is {}!\", &p1, &p2,\n" -" if p1 == p2 { \"yes\" } else { \"no\" });\n" -"}\n" -"```" +#: src/traits/default.md:1 +msgid "The `Default` Trait" msgstr "" -#: src/traits/default-methods.md:3 -msgid "Traits can implement behavior in terms of other trait methods:" +#: src/traits/default.md:3 +msgid "" +"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " +"produces a default value for a type." msgstr "" -#: src/traits/default-methods.md:5 +#: src/traits/default.md:5 msgid "" "```rust,editable\n" -"trait Equals {\n" -" fn equals(&self, other: &Self) -> bool;\n" -" fn not_equals(&self, other: &Self) -> bool {\n" -" !self.equals(other)\n" -" }\n" +"#[derive(Debug, Default)]\n" +"struct Derived {\n" +" x: u32,\n" +" y: String,\n" +" z: Implemented,\n" "}\n" "\n" "#[derive(Debug)]\n" -"struct Centimeter(i16);\n" +"struct Implemented(String);\n" "\n" -"impl Equals for Centimeter {\n" -" fn equals(&self, other: &Centimeter) -> bool {\n" -" self.0 == other.0\n" +"impl Default for Implemented {\n" +" fn default() -> Self {\n" +" Self(\"John Smith\".into())\n" " }\n" "}\n" "\n" "fn main() {\n" -" let a = Centimeter(10);\n" -" let b = Centimeter(20);\n" -" println!(\"{a:?} equals {b:?}: {}\", a.equals(&b));\n" -" println!(\"{a:?} not_equals {b:?}: {}\", a.not_equals(&b));\n" +" let default_struct = Derived::default();\n" +" println!(\"{default_struct:#?}\");\n" +"\n" +" let almost_default_struct = Derived {\n" +" y: \"Y is set!\".into(),\n" +" ..Derived::default()\n" +" };\n" +" println!(\"{almost_default_struct:#?}\");\n" +"\n" +" let nothing: Option = None;\n" +" println!(\"{:#?}\", nothing.unwrap_or_default());\n" "}\n" +"\n" "```" msgstr "" -#: src/traits/default-methods.md:32 +#: src/traits/default.md:40 msgid "" -"Traits may specify pre-implemented (default) methods and methods that users " -"are required to implement themselves. Methods with default implementations " -"can rely on required methods." +"It can be implemented directly or it can be derived via `#[derive(Default)]`." msgstr "" -#: src/traits/default-methods.md:35 -msgid "Move method `not_equals` to a new trait `NotEquals`." +#: src/traits/default.md:41 +msgid "" +"A derived implementation will produce a value where all fields are set to " +"their default values." msgstr "" -#: src/traits/default-methods.md:37 -msgid "Make `Equals` a super trait for `NotEquals`." +#: src/traits/default.md:42 +msgid "This means all types in the struct must implement `Default` too." msgstr "" -#: src/traits/default-methods.md:38 +#: src/traits/default.md:43 msgid "" -"```rust,editable,compile_fail\n" -"trait NotEquals: Equals {\n" -" fn not_equals(&self, other: &Self) -> bool {\n" -" !self.equals(other)\n" -" }\n" -"}\n" -"```" +"Standard Rust types often implement `Default` with reasonable values (e.g. " +"`0`, `\"\"`, etc)." msgstr "" -#: src/traits/default-methods.md:46 -msgid "Provide a blanket implementation of `NotEquals` for `Equals`." +#: src/traits/default.md:44 +msgid "The partial struct copy works nicely with default." msgstr "" -#: src/traits/default-methods.md:47 +#: src/traits/default.md:45 msgid "" -"```rust,editable,compile_fail\n" -"trait NotEquals {\n" -" fn not_equals(&self, other: &Self) -> bool;\n" -"}\n" -"\n" -"impl NotEquals for T where T: Equals {\n" -" fn not_equals(&self, other: &Self) -> bool {\n" -" !self.equals(other)\n" -" }\n" -"}\n" -"```" +"Rust standard library is aware that types can implement `Default` and " +"provides convenience methods that use it." msgstr "" -#: src/traits/default-methods.md:58 +#: src/traits/default.md:46 msgid "" -"With the blanket implementation, you no longer need `Equals` as a super " -"trait for `NotEqual`." +"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/trait-bounds.md:3 -msgid "" -"When working with generics, you often want to require the types to implement " -"some trait, so that you can call this trait's methods." +#: src/traits/operators.md:1 +msgid "`Add`, `Mul`, ..." msgstr "" -#: src/traits/trait-bounds.md:6 -msgid "You can do this with `T: Trait` or `impl Trait`:" +#: src/traits/operators.md:3 +msgid "" +"Operator overloading is implemented via traits in [`std::ops`](https://doc." +"rust-lang.org/std/ops/index.html):" msgstr "" -#: src/traits/trait-bounds.md:8 +#: src/traits/operators.md:5 msgid "" "```rust,editable\n" -"fn duplicate(a: T) -> (T, T) {\n" -" (a.clone(), a.clone())\n" -"}\n" +"#[derive(Debug, Copy, Clone)]\n" +"struct Point { x: i32, y: i32 }\n" "\n" -"// Syntactic sugar for:\n" -"// fn add_42_millions>(x: T) -> i32 {\n" -"fn add_42_millions(x: impl Into) -> i32 {\n" -" x.into() + 42_000_000\n" -"}\n" +"impl std::ops::Add for Point {\n" +" type Output = Self;\n" "\n" -"// struct NotClonable;\n" +" fn add(self, other: Self) -> Self {\n" +" Self {x: self.x + other.x, y: self.y + other.y}\n" +" }\n" +"}\n" "\n" "fn main() {\n" -" let foo = String::from(\"foo\");\n" -" let pair = duplicate(foo);\n" -" println!(\"{pair:?}\");\n" -"\n" -" let many = add_42_millions(42_i8);\n" -" println!(\"{many}\");\n" -" let many_more = add_42_millions(10_000_000);\n" -" println!(\"{many_more}\");\n" +" let p1 = Point { x: 10, y: 20 };\n" +" let p2 = Point { x: 100, y: 200 };\n" +" println!(\"{:?} + {:?} = {:?}\", p1, p2, p1 + p2);\n" "}\n" "```" msgstr "" -#: src/traits/trait-bounds.md:35 -msgid "Show a `where` clause, students will encounter it when reading code." +#: src/traits/operators.md:28 +msgid "" +"You could implement `Add` for `&Point`. In which situations is that useful? " msgstr "" -#: src/traits/trait-bounds.md:37 +#: src/traits/operators.md:29 msgid "" -"```rust,ignore\n" -"fn duplicate(a: T) -> (T, T)\n" -"where\n" -" T: Clone,\n" -"{\n" -" (a.clone(), a.clone())\n" -"}\n" -"```" +"Answer: `Add:add` consumes `self`. If type `T` for which you are overloading " +"the operator is not `Copy`, you should consider overloading the operator for " +"`&T` as well. This avoids unnecessary cloning on the call site." msgstr "" -#: src/traits/trait-bounds.md:46 -msgid "It declutters the function signature if you have many parameters." +#: src/traits/operators.md:33 +msgid "" +"Why is `Output` an associated type? Could it be made a type parameter of the " +"method?" msgstr "" -#: src/traits/trait-bounds.md:47 -msgid "It has additional features making it more powerful." +#: src/traits/operators.md:34 +msgid "" +"Short answer: Function type parameters are controlled by the caller, but " +"associated types (like `Output`) are controlled by the implementor of a " +"trait." msgstr "" -#: src/traits/trait-bounds.md:48 +#: src/traits/operators.md:37 msgid "" -"If someone asks, the extra feature is that the type on the left of \":\" can " -"be arbitrary, like `Option`." +"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/impl-trait.md:1 -msgid "`impl Trait`" +#: src/traits/closures.md:1 +msgid "Closures" msgstr "" -#: src/traits/impl-trait.md:3 +#: src/traits/closures.md:3 msgid "" -"Similar to trait bounds, an `impl Trait` syntax can be used in function " -"arguments and return values:" +"Closures or lambda expressions have types which cannot be named. However, " +"they implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn." +"html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), and " +"[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:" msgstr "" -#: src/traits/impl-trait.md:6 +#: src/traits/closures.md:8 msgid "" "```rust,editable\n" -"use std::fmt::Display;\n" -"\n" -"fn get_x(name: impl Display) -> impl Display {\n" -" format!(\"Hello {name}\")\n" +"fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {\n" +" println!(\"Calling function on {input}\");\n" +" func(input)\n" "}\n" "\n" "fn main() {\n" -" let x = get_x(\"foo\");\n" -" println!(\"{x}\");\n" +" let add_3 = |x| x + 3;\n" +" println!(\"add_3: {}\", apply_with_log(add_3, 10));\n" +" println!(\"add_3: {}\", apply_with_log(add_3, 20));\n" +"\n" +" let mut v = Vec::new();\n" +" let mut accumulate = |x: i32| {\n" +" v.push(x);\n" +" v.iter().sum::()\n" +" };\n" +" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 4));\n" +" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 5));\n" +"\n" +" let multiply_sum = |x| x * v.into_iter().sum::();\n" +" println!(\"multiply_sum: {}\", apply_with_log(multiply_sum, 3));\n" "}\n" "```" msgstr "" -#: src/traits/impl-trait.md:19 -msgid "`impl Trait` allows you to work with types which you cannot name." -msgstr "" - -#: src/traits/impl-trait.md:23 +#: src/traits/closures.md:34 msgid "" -"The meaning of `impl Trait` is a bit different in the different positions." +"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/impl-trait.md:25 +#: src/traits/closures.md:37 msgid "" -"For a parameter, `impl Trait` is like an anonymous generic parameter with a " -"trait bound." +"An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it " +"multiple times, but not concurrently." msgstr "" -#: src/traits/impl-trait.md:27 +#: src/traits/closures.md:40 msgid "" -"For a return type, it means that the return type is some concrete type that " -"implements the trait, without naming the type. This can be useful when you " -"don't want to expose the concrete type in a public API." +"If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It " +"might consume captured values." msgstr "" -#: src/traits/impl-trait.md:31 +#: src/traits/closures.md:43 msgid "" -"Inference is hard in return position. A function returning `impl Foo` picks " -"the concrete type it returns, without writing it out in the source. A " -"function returning a generic type like `collect() -> B` can return any " -"type satisfying `B`, and the caller may need to choose one, such as with " -"`let x: Vec<_> = foo.collect()` or with the turbofish, `foo.collect::" -">()`." +"`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/impl-trait.md:37 +#: src/traits/closures.md:47 msgid "" -"This example is great, because it uses `impl Display` twice. It helps to " -"explain that nothing here enforces that it is _the same_ `impl Display` " -"type. If we used a single `T: Display`, it would enforce the constraint " -"that input `T` and return `T` type are the same type. It would not work for " -"this particular function, as the type we expect as input is likely not what " -"`format!` returns. If we wanted to do the same via `: Display` syntax, we'd " -"need two independent generic parameters." +"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/important-traits.md:3 +#: src/traits/closures.md:50 msgid "" -"We will now look at some of the most common traits of the Rust standard " -"library:" +"By default, closures will capture by reference if they can. The `move` " +"keyword makes them capture by value." msgstr "" -#: src/traits/important-traits.md:5 +#: src/traits/closures.md:52 msgid "" -"[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) and " -"[`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) " -"used in `for` loops," +"```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/traits/important-traits.md:6 -msgid "" -"[`From`](https://doc.rust-lang.org/std/convert/trait.From.html) and [`Into`]" -"(https://doc.rust-lang.org/std/convert/trait.Into.html) used to convert " -"values," +#: src/exercises/day-3/morning.md:1 +msgid "Day 3: Morning Exercises" msgstr "" -#: src/traits/important-traits.md:7 -msgid "" -"[`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and [`Write`]" -"(https://doc.rust-lang.org/std/io/trait.Write.html) used for IO," +#: src/exercises/day-3/morning.md:3 +msgid "We will design a classical GUI library using traits and trait objects." msgstr "" -#: src/traits/important-traits.md:8 +#: src/exercises/day-3/morning.md:5 msgid "" -"[`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html), [`Mul`](https://" -"doc.rust-lang.org/std/ops/trait.Mul.html), ... used for operator " -"overloading, and" +"We will also look at enum dispatch with an exercise involving points and " +"polygons." msgstr "" -#: src/traits/important-traits.md:9 -msgid "" -"[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) used for " -"defining destructors." -msgstr "" - -#: src/traits/important-traits.md:10 -msgid "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) used " -"to construct a default instance of a type." -msgstr "" - -#: src/traits/iterator.md:1 -msgid "Iterators" -msgstr "" - -#: src/traits/iterator.md:3 -msgid "" -"You can implement the [`Iterator`](https://doc.rust-lang.org/std/iter/trait." -"Iterator.html) trait on your own types:" -msgstr "" - -#: src/traits/iterator.md:5 -msgid "" -"```rust,editable\n" -"struct Fibonacci {\n" -" curr: u32,\n" -" next: u32,\n" -"}\n" -"\n" -"impl Iterator for Fibonacci {\n" -" type Item = u32;\n" -"\n" -" fn next(&mut self) -> Option {\n" -" let new_next = self.curr + self.next;\n" -" self.curr = self.next;\n" -" self.next = new_next;\n" -" Some(self.curr)\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let fib = Fibonacci { curr: 0, next: 1 };\n" -" for (i, n) in fib.enumerate().take(5) {\n" -" println!(\"fib({i}): {n}\");\n" -" }\n" -"}\n" -"```" -msgstr "" - -#: src/traits/iterator.md:32 -msgid "" -"The `Iterator` trait implements many common functional programming " -"operations over collections (e.g. `map`, `filter`, `reduce`, etc). This is " -"the trait where you can find all the documentation about them. In Rust these " -"functions should produce the code as efficient as equivalent imperative " -"implementations." -msgstr "" - -#: src/traits/iterator.md:37 -msgid "" -"`IntoIterator` is the trait that makes for loops work. It is implemented by " -"collection types such as `Vec` and references to them such as `&Vec` " -"and `&[T]`. Ranges also implement it. This is why you can iterate over a " -"vector with `for i in some_vec { .. }` but `some_vec.next()` doesn't exist." -msgstr "" - -#: src/traits/from-iterator.md:3 -msgid "" -"[`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) " -"lets you build a collection from an [`Iterator`](https://doc.rust-lang.org/" -"std/iter/trait.Iterator.html)." -msgstr "" - -#: src/traits/from-iterator.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let primes = vec![2, 3, 5, 7];\n" -" let prime_squares = primes\n" -" .into_iter()\n" -" .map(|prime| prime * prime)\n" -" .collect::>();\n" -"}\n" -"```" -msgstr "" - -#: src/traits/from-iterator.md:17 -msgid "" -"`Iterator` implements `fn collect(self) -> B where B: FromIterator, Self: Sized`" -msgstr "" - -#: src/traits/from-iterator.md:23 -msgid "" -"There are also implementations which let you do cool things like convert an " -"`Iterator>` into a `Result, E>`." -msgstr "" - -#: src/traits/from-into.md:1 -msgid "`From` and `Into`" -msgstr "" - -#: src/traits/from-into.md:3 -msgid "" -"Types implement [`From`](https://doc.rust-lang.org/std/convert/trait.From." -"html) and [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) to " -"facilitate type conversions:" -msgstr "" - -#: src/traits/from-into.md:5 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let s = String::from(\"hello\");\n" -" let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);\n" -" let one = i16::from(true);\n" -" let bigger = i32::from(123i16);\n" -" println!(\"{s}, {addr}, {one}, {bigger}\");\n" -"}\n" -"```" -msgstr "" - -#: src/traits/from-into.md:15 -msgid "" -"[`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) is " -"automatically implemented when [`From`](https://doc.rust-lang.org/std/" -"convert/trait.From.html) is implemented:" -msgstr "" - -#: src/traits/from-into.md:17 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let s: String = \"hello\".into();\n" -" let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();\n" -" let one: i16 = true.into();\n" -" let bigger: i32 = 123i16.into();\n" -" println!(\"{s}, {addr}, {one}, {bigger}\");\n" -"}\n" -"```" -msgstr "" - -#: src/traits/from-into.md:29 -msgid "" -"That's why it is common to only implement `From`, as your type will get " -"`Into` implementation too." -msgstr "" - -#: src/traits/from-into.md:30 -msgid "" -"When declaring a function argument input type like \"anything that can be " -"converted into a `String`\", the rule is opposite, you should use `Into`. " -"Your function will accept types that implement `From` and those that _only_ " -"implement `Into`." -msgstr "" - -#: src/traits/read-write.md:1 -msgid "`Read` and `Write`" -msgstr "" - -#: src/traits/read-write.md:3 -msgid "" -"Using [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) and " -"[`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html), you can " -"abstract over `u8` sources:" -msgstr "" - -#: src/traits/read-write.md:5 -msgid "" -"```rust,editable\n" -"use std::io::{BufRead, BufReader, Read, Result};\n" -"\n" -"fn count_lines(reader: R) -> usize {\n" -" let buf_reader = BufReader::new(reader);\n" -" buf_reader.lines().count()\n" -"}\n" -"\n" -"fn main() -> Result<()> {\n" -" let slice: &[u8] = b\"foo\\nbar\\nbaz\\n\";\n" -" println!(\"lines in slice: {}\", count_lines(slice));\n" -"\n" -" let file = std::fs::File::open(std::env::current_exe()?)?;\n" -" println!(\"lines in file: {}\", count_lines(file));\n" -" Ok(())\n" -"}\n" -"```" -msgstr "" - -#: src/traits/read-write.md:23 -msgid "" -"Similarly, [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) lets " -"you abstract over `u8` sinks:" -msgstr "" - -#: src/traits/read-write.md:25 -msgid "" -"```rust,editable\n" -"use std::io::{Result, Write};\n" -"\n" -"fn log(writer: &mut W, msg: &str) -> Result<()> {\n" -" writer.write_all(msg.as_bytes())?;\n" -" writer.write_all(\"\\n\".as_bytes())\n" -"}\n" -"\n" -"fn main() -> Result<()> {\n" -" let mut buffer = Vec::new();\n" -" log(&mut buffer, \"Hello\")?;\n" -" log(&mut buffer, \"World\")?;\n" -" println!(\"Logged: {:?}\", buffer);\n" -" Ok(())\n" -"}\n" -"```" -msgstr "" - -#: src/traits/drop.md:1 -msgid "The `Drop` Trait" -msgstr "" - -#: src/traits/drop.md:3 -msgid "" -"Values which implement [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop." -"html) can specify code to run when they go out of scope:" -msgstr "" - -#: src/traits/drop.md:5 -msgid "" -"```rust,editable\n" -"struct Droppable {\n" -" name: &'static str,\n" -"}\n" -"\n" -"impl Drop for Droppable {\n" -" fn drop(&mut self) {\n" -" println!(\"Dropping {}\", self.name);\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let a = Droppable { name: \"a\" };\n" -" {\n" -" let b = Droppable { name: \"b\" };\n" -" {\n" -" let c = Droppable { name: \"c\" };\n" -" let d = Droppable { name: \"d\" };\n" -" println!(\"Exiting block B\");\n" -" }\n" -" println!(\"Exiting block A\");\n" -" }\n" -" drop(a);\n" -" println!(\"Exiting main\");\n" -"}\n" -"```" -msgstr "" - -#: src/traits/drop.md:34 src/traits/operators.md:26 -msgid "Discussion points:" -msgstr "" - -#: src/traits/drop.md:36 -msgid "Why doesn't `Drop::drop` take `self`?" -msgstr "" - -#: src/traits/drop.md:37 -msgid "" -"Short-answer: If it did, `std::mem::drop` would be called at the end of the " -"block, resulting in another call to `Drop::drop`, and a stack overflow!" -msgstr "" - -#: src/traits/drop.md:40 -msgid "Try replacing `drop(a)` with `a.drop()`." -msgstr "" - -#: src/traits/default.md:1 -msgid "The `Default` Trait" -msgstr "" - -#: src/traits/default.md:3 -msgid "" -"[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait " -"produces a default value for a type." -msgstr "" - -#: src/traits/default.md:5 -msgid "" -"```rust,editable\n" -"#[derive(Debug, Default)]\n" -"struct Derived {\n" -" x: u32,\n" -" y: String,\n" -" z: Implemented,\n" -"}\n" -"\n" -"#[derive(Debug)]\n" -"struct Implemented(String);\n" -"\n" -"impl Default for Implemented {\n" -" fn default() -> Self {\n" -" Self(\"John Smith\".into())\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let default_struct = Derived::default();\n" -" println!(\"{default_struct:#?}\");\n" -"\n" -" let almost_default_struct = Derived {\n" -" y: \"Y is set!\".into(),\n" -" ..Derived::default()\n" -" };\n" -" println!(\"{almost_default_struct:#?}\");\n" -"\n" -" let nothing: Option = None;\n" -" println!(\"{:#?}\", nothing.unwrap_or_default());\n" -"}\n" -"\n" -"```" -msgstr "" - -#: src/traits/default.md:40 -msgid "" -"It can be implemented directly or it can be derived via `#[derive(Default)]`." -msgstr "" - -#: src/traits/default.md:41 -msgid "" -"A derived implementation will produce a value where all fields are set to " -"their default values." -msgstr "" - -#: src/traits/default.md:42 -msgid "This means all types in the struct must implement `Default` too." -msgstr "" - -#: src/traits/default.md:43 -msgid "" -"Standard Rust types often implement `Default` with reasonable values (e.g. " -"`0`, `\"\"`, etc)." -msgstr "" - -#: src/traits/default.md:44 -msgid "The partial struct copy works nicely with default." -msgstr "" - -#: src/traits/default.md:45 -msgid "" -"Rust standard library is aware that types can implement `Default` and " -"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 "" - -#: src/traits/operators.md:3 -msgid "" -"Operator overloading is implemented via traits in [`std::ops`](https://doc." -"rust-lang.org/std/ops/index.html):" -msgstr "" - -#: src/traits/operators.md:5 -msgid "" -"```rust,editable\n" -"#[derive(Debug, Copy, Clone)]\n" -"struct Point { x: i32, y: i32 }\n" -"\n" -"impl std::ops::Add for Point {\n" -" type Output = Self;\n" -"\n" -" fn add(self, other: Self) -> Self {\n" -" Self {x: self.x + other.x, y: self.y + other.y}\n" -" }\n" -"}\n" -"\n" -"fn main() {\n" -" let p1 = Point { x: 10, y: 20 };\n" -" let p2 = Point { x: 100, y: 200 };\n" -" println!(\"{:?} + {:?} = {:?}\", p1, p2, p1 + p2);\n" -"}\n" -"```" -msgstr "" - -#: src/traits/operators.md:28 -msgid "" -"You could implement `Add` for `&Point`. In which situations is that useful? " -msgstr "" - -#: src/traits/operators.md:29 -msgid "" -"Answer: `Add:add` consumes `self`. If type `T` for which you are overloading " -"the operator is not `Copy`, you should consider overloading the operator for " -"`&T` as well. This avoids unnecessary cloning on the call site." -msgstr "" - -#: src/traits/operators.md:33 -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: 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 -msgid "Closures" -msgstr "" - -#: src/traits/closures.md:3 -msgid "" -"Closures or lambda expressions have types which cannot be named. However, " -"they implement special [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn." -"html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), and " -"[`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) traits:" -msgstr "" - -#: src/traits/closures.md:8 -msgid "" -"```rust,editable\n" -"fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {\n" -" println!(\"Calling function on {input}\");\n" -" func(input)\n" -"}\n" -"\n" -"fn main() {\n" -" let add_3 = |x| x + 3;\n" -" println!(\"add_3: {}\", apply_with_log(add_3, 10));\n" -" println!(\"add_3: {}\", apply_with_log(add_3, 20));\n" -"\n" -" let mut v = Vec::new();\n" -" let mut accumulate = |x: i32| {\n" -" v.push(x);\n" -" v.iter().sum::()\n" -" };\n" -" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 4));\n" -" println!(\"accumulate: {}\", apply_with_log(&mut accumulate, 5));\n" -"\n" -" let multiply_sum = |x| x * v.into_iter().sum::();\n" -" println!(\"multiply_sum: {}\", apply_with_log(multiply_sum, 3));\n" -"}\n" -"```" -msgstr "" - -#: src/traits/closures.md:34 -msgid "" -"An `Fn` (e.g. `add_3`) neither consumes nor mutates captured values, or " -"perhaps captures nothing at all. It can be called multiple times " -"concurrently." -msgstr "" - -#: src/traits/closures.md:37 -msgid "" -"An `FnMut` (e.g. `accumulate`) might mutate captured values. You can call it " -"multiple times, but not concurrently." -msgstr "" - -#: src/traits/closures.md:40 -msgid "" -"If you have an `FnOnce` (e.g. `multiply_sum`), you may only call it once. It " -"might consume captured values." -msgstr "" - -#: 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: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 -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." -msgstr "" +#: src/exercises/day-3/simple-gui.md:1 +#: src/exercises/day-3/solutions-morning.md:3 +#, fuzzy +msgid "Drawing A Simple GUI" +msgstr "یک کتابخانه GUI ساده" #: src/exercises/day-3/simple-gui.md:3 msgid "" "Let us design a classical GUI library using our new knowledge of traits and " -"trait objects." +"trait objects. We'll only implement the drawing of it (as text) for " +"simplicity." msgstr "" #: src/exercises/day-3/simple-gui.md:6 @@ -9625,25 +8922,26 @@ msgstr "" #: src/exercises/day-3/simple-gui.md:9 msgid "" -"`Button`: has a `label` and a callback function which is invoked when the " -"button is pressed." +"`Button`: has a `label`. In reality, it would also take a callback function " +"to allow the program to do something when the button is clicked but we won't " +"include that since we're only drawing the GUI." msgstr "" -#: src/exercises/day-3/simple-gui.md:11 +#: src/exercises/day-3/simple-gui.md:12 msgid "`Label`: has a `label`." msgstr "" -#: src/exercises/day-3/simple-gui.md:13 +#: src/exercises/day-3/simple-gui.md:14 msgid "The widgets will implement a `Widget` trait, see below." msgstr "" -#: src/exercises/day-3/simple-gui.md:15 +#: src/exercises/day-3/simple-gui.md:16 msgid "" "Copy the code below to , fill in the missing " "`draw_into` methods so that you implement the `Widget` trait:" msgstr "" -#: src/exercises/day-3/simple-gui.md:18 +#: src/exercises/day-3/simple-gui.md:19 msgid "" "```rust,should_panic\n" "// TODO: remove this when you're done with your implementation.\n" @@ -9678,14 +8976,12 @@ msgid "" "\n" "pub struct Button {\n" " label: Label,\n" -" callback: Box,\n" "}\n" "\n" "impl Button {\n" -" fn new(label: &str, callback: Box) -> Button {\n" +" fn new(label: &str) -> Button {\n" " Button {\n" " label: Label::new(label),\n" -" callback,\n" " }\n" " }\n" "}\n" @@ -9751,68 +9047,185 @@ msgid "" " 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" +" \"Click me!\"\n" " )));\n" " window.draw();\n" "}\n" "```" msgstr "" -#: src/exercises/day-3/simple-gui.md:130 +#: src/exercises/day-3/simple-gui.md:128 msgid "The output of the above program can be something simple like this:" msgstr "" -#: src/exercises/day-3/simple-gui.md:132 +#: src/exercises/day-3/simple-gui.md:140 +msgid "" +"If you want to draw aligned text, you can use the [fill/alignment](https://" +"doc.rust-lang.org/std/fmt/index.html#fillalignment) formatting operators. In " +"particular, notice how you can pad with different characters (here a `'/'`) " +"and how you can control alignment:" +msgstr "" + +#: src/exercises/day-3/simple-gui.md:145 +msgid "" +"```rust,editable\n" +"fn main() {\n" +" let width = 10;\n" +" println!(\"left aligned: |{:/width$}|\", \"foo\");\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-3/simple-gui.md:154 +msgid "" +"Using such alignment tricks, you can for example produce output like this:" +msgstr "" + +#: src/exercises/day-3/points-polygons.md:1 +msgid "Polygon Struct" +msgstr "" + +#: 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 "" -"```text\n" -"========\n" -"Rust GUI Demo 1.23\n" -"========\n" +"```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" -"This is a small text GUI demo.\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" -"| Click me! |\n" +"#[allow(dead_code)]\n" +"fn main() {}\n" "```" msgstr "" -#: src/exercises/day-3/simple-gui.md:142 +#: src/exercises/day-3/points-polygons.md:117 msgid "" -"If you want to draw aligned text, you can use the [fill/alignment](https://" -"doc.rust-lang.org/std/fmt/index.html#fillalignment) formatting operators. In " -"particular, notice how you can pad with different characters (here a `'/'`) " -"and how you can control alignment:" +"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/simple-gui.md:147 -msgid "" -"```rust,editable\n" -"fn main() {\n" -" let width = 10;\n" -" println!(\"left aligned: |{:/width$}|\", \"foo\");\n" -"}\n" -"```" +#: src/exercises/day-3/points-polygons.md:120 +msgid "Other interesting parts of the exercise:" msgstr "" -#: src/exercises/day-3/simple-gui.md:156 +#: src/exercises/day-3/points-polygons.md:122 msgid "" -"Using such alignment tricks, you can for example produce output like this:" +"Derive a `Copy` trait for some structs, as in tests the methods sometimes " +"don't borrow their arguments." msgstr "" -#: src/exercises/day-3/simple-gui.md:158 +#: src/exercises/day-3/points-polygons.md:123 msgid "" -"```text\n" -"+--------------------------------+\n" -"| Rust GUI Demo 1.23 |\n" -"+================================+\n" -"| This is a small text GUI demo. |\n" -"| +-----------+ |\n" -"| | Click me! | |\n" -"| +-----------+ |\n" -"+--------------------------------+\n" -"```" +"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 @@ -9871,14 +9284,14 @@ msgid "" "\n" "fn main() {\n" " let result = panic::catch_unwind(|| {\n" -" println!(\"hello!\");\n" +" \"No problem here!\"\n" " });\n" -" assert!(result.is_ok());\n" -" \n" +" println!(\"{result:?}\");\n" +"\n" " let result = panic::catch_unwind(|| {\n" " panic!(\"oh no!\");\n" " });\n" -" assert!(result.is_err());\n" +" println!(\"{result:?}\");\n" "}\n" "```" msgstr "" @@ -9950,27 +9363,10 @@ msgid "" "turn the common" msgstr "" -#: src/error-handling/try-operator.md:6 -msgid "" -"```rust,ignore\n" -"match some_expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(err),\n" -"}\n" -"```" -msgstr "" - #: src/error-handling/try-operator.md:13 msgid "into the much simpler" msgstr "" -#: src/error-handling/try-operator.md:15 -msgid "" -"```rust,ignore\n" -"some_expression?\n" -"```" -msgstr "" - #: src/error-handling/try-operator.md:19 msgid "We can use this to simplify our error handling code:" msgstr "" @@ -10020,10 +9416,10 @@ 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 a `Result` or an `Option`. Reciprocally, a function returning an " -"`Option` can only apply the `?` operator on a function returning an " -"`Option`." +"Err>`. 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 @@ -10039,27 +9435,10 @@ msgid "" "indicated:" msgstr "" -#: src/error-handling/converting-error-types.md:5 -msgid "" -"```rust,ignore\n" -"expression?\n" -"```" -msgstr "" - #: src/error-handling/converting-error-types.md:9 msgid "works the same as" msgstr "" -#: src/error-handling/converting-error-types.md:11 -msgid "" -"```rust,ignore\n" -"match expression {\n" -" Ok(value) => value,\n" -" Err(err) => return Err(From::from(err)),\n" -"}\n" -"```" -msgstr "" - #: src/error-handling/converting-error-types.md:18 msgid "" "The `From::from` call here means we attempt to convert the error type to the " @@ -10381,7 +9760,7 @@ msgid "" "/// Shortens a string to the given length.\n" "///\n" "/// ```\n" -"/// use playground::shorten_string;\n" +"/// # use playground::shorten_string;\n" "/// assert_eq!(shorten_string(\"Hello World\", 5), \"Hello\");\n" "/// assert_eq!(shorten_string(\"Hello World\", 20), \"Hello World\");\n" "/// ```\n" @@ -10401,6 +9780,12 @@ msgstr "" #: src/testing/doc-tests.md:20 msgid "" +"Adding `# ` in the code will hide it from the docs, but will still compile/" +"run it." +msgstr "" + +#: src/testing/doc-tests.md:21 +msgid "" "Test the above code on the [Rust Playground](https://play.rust-lang.org/?" "version=stable&mode=debug&edition=2021&gist=3ce2ad13ea1302f6572cb15cd96becf0)." msgstr "" @@ -10413,18 +9798,6 @@ msgstr "" msgid "Create a `.rs` file under `tests/`:" msgstr "" -#: src/testing/integration-tests.md:7 -msgid "" -"```rust,ignore\n" -"use my_library::init;\n" -"\n" -"#[test]\n" -"fn test_init() {\n" -" assert!(init().is_ok());\n" -"}\n" -"```" -msgstr "" - #: src/testing/integration-tests.md:16 msgid "These tests only have access to the public API of your crate." msgstr "" @@ -10631,6 +10004,14 @@ msgstr "" #: src/unsafe/mutable-static-variables.md:32 msgid "" +"The program here is safe because it is single-threaded. However, the Rust " +"compiler is conservative and will assume the worst. Try removing the " +"`unsafe` and see how the compiler explains that it is undefined behavior to " +"mutate a static from multiple threads." +msgstr "" + +#: src/unsafe/mutable-static-variables.md:36 +msgid "" "Using a mutable static is generally a bad idea, but there are some cases " "where it might make sense in low-level `no_std` code, such as implementing a " "heap allocator or working with some C APIs." @@ -10702,7 +10083,7 @@ msgid "" "}\n" "\n" "fn count_chars(s: &str) -> usize {\n" -" s.chars().map(|_| 1).sum()\n" +" s.chars().count()\n" "}\n" "```" msgstr "" @@ -10874,7 +10255,7 @@ msgstr "" msgid "" "Rust has great support for calling functions through a _foreign function " "interface_ (FFI). We will use this to build a safe wrapper for the `libc` " -"functions you would use from C to read the filenames of a directory." +"functions you would use from C to read the names of files in a directory." msgstr "" #: src/exercises/day-3/safe-ffi-wrapper.md:7 @@ -10973,7 +10354,7 @@ msgstr "" #: src/exercises/day-3/safe-ffi-wrapper.md:27 msgid "" "`&CStr` to `&[u8]`: a slice of bytes is the universal interface for \"some " -"unknow data\"," +"unknown data\"," msgstr "" #: src/exercises/day-3/safe-ffi-wrapper.md:28 @@ -11132,15 +10513,6 @@ msgid "" "have access to one or create a new one with:" 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" -"```" -msgstr "" - #: src/android/setup.md:12 msgid "" "Please see the [Android Developer Codelab](https://source.android.com/docs/" @@ -11289,13 +10661,6 @@ msgid "" "```" msgstr "" -#: src/android/build-rules/binary.md:35 -msgid "" -"```text\n" -"Hello from Rust!\n" -"```" -msgstr "" - #: src/android/build-rules/library.md:1 msgid "Rust Libraries" msgstr "" @@ -11386,14 +10751,6 @@ msgid "" "```" msgstr "" -#: src/android/build-rules/library.md:67 -msgid "" -"```text\n" -"Hello Bob, it is very\n" -"nice to meet you!\n" -"```" -msgstr "" - #: src/android/aidl.md:3 msgid "" "The [Android Interface Definition Language (AIDL)](https://developer.android." @@ -11421,19 +10778,6 @@ msgid "" "_birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_:" msgstr "" -#: src/android/aidl/interface.md:7 -msgid "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** Birthday service interface. */\n" -"interface IBirthdayService {\n" -" /** Generate a Happy Birthday message. */\n" -" String wishHappyBirthday(String name, int years);\n" -"}\n" -"```" -msgstr "" - #: src/android/aidl/interface.md:17 msgid "_birthday_service/aidl/Android.bp_:" msgstr "" @@ -11590,46 +10934,10 @@ msgstr "" msgid "In another terminal, check that the service runs:" msgstr "" -#: src/android/aidl/deploy.md:13 -msgid "" -"```shell\n" -"adb shell service check birthdayservice\n" -"```" -msgstr "" - -#: src/android/aidl/deploy.md:17 -msgid "" -"```text\n" -"Service birthdayservice: found\n" -"```" -msgstr "" - #: src/android/aidl/deploy.md:21 msgid "You can also call the service with `service call`:" msgstr "" -#: src/android/aidl/deploy.md:23 -msgid "" -"```shell\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" -" 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'\n" -" 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'\n" -" 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'\n" -" 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'\n" -" 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'\n" -" 0x00000070: 00210073 00000000 's.!..... ')\n" -"```" -msgstr "" - #: src/android/aidl/client.md:1 msgid "AIDL Client" msgstr "" @@ -11712,32 +11020,12 @@ msgid "" "```" msgstr "" -#: src/android/aidl/client.md:62 -msgid "" -"```text\n" -"Happy Birthday Charlie, congratulations with the 60 years!\n" -"```" -msgstr "" - #: src/android/aidl/changing.md:3 msgid "" "Let us extend the API with more functionality: we want to let clients " "specify a list of lines for the birthday card:" msgstr "" -#: src/android/aidl/changing.md:6 -msgid "" -"```java\n" -"package com.example.birthdayservice;\n" -"\n" -"/** Birthday service interface. */\n" -"interface IBirthdayService {\n" -" /** Generate a Happy Birthday message. */\n" -" String wishHappyBirthday(String name, int years, in String[] text);\n" -"}\n" -"```" -msgstr "" - #: src/android/logging.md:3 msgid "" "You should use the `log` crate to automatically log to `logcat` (on-device) " @@ -11809,24 +11097,6 @@ msgstr "" msgid "The logs show up in `adb logcat`:" msgstr "" -#: src/android/logging.md:52 -msgid "" -"```shell\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" -"09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went " -"wrong!\n" -"```" -msgstr "" - #: src/android/interoperability.md:3 msgid "" "Rust has excellent support for interoperability with other languages. This " @@ -11910,18 +11180,6 @@ msgstr "" msgid "_interoperability/bindgen/libbirthday.h_:" msgstr "" -#: src/android/interoperability/with-c/bindgen.md:10 -msgid "" -"```c\n" -"typedef struct card {\n" -" const char* name;\n" -" int years;\n" -"} card;\n" -"\n" -"void print_card(const card* card);\n" -"```" -msgstr "" - #: src/android/interoperability/with-c/bindgen.md:19 msgid "_interoperability/bindgen/libbirthday.c_:" msgstr "" @@ -12064,13 +11322,6 @@ msgid "" "```" msgstr "" -#: src/android/interoperability/with-c/bindgen.md:122 -msgid "" -"```shell\n" -"atest libbirthday_bindgen_test\n" -"```" -msgstr "" - #: src/android/interoperability/with-c/rust.md:1 msgid "Calling Rust" msgstr "" @@ -12206,6 +11457,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 "" @@ -12308,15 +11595,6 @@ msgstr "" msgid "Finally, you can build, sync, and run the binary:" msgstr "" -#: src/android/interoperability/java.md:75 -msgid "" -"```shell\n" -"m helloworld_jni\n" -"adb sync # requires adb root && adb remount\n" -"adb shell /system/bin/helloworld_jni\n" -"```" -msgstr "" - #: src/exercises/android/morning.md:3 msgid "" "This is a group exercise: We will look at one of the projects you work with " @@ -12385,50 +11663,15 @@ msgid "" "To get started, install some tools we'll need later. On gLinux or Debian:" msgstr "" -#: src/bare-metal.md:22 -msgid "" -"```bash\n" -"sudo apt install gcc-aarch64-linux-gnu gdb-multiarch libudev-dev picocom pkg-" -"config qemu-system-arm\n" -"rustup update\n" -"rustup target add aarch64-unknown-none thumbv7em-none-eabihf\n" -"rustup component add llvm-tools-preview\n" -"cargo install cargo-binutils cargo-embed\n" -"```" -msgstr "" - #: src/bare-metal.md:30 msgid "" "And give users in the `plugdev` group access to the micro:bit programmer:" msgstr "" -#: src/bare-metal.md:32 -msgid "" -"```bash\n" -"echo 'SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0d28\", MODE=\"0664\", " -"GROUP=\"plugdev\"' |\\\n" -" sudo tee /etc/udev/rules.d/50-microbit.rules\n" -"sudo udevadm control --reload-rules\n" -"```" -msgstr "" - -#: src/bare-metal.md:38 +#: src/bare-metal.md:38 src/bare-metal/microcontrollers/debugging.md:27 msgid "On MacOS:" msgstr "" -#: src/bare-metal.md:40 -msgid "" -"```bash\n" -"xcode-select --install\n" -"brew install gdb picocom qemu\n" -"brew install --cask gcc-aarch64-embedded\n" -"rustup update\n" -"rustup target add aarch64-unknown-none thumbv7em-none-eabihf\n" -"rustup component add llvm-tools-preview\n" -"cargo install cargo-binutils cargo-embed\n" -"```" -msgstr "" - #: src/bare-metal/no_std.md:1 msgid "`no_std`" msgstr "" @@ -12541,21 +11784,6 @@ msgstr "" msgid "A minimal `no_std` program" msgstr "" -#: src/bare-metal/minimal.md:3 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"use core::panic::PanicInfo;\n" -"\n" -"#[panic_handler]\n" -"fn panic(_panic: &PanicInfo) -> ! {\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/minimal.md:17 msgid "This will compile to an empty binary." msgstr "" @@ -12658,25 +11886,6 @@ msgid "" "Cortex M microcontrollers." msgstr "" -#: src/bare-metal/microcontrollers.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"extern crate panic_halt as _;\n" -"\n" -"mod interrupts;\n" -"\n" -"use cortex_m_rt::entry;\n" -"\n" -"#[entry]\n" -"fn main() -> ! {\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers.md:21 msgid "" "Next we'll look at how to access peripherals, with increasing levels of " @@ -12777,13 +11986,6 @@ msgstr "" msgid "Run the example with:" msgstr "" -#: src/bare-metal/microcontrollers/mmio.md:68 -msgid "" -"```sh\n" -"cargo embed --bin mmio\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/pacs.md:1 msgid "Peripheral Access Crates" msgstr "" @@ -12867,13 +12069,6 @@ msgid "" "pac -- -d --no-show-raw-insn` to see the resulting binary." msgstr "" -#: src/bare-metal/microcontrollers/pacs.md:61 -msgid "" -"```sh\n" -"cargo embed --bin pac\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/hals.md:1 msgid "HAL crates" msgstr "" @@ -12930,13 +12125,6 @@ msgid "" "STM32, GD32, nRF, NXP, MSP430, AVR and PIC microcontrollers." msgstr "" -#: src/bare-metal/microcontrollers/hals.md:45 -msgid "" -"```sh\n" -"cargo embed --bin hal\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/board-support.md:1 msgid "Board support crates" msgstr "" @@ -12947,30 +12135,6 @@ msgid "" "board for convenience." msgstr "" -#: src/bare-metal/microcontrollers/board-support.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"extern crate panic_halt as _;\n" -"\n" -"use cortex_m_rt::entry;\n" -"use microbit::hal::prelude::*;\n" -"use microbit::Board;\n" -"\n" -"#[entry]\n" -"fn main() -> ! {\n" -" let mut board = Board::take().unwrap();\n" -"\n" -" board.display_pins.col1.set_low().unwrap();\n" -" board.display_pins.row1.set_high().unwrap();\n" -"\n" -" loop {}\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/board-support.md:28 msgid "" "In this case the board support crate is just providing more useful names, " @@ -12987,13 +12151,6 @@ msgstr "" msgid "`microbit-v2` includes a simple driver for the LED matrix." msgstr "" -#: src/bare-metal/microcontrollers/board-support.md:36 -msgid "" -"```sh\n" -"cargo embed --bin board_support\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/type-state.md:1 msgid "The type state pattern" msgstr "" @@ -13126,23 +12283,12 @@ msgid "" msgstr "" #: src/bare-metal/microcontrollers/probe-rs.md:6 -msgid "SWD" -msgstr "" - -#: src/bare-metal/microcontrollers/probe-rs.md:6 -msgid " and JTAG via CMSIS-DAP, ST-Link and J-Link probes" -msgstr "" - -#: src/bare-metal/microcontrollers/probe-rs.md:7 -msgid "GDB stub and Microsoft " -msgstr "" - -#: src/bare-metal/microcontrollers/probe-rs.md:7 -msgid "DAP" +msgid "" +"SWD (Serial Wire Debug) and JTAG via CMSIS-DAP, ST-Link and J-Link probes" msgstr "" #: src/bare-metal/microcontrollers/probe-rs.md:7 -msgid " server" +msgid "GDB stub and Microsoft DAP (Debug Adapter Protocol) server" msgstr "" #: src/bare-metal/microcontrollers/probe-rs.md:8 @@ -13150,17 +12296,10 @@ msgid "Cargo integration" msgstr "" #: src/bare-metal/microcontrollers/probe-rs.md:10 -msgid "`cargo-embed` is a cargo subcommand to build and flash binaries, log " -msgstr "" - -#: src/bare-metal/microcontrollers/probe-rs.md:11 -msgid "RTT" -msgstr "" - -#: src/bare-metal/microcontrollers/probe-rs.md:11 msgid "" -" output and connect GDB. It's configured by an `Embed.toml` file in your " -"project directory." +"`cargo-embed` is a cargo subcommand to build and flash binaries, log RTT " +"(Real Time Transfers) output and connect GDB. It's configured by an `Embed." +"toml` file in your project directory." msgstr "" #: src/bare-metal/microcontrollers/probe-rs.md:16 @@ -13207,57 +12346,23 @@ msgid "" msgstr "" #: src/bare-metal/microcontrollers/debugging.md:3 -msgid "Embed.toml:" -msgstr "" - -#: src/bare-metal/microcontrollers/debugging.md:5 -msgid "" -"```toml\n" -"[default.general]\n" -"chip = \"nrf52833_xxAA\"\n" -"\n" -"[debug.gdb]\n" -"enabled = true\n" -"```" +msgid "_Embed.toml_:" msgstr "" #: src/bare-metal/microcontrollers/debugging.md:13 msgid "In one terminal under `src/bare-metal/microcontrollers/examples/`:" msgstr "" -#: src/bare-metal/microcontrollers/debugging.md:15 -msgid "" -"```sh\n" -"cargo embed --bin board_support debug\n" -"```" -msgstr "" - #: src/bare-metal/microcontrollers/debugging.md:19 msgid "In another terminal in the same directory:" msgstr "" #: src/bare-metal/microcontrollers/debugging.md:21 -msgid "" -"```sh\n" -"gdb-multiarch target/thumbv7em-none-eabihf/debug/board_support --eval-" -"command=\"target remote :1337\"\n" -"```" -msgstr "" - -#: src/bare-metal/microcontrollers/debugging.md:27 -msgid "In GDB, try running:" +msgid "On gLinux or Debian:" msgstr "" -#: src/bare-metal/microcontrollers/debugging.md:29 -msgid "" -"```gdb\n" -"b src/bin/board_support.rs:29\n" -"b src/bin/board_support.rs:30\n" -"b src/bin/board_support.rs:32\n" -"c\n" -"c\n" -"c\n" -"```" +#: src/bare-metal/microcontrollers/debugging.md:34 +msgid "In GDB, try running:" msgstr "" #: src/bare-metal/microcontrollers/other-projects.md:1 @@ -13407,7 +12512,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/compass.md:26 src/exercises/bare-metal/rtc.md:19 -msgid "`src/main.rs`:" +msgid "_src/main.rs_:" msgstr "" #: src/exercises/bare-metal/compass.md:30 @@ -13448,85 +12553,26 @@ msgid "" msgstr "" #: src/exercises/bare-metal/compass.md:64 src/exercises/bare-metal/rtc.md:385 -msgid "`Cargo.toml` (you shouldn't need to change this):" -msgstr "" - -#: src/exercises/bare-metal/compass.md:68 -msgid "" -"```toml\n" -"[workspace]\n" -"\n" -"[package]\n" -"name = \"compass\"\n" -"version = \"0.1.0\"\n" -"edition = \"2021\"\n" -"publish = false\n" -"\n" -"[dependencies]\n" -"cortex-m-rt = \"0.7.3\"\n" -"embedded-hal = \"0.2.6\"\n" -"lsm303agr = \"0.2.2\"\n" -"microbit-v2 = \"0.13.0\"\n" -"panic-halt = \"0.2.0\"\n" -"```" +msgid "_Cargo.toml_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/compass.md:85 -msgid "`Embed.toml` (you shouldn't need to change this):" -msgstr "" - -#: src/exercises/bare-metal/compass.md:89 -msgid "" -"```toml\n" -"[default.general]\n" -"chip = \"nrf52833_xxAA\"\n" -"\n" -"[debug.gdb]\n" -"enabled = true\n" -"\n" -"[debug.reset]\n" -"halt_afterwards = true\n" -"```" +msgid "_Embed.toml_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/compass.md:100 src/exercises/bare-metal/rtc.md:985 -msgid "`.cargo/config.toml` (you shouldn't need to change this):" -msgstr "" - -#: src/exercises/bare-metal/compass.md:104 -msgid "" -"```toml\n" -"[build]\n" -"target = \"thumbv7em-none-eabihf\" # Cortex-M4F\n" -"\n" -"[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n" -"rustflags = [\"-C\", \"link-arg=-Tlink.x\"]\n" -"```" +msgid "_.cargo/config.toml_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/compass.md:112 msgid "See the serial output on Linux with:" msgstr "" -#: src/exercises/bare-metal/compass.md:114 -msgid "" -"```sh\n" -"picocom --baud 115200 --imap lfcrlf /dev/ttyACM0\n" -"```" -msgstr "" - #: src/exercises/bare-metal/compass.md:118 msgid "" "Or on Mac OS something like (the device name may be slightly different):" msgstr "" -#: src/exercises/bare-metal/compass.md:120 -msgid "" -"```sh\n" -"picocom --baud 115200 --imap lfcrlf /dev/tty.usbmodem14502\n" -"```" -msgstr "" - #: src/exercises/bare-metal/compass.md:124 msgid "Use Ctrl+A Ctrl+Q to quit picocom." msgstr "" @@ -13677,7 +12723,7 @@ msgstr "" 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 " +"while the host has cacheable aliases to the same memory. Even if the host " "doesn't explicitly access the memory, speculative accesses can lead to cache " "fills, and then changes from one or the other will get lost when the cache " "is cleaned or the VM enables the cache. (Cache is keyed by physical address, " @@ -13712,15 +12758,8 @@ 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:" +"Rust code. For example, to make an HVC (hypervisor call) to tell the " +"firmware to power off the system:" msgstr "" #: src/bare-metal/aps/inline-assembly.md:6 @@ -14205,43 +13244,6 @@ msgid "" "We can use a struct to represent the memory layout of the UART's registers." msgstr "" -#: src/bare-metal/aps/better-uart/registers.md:5 -msgid "" -"```rust,editable,compile_fail\n" -"#[repr(C, align(4))]\n" -"struct Registers {\n" -" dr: u16,\n" -" _reserved0: [u8; 2],\n" -" rsr: ReceiveStatus,\n" -" _reserved1: [u8; 19],\n" -" fr: Flags,\n" -" _reserved2: [u8; 6],\n" -" ilpr: u8,\n" -" _reserved3: [u8; 3],\n" -" ibrd: u16,\n" -" _reserved4: [u8; 2],\n" -" fbrd: u8,\n" -" _reserved5: [u8; 3],\n" -" lcr_h: u8,\n" -" _reserved6: [u8; 3],\n" -" cr: u16,\n" -" _reserved7: [u8; 3],\n" -" ifls: u8,\n" -" _reserved8: [u8; 3],\n" -" imsc: u16,\n" -" _reserved9: [u8; 2],\n" -" ris: u16,\n" -" _reserved10: [u8; 2],\n" -" mis: u16,\n" -" _reserved11: [u8; 2],\n" -" icr: u16,\n" -" _reserved12: [u8; 2],\n" -" dmacr: u8,\n" -" _reserved13: [u8; 3],\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/aps/better-uart/registers.md:41 msgid "" "[`#[repr(C)]`](https://doc.rust-lang.org/reference/type-layout.html#the-c-" @@ -14521,63 +13523,6 @@ msgid "" "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 "" @@ -14664,7 +13609,7 @@ msgstr "" 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 " +"while the host has cacheable aliases to the same memory. Even if the host " "doesn't explicitly access the memory, speculative accesses can lead to cache " "fills, and then changes from one or the other will get lost. Again this is " "alright in this particular case (running directly on the hardware with no " @@ -14688,43 +13633,6 @@ msgid "" "types." msgstr "" -#: src/bare-metal/useful-crates/zerocopy.md:6 -msgid "" -"```rust,editable,compile_fail\n" -"use zerocopy::AsBytes;\n" -"\n" -"#[repr(u32)]\n" -"#[derive(AsBytes, Debug, Default)]\n" -"enum RequestType {\n" -" #[default]\n" -" In = 0,\n" -" Out = 1,\n" -" Flush = 4,\n" -"}\n" -"\n" -"#[repr(C)]\n" -"#[derive(AsBytes, Debug, Default)]\n" -"struct VirtioBlockRequest {\n" -" request_type: RequestType,\n" -" reserved: u32,\n" -" sector: u64,\n" -"}\n" -"\n" -"fn main() {\n" -" let request = VirtioBlockRequest {\n" -" request_type: RequestType::Flush,\n" -" sector: 42,\n" -" ..Default::default()\n" -" };\n" -"\n" -" assert_eq!(\n" -" request.as_bytes(),\n" -" &[4, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0]\n" -" );\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/useful-crates/zerocopy.md:40 msgid "" "This is not suitable for MMIO (as it doesn't use volatile reads and writes), " @@ -14826,25 +13734,6 @@ msgid "" "space for PCI BARs:" msgstr "" -#: src/bare-metal/useful-crates/buddy_system_allocator.md:8 -msgid "" -"```rust,editable,compile_fail\n" -"use buddy_system_allocator::FrameAllocator;\n" -"use core::alloc::Layout;\n" -"\n" -"fn main() {\n" -" let mut allocator = FrameAllocator::<32>::new();\n" -" allocator.add_frame(0x200_0000, 0x400_0000);\n" -"\n" -" let layout = Layout::from_size_align(0x100, 0x100).unwrap();\n" -" let bar = allocator\n" -" .alloc_aligned(layout)\n" -" .expect(\"Failed to allocate 0x100 byte MMIO region\");\n" -" println!(\"Allocated 0x100 byte MMIO region at {:#x}\", bar);\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/useful-crates/buddy_system_allocator.md:26 msgid "PCI BARs always have alignment equal to their size." msgstr "" @@ -14869,22 +13758,6 @@ msgid "" "and panics if you try to use more than are allocated." msgstr "" -#: src/bare-metal/useful-crates/tinyvec.md:8 -msgid "" -"```rust,editable,compile_fail\n" -"use tinyvec::{array_vec, ArrayVec};\n" -"\n" -"fn main() {\n" -" let mut numbers: ArrayVec<[u32; 5]> = array_vec!(42, 66);\n" -" println!(\"{numbers:?}\");\n" -" numbers.push(7);\n" -" println!(\"{numbers:?}\");\n" -" numbers.remove(1);\n" -" println!(\"{numbers:?}\");\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/useful-crates/tinyvec.md:23 msgid "" "`tinyvec` requires that the element type implement `Default` for " @@ -14913,21 +13786,6 @@ msgid "" "equivalents of many of these primitives." msgstr "" -#: src/bare-metal/useful-crates/spin.md:9 -msgid "" -"```rust,editable,compile_fail\n" -"use spin::mutex::SpinMutex;\n" -"\n" -"static counter: SpinMutex = SpinMutex::new(0);\n" -"\n" -"fn main() {\n" -" println!(\"count: {}\", counter.lock());\n" -" *counter.lock() += 2;\n" -" println!(\"count: {}\", counter.lock());\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/useful-crates/spin.md:23 msgid "Be careful to avoid deadlock if you take locks in interrupt handlers." msgstr "" @@ -14958,48 +13816,6 @@ msgid "" "to convert the ELF to a raw binary ready to be run." msgstr "" -#: src/bare-metal/android.md:7 -msgid "" -"```soong\n" -"rust_ffi_static {\n" -" name: \"libvmbase_example\",\n" -" defaults: [\"vmbase_ffi_defaults\"],\n" -" crate_name: \"vmbase_example\",\n" -" srcs: [\"src/main.rs\"],\n" -" rustlibs: [\n" -" \"libvmbase\",\n" -" ],\n" -"}\n" -"\n" -"cc_binary {\n" -" name: \"vmbase_example\",\n" -" defaults: [\"vmbase_elf_defaults\"],\n" -" srcs: [\n" -" \"idmap.S\",\n" -" ],\n" -" static_libs: [\n" -" \"libvmbase_example\",\n" -" ],\n" -" linker_scripts: [\n" -" \"image.ld\",\n" -" \":vmbase_sections\",\n" -" ],\n" -"}\n" -"\n" -"raw_binary {\n" -" name: \"vmbase_example_bin\",\n" -" stem: \"vmbase_example.bin\",\n" -" src: \":vmbase_example\",\n" -" enabled: false,\n" -" target: {\n" -" android_arm64: {\n" -" enabled: true,\n" -" },\n" -" },\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/android/vmbase.md:3 msgid "" "For VMs running under crosvm on aarch64, the [vmbase](https://android." @@ -15008,22 +13824,6 @@ msgid "" "build rules, along with an entry point, UART console logging and more." msgstr "" -#: src/bare-metal/android/vmbase.md:6 -msgid "" -"```rust,compile_fail\n" -"#![no_main]\n" -"#![no_std]\n" -"\n" -"use vmbase::{main, println};\n" -"\n" -"main!(main);\n" -"\n" -"pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) {\n" -" println!(\"Hello world\");\n" -"}\n" -"```" -msgstr "" - #: src/bare-metal/android/vmbase.md:21 msgid "" "The `main!` macro marks your main function, to be called from the `vmbase` " @@ -15148,7 +13948,7 @@ msgstr "" #: src/exercises/bare-metal/rtc.md:75 msgid "" -"`src/exceptions.rs` (you should only need to change this for the 3rd part of " +"_src/exceptions.rs_ (you should only need to change this for the 3rd part of " "the exercise):" msgstr "" @@ -15227,7 +14027,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:149 -msgid "`src/logger.rs` (you shouldn't need to change this):" +msgid "_src/logger.rs_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:153 @@ -15292,7 +14092,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:210 -msgid "`src/pl011.rs` (you shouldn't need to change this):" +msgid "_src/pl011.rs_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:214 @@ -15474,32 +14274,8 @@ msgid "" "```" msgstr "" -#: src/exercises/bare-metal/rtc.md:389 -msgid "" -"```toml\n" -"[workspace]\n" -"\n" -"[package]\n" -"name = \"rtc\"\n" -"version = \"0.1.0\"\n" -"edition = \"2021\"\n" -"publish = false\n" -"\n" -"[dependencies]\n" -"arm-gic = \"0.1.0\"\n" -"bitflags = \"2.0.0\"\n" -"chrono = { version = \"0.4.24\", default-features = false }\n" -"log = \"0.4.17\"\n" -"smccc = \"0.1.1\"\n" -"spin = \"0.9.8\"\n" -"\n" -"[build-dependencies]\n" -"cc = \"1.0.73\"\n" -"```" -msgstr "" - #: src/exercises/bare-metal/rtc.md:410 -msgid "`build.rs` (you shouldn't need to change this):" +msgid "_build.rs_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:414 @@ -15538,7 +14314,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:446 -msgid "`entry.S` (you shouldn't need to change this):" +msgid "_entry.S_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:450 @@ -15704,7 +14480,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:595 -msgid "`exceptions.S` (you shouldn't need to change this):" +msgid "_exceptions.S_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:599 @@ -15902,7 +14678,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:780 -msgid "`idmap.S` (you shouldn't need to change this):" +msgid "_idmap.S_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:784 @@ -15955,7 +14731,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:829 -msgid "`image.ld` (you shouldn't need to change this):" +msgid "_image.ld_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:833 @@ -16070,7 +14846,7 @@ msgid "" msgstr "" #: src/exercises/bare-metal/rtc.md:940 -msgid "`Makefile` (you shouldn't need to change this):" +msgid "_Makefile_ (you shouldn't need to change this):" msgstr "" #: src/exercises/bare-metal/rtc.md:944 @@ -16118,15 +14894,6 @@ msgid "" "```" msgstr "" -#: src/exercises/bare-metal/rtc.md:989 -msgid "" -"```toml\n" -"[build]\n" -"target = \"aarch64-unknown-none\"\n" -"rustflags = [\"-C\", \"link-arg=-Timage.ld\"]\n" -"```" -msgstr "" - #: src/exercises/bare-metal/rtc.md:995 msgid "Run the code in QEMU with `make qemu`." msgstr "" @@ -16276,7 +15043,6 @@ msgstr "" msgid "" "```rust,editable\n" "use std::sync::mpsc;\n" -"use std::thread;\n" "\n" "fn main() {\n" " let (tx, rx) = mpsc::channel();\n" @@ -16294,14 +15060,14 @@ msgid "" "```" msgstr "" -#: src/concurrency/channels.md:27 +#: src/concurrency/channels.md:26 msgid "" "`mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender` " "implement `Clone` (so you can make multiple producers) but `Receiver` does " "not." msgstr "" -#: src/concurrency/channels.md:29 +#: src/concurrency/channels.md:28 msgid "" "`send()` and `recv()` return `Result`. If they return `Err`, it means the " "counterpart `Sender` or `Receiver` is dropped and the channel is closed." @@ -16395,7 +15161,7 @@ msgstr "" #: src/concurrency/send-sync.md:3 msgid "" -"How does Rust know to forbid shared access across thread? The answer is in " +"How does Rust know to forbid shared access across threads? The answer is in " "two traits:" msgstr "" @@ -16561,8 +15327,8 @@ msgstr "" #: src/concurrency/send-sync/examples.md:31 msgid "" -"`MutexGuard`: Uses OS level primitives which must be deallocated on the " -"thread which created them." +"`MutexGuard`: Uses OS level primitives which must be deallocated on " +"the thread which created them." msgstr "" #: src/concurrency/send-sync/examples.md:34 @@ -16646,7 +15412,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 @@ -16893,7 +15659,7 @@ msgid "" "}\n" "\n" "static PHILOSOPHERS: &[&str] =\n" -" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n" +" &[\"Socrates\", \"Hypatia\", \"Plato\", \"Aristotle\", \"Pythagoras\"];\n" "\n" "fn main() {\n" " // Create forks\n" @@ -16935,15 +15701,6 @@ msgid "" "reqwest/). Create a new Cargo project and `reqwest` it as a dependency with:" msgstr "" -#: src/exercises/concurrency/link-checker.md:11 -msgid "" -"```shell\n" -"cargo new link-checker\n" -"cd link-checker\n" -"cargo add --features blocking,rustls-tls reqwest\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:17 msgid "" "If `cargo add` fails with `error: no such subcommand`, then please edit the " @@ -16956,26 +15713,12 @@ msgid "" "rs/scraper/) for that:" msgstr "" -#: src/exercises/concurrency/link-checker.md:22 -msgid "" -"```shell\n" -"cargo add scraper\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:26 msgid "" "Finally, we'll need some way of handling errors. We use [`thiserror`]" "(https://docs.rs/thiserror/) for that:" msgstr "" -#: src/exercises/concurrency/link-checker.md:29 -msgid "" -"```shell\n" -"cargo add thiserror\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:33 msgid "" "The `cargo add` calls will update the `Cargo.toml` file to look like this:" @@ -17081,13 +15824,6 @@ msgstr "" msgid "Run the code in `src/main.rs` with" msgstr "" -#: src/exercises/concurrency/link-checker.md:122 -msgid "" -"```shell\n" -"cargo run\n" -"```" -msgstr "" - #: src/exercises/concurrency/link-checker.md:128 msgid "" "Use threads to check the links in parallel: send the URLs to be checked to a " @@ -17221,31 +15957,12 @@ msgid "" "introduced later). " msgstr "" -#: src/async/futures.md:3 -msgid "" -"[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a " -"trait, implemented by objects that represent an operation that may not be " -"complete yet. A future can be polled, and `poll` returns a [`Poll`](https://" -"doc.rust-lang.org/std/task/enum.Poll.html)." -msgstr "" - -#: src/async/futures.md:8 -msgid "" -"```rust\n" -"use std::pin::Pin;\n" -"use std::task::Context;\n" -"\n" -"pub trait Future {\n" -" type Output;\n" -" fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll;\n" -"}\n" -"\n" -"pub enum Poll {\n" -" Ready(T),\n" -" Pending,\n" -"}\n" -"```" +#: src/async/futures.md:3 +msgid "" +"[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a " +"trait, implemented by objects that represent an operation that may not be " +"complete yet. A future can be polled, and `poll` returns a [`Poll`](https://" +"doc.rust-lang.org/std/task/enum.Poll.html)." msgstr "" #: src/async/futures.md:23 @@ -17468,21 +16185,28 @@ msgstr "" #: src/async/tasks.md:54 msgid "" +"Try connecting to it with a TCP connection tool like [nc](https://www.unix." +"com/man-page/linux/1/nc/) or [telnet](https://www.unix.com/man-page/linux/1/" +"telnet/)." +msgstr "" + +#: src/async/tasks.md:56 +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:57 +#: src/async/tasks.md:59 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:61 +#: src/async/tasks.md:63 msgid "" "Refactor the async block into a function, and improve the error handling " -"using `?`." +"using `?`. " msgstr "" #: src/async/channels.md:3 @@ -17699,7 +16423,7 @@ msgid "" "In this example, we have a race between a cat and a dog. " "`first_animal_to_finish_race` listens to both channels and will pick " "whichever arrives first. Since the dog takes 50ms, it wins against the cat " -"that take 500ms seconds." +"that take 500ms." msgstr "" #: src/async/control-flow/select.md:67 @@ -17924,19 +16648,6 @@ msgid "" "Instead, add a `timeout_fut` containing that future outside of the `loop`:" msgstr "" -#: src/async/pitfalls/pin.md:79 -msgid "" -"```rust,compile_fail\n" -"let mut timeout_fut = sleep(Duration::from_millis(100));\n" -"loop {\n" -" select! {\n" -" ..,\n" -" _ = timeout_fut => { println!(..); },\n" -" }\n" -"}\n" -"```" -msgstr "" - #: src/async/pitfalls/pin.md:88 msgid "" "This still doesn't work. Follow the compiler errors, adding `&mut` to the " @@ -17944,19 +16655,6 @@ msgid "" "pin`:" msgstr "" -#: src/async/pitfalls/pin.md:92 -msgid "" -"```rust,compile_fail\n" -"let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100)));\n" -"loop {\n" -" select! {\n" -" ..,\n" -" _ = &mut timeout_fut => { println!(..); },\n" -" }\n" -"}\n" -"```" -msgstr "" - #: src/async/pitfalls/pin.md:102 msgid "" "This compiles, but once the timeout expires it is `Poll::Ready` on every " @@ -18158,7 +16856,7 @@ msgstr "" #: src/async/pitfalls/cancellation.md:82 msgid "" -"`LinesReader` can be made cancellation-safe by makeing `buf` part of the " +"`LinesReader` can be made cancellation-safe by making `buf` part of the " "struct:" msgstr "" @@ -18275,7 +16973,7 @@ msgid "" "}\n" "\n" "static PHILOSOPHERS: &[&str] =\n" -" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n" +" &[\"Socrates\", \"Hypatia\", \"Plato\", \"Aristotle\", \"Pythagoras\"];\n" "\n" "#[tokio::main]\n" "async fn main() {\n" @@ -18333,7 +17031,7 @@ msgstr "" msgid "" "For this, we use [a broadcast channel](https://docs.rs/tokio/latest/tokio/" "sync/broadcast/fn.channel.html) on the server, and [`tokio_websockets`]" -"(https://docs.rs/tokio-websockets/0.3.2/tokio_websockets/) for the " +"(https://docs.rs/tokio-websockets/0.4.0/tokio_websockets/) for the " "communication between the client and the server." msgstr "" @@ -18342,7 +17040,7 @@ msgid "Create a new Cargo project and add the following dependencies:" msgstr "" #: src/exercises/concurrency/chat-app.md:15 -msgid "`Cargo.toml`:" +msgid "_Cargo.toml_:" msgstr "" #: src/exercises/concurrency/chat-app.md:19 @@ -18354,10 +17052,11 @@ msgid "" "edition = \"2021\"\n" "\n" "[dependencies]\n" -"futures-util = \"0.3.28\"\n" +"futures-util = { version = \"0.3.28\", features = [\"sink\"] }\n" "http = \"0.2.9\"\n" "tokio = { version = \"1.28.1\", features = [\"full\"] }\n" -"tokio-websockets = \"0.3.2\"\n" +"tokio-websockets = { version = \"0.4.0\", features = [\"client\", " +"\"fastrand\", \"server\", \"sha1_smol\"] }\n" "```" msgstr "" @@ -18368,15 +17067,15 @@ msgstr "" #: src/exercises/concurrency/chat-app.md:33 msgid "" "You are going to need the following functions from `tokio` and " -"[`tokio_websockets`](https://docs.rs/tokio-websockets/0.3.2/" +"[`tokio_websockets`](https://docs.rs/tokio-websockets/0.4.0/" "tokio_websockets/). Spend a few minutes to familiarize yourself with the " "API. " msgstr "" #: src/exercises/concurrency/chat-app.md:37 msgid "" -"[WebsocketStream::next()](https://docs.rs/tokio-websockets/0.3.2/" -"tokio_websockets/proto/struct.WebsocketStream.html#method.next): for " +"[StreamExt::next()](https://docs.rs/futures-util/0.3.28/futures_util/stream/" +"trait.StreamExt.html#method.next) implemented by `WebsocketStream`: for " "asynchronously reading messages from a Websocket Stream." msgstr "" @@ -18394,180 +17093,484 @@ msgid "" "standard input." msgstr "" -#: src/exercises/concurrency/chat-app.md:43 -msgid "" -"[Sender::subscribe()](https://docs.rs/tokio/latest/tokio/sync/broadcast/" -"struct.Sender.html#method.subscribe): for subscribing to a broadcast channel." +#: src/exercises/concurrency/chat-app.md:43 +msgid "" +"[Sender::subscribe()](https://docs.rs/tokio/latest/tokio/sync/broadcast/" +"struct.Sender.html#method.subscribe): for subscribing to a broadcast channel." +msgstr "" + +#: src/exercises/concurrency/chat-app.md:46 +msgid "Two binaries" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:48 +msgid "" +"Normally in a Cargo project, you can have only one binary, and one `src/main." +"rs` file. In this project, we need two binaries. One for the client, and one " +"for the server. You could potentially make them two separate Cargo projects, " +"but we are going to put them in a single Cargo project with two binaries. " +"For this to work, the client and the server code should go under `src/bin` " +"(see the [documentation](https://doc.rust-lang.org/cargo/reference/cargo-" +"targets.html#binaries)). " +msgstr "" + +#: src/exercises/concurrency/chat-app.md:55 +msgid "" +"Copy the following server and client code into `src/bin/server.rs` and `src/" +"bin/client.rs`, respectively. Your task is to complete these files as " +"described below. " +msgstr "" + +#: src/exercises/concurrency/chat-app.md:59 +#: src/exercises/concurrency/solutions-afternoon.md:99 +msgid "_src/bin/server.rs_:" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:63 +msgid "" +"```rust,compile_fail\n" +"use futures_util::sink::SinkExt;\n" +"use futures_util::stream::StreamExt;\n" +"use std::error::Error;\n" +"use std::net::SocketAddr;\n" +"use tokio::net::{TcpListener, TcpStream};\n" +"use tokio::sync::broadcast::{channel, Sender};\n" +"use tokio_websockets::{Message, ServerBuilder, WebsocketStream};\n" +"\n" +"async fn handle_connection(\n" +" addr: SocketAddr,\n" +" mut ws_stream: WebsocketStream,\n" +" bcast_tx: Sender,\n" +") -> Result<(), Box> {\n" +"\n" +" // TODO: For a hint, see the description of the task below.\n" +"\n" +"}\n" +"\n" +"#[tokio::main]\n" +"async fn main() -> Result<(), Box> {\n" +" let (bcast_tx, _) = channel(16);\n" +"\n" +" let listener = TcpListener::bind(\"127.0.0.1:2000\").await?;\n" +" println!(\"listening on port 2000\");\n" +"\n" +" loop {\n" +" let (socket, addr) = listener.accept().await?;\n" +" println!(\"New connection from {addr:?}\");\n" +" let bcast_tx = bcast_tx.clone();\n" +" tokio::spawn(async move {\n" +" // Wrap the raw TCP stream into a websocket.\n" +" let ws_stream = ServerBuilder::new().accept(socket).await?;\n" +"\n" +" handle_connection(addr, ws_stream, bcast_tx).await\n" +" });\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:103 +#: src/exercises/concurrency/solutions-afternoon.md:166 +msgid "_src/bin/client.rs_:" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:107 +msgid "" +"```rust,compile_fail\n" +"use futures_util::stream::StreamExt;\n" +"use futures_util::SinkExt;\n" +"use http::Uri;\n" +"use tokio::io::{AsyncBufReadExt, BufReader};\n" +"use tokio_websockets::{ClientBuilder, Message};\n" +"\n" +"#[tokio::main]\n" +"async fn main() -> Result<(), tokio_websockets::Error> {\n" +" let (mut ws_stream, _) =\n" +" ClientBuilder::from_uri(Uri::from_static(\"ws://127.0.0.1:2000\"))\n" +" .connect()\n" +" .await?;\n" +"\n" +" let stdin = tokio::io::stdin();\n" +" let mut stdin = BufReader::new(stdin).lines();\n" +"\n" +"\n" +" // TODO: For a hint, see the description of the task below.\n" +"\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:130 +msgid "Running the binaries" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:131 +msgid "Run the server with:" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:137 +msgid "and the client with:" +msgstr "" + +#: src/exercises/concurrency/chat-app.md:145 +msgid "Implement the `handle_connection` function in `src/bin/server.rs`." +msgstr "" + +#: src/exercises/concurrency/chat-app.md:146 +msgid "" +"Hint: Use `tokio::select!` for concurrently performing two tasks in a " +"continuous loop. One task receives messages from the client and broadcasts " +"them. The other sends messages received by the server to the client." +msgstr "" + +#: src/exercises/concurrency/chat-app.md:149 +msgid "Complete the main function in `src/bin/client.rs`." +msgstr "" + +#: src/exercises/concurrency/chat-app.md:150 +msgid "" +"Hint: As before, use `tokio::select!` in a continuous loop for concurrently " +"performing two tasks: (1) reading user messages from standard input and " +"sending them to the server, and (2) receiving messages from the server, and " +"displaying them for the user." +msgstr "" + +#: src/exercises/concurrency/chat-app.md:154 +msgid "" +"Optional: Once you are done, change the code to broadcast messages to all " +"clients, but the sender of the message." +msgstr "" + +#: src/thanks.md:3 +msgid "" +"_Thank you for taking Comprehensive Rust 🦀!_ We hope you enjoyed it and " +"that it was useful." +msgstr "" + +#: src/thanks.md:6 +msgid "" +"We've had a lot of fun putting the course together. The course is not " +"perfect, so if you spotted any mistakes or have ideas for improvements, " +"please get in [contact with us on GitHub](https://github.com/google/" +"comprehensive-rust/discussions). We would love to hear from you." +msgstr "" + +#: src/glossary.md:3 +msgid "" +"The following is a glossary which aims to give a short definition of many " +"Rust terms. For translations, this also serves to connect the term back to " +"the English original." +msgstr "" + +#: src/glossary.md:30 +msgid "" +"allocate: \n" +"Dynamic memory allocation on [the heap](memory-management/stack-vs-heap.md)." +msgstr "" + +#: src/glossary.md:32 +msgid "argument:" +msgstr "" + +#: src/glossary.md:33 +msgid "" +"Bare-metal Rust: \n" +"Low-level Rust development, often deployed to a system without an operating " +"system. See [Bare-metal Rust](bare-metal.md)." +msgstr "" + +#: src/glossary.md:36 +msgid "" +"block: \n" +"See [Blocks](control-flow/blocks.md) and _scope_." +msgstr "" + +#: src/glossary.md:38 +msgid "" +"borrow: \n" +"See [Borrowing](ownership/borrowing.md)." +msgstr "" + +#: src/glossary.md:40 +msgid "" +"borrow checker: \n" +"The part of the Rust compiler which checks that all borrows are valid." +msgstr "" + +#: src/glossary.md:42 +msgid "" +"brace: \n" +"`{` and `}`. Also called _curly brace_, they delimit _blocks_." +msgstr "" + +#: src/glossary.md:44 +msgid "build:" +msgstr "" + +#: src/glossary.md:45 +msgid "call:" +msgstr "" + +#: src/glossary.md:46 +msgid "" +"channel: \n" +"Used to safely pass messages [between threads](concurrency/channels.md)." +msgstr "" + +#: src/glossary.md:48 +msgid "" +"Comprehensive Rust 🦀: \n" +"The courses here are jointly called Comprehensive Rust 🦀." +msgstr "" + +#: src/glossary.md:50 +#, fuzzy +msgid "concurrency:" +msgstr "همزمانی" + +#: src/glossary.md:51 +msgid "" +"Concurrency in Rust: \n" +"See [Concurrency in Rust](concurrency.md)." +msgstr "" + +#: src/glossary.md:53 +msgid "constant:" +msgstr "" + +#: src/glossary.md:54 +#, fuzzy +msgid "control flow:" +msgstr "کنترل جریان" + +#: src/glossary.md:55 +msgid "crash:" +msgstr "" + +#: src/glossary.md:56 +#, fuzzy +msgid "enumeration:" +msgstr "پیاده سازی" + +#: src/glossary.md:57 +msgid "error:" +msgstr "" + +#: src/glossary.md:58 +#, fuzzy +msgid "error handling:" +msgstr "مدیریت خطا (Error Handling)" + +#: src/glossary.md:59 +#, fuzzy +msgid "exercise:" +msgstr "تمرین‌ها" + +#: src/glossary.md:60 +#, fuzzy +msgid "function:" +msgstr "توابع" + +#: src/glossary.md:61 +#, fuzzy +msgid "garbage collector:" +msgstr "Garbage Collection" + +#: src/glossary.md:62 +#, fuzzy +msgid "generics:" +msgstr "Generics" + +#: src/glossary.md:63 +msgid "immutable:" +msgstr "" + +#: src/glossary.md:64 +#, fuzzy +msgid "integration test:" +msgstr "Integration Tests" + +#: src/glossary.md:65 +msgid "keyword:" +msgstr "" + +#: src/glossary.md:66 +#, fuzzy +msgid "library:" +msgstr "کتابخانه" + +#: src/glossary.md:67 +msgid "macro:" +msgstr "" + +#: src/glossary.md:68 +#, fuzzy +msgid "main function:" +msgstr "فراخوانی متدهای ناامن" + +#: src/glossary.md:69 +msgid "match:" +msgstr "" + +#: src/glossary.md:70 +msgid "memory leak:" +msgstr "" + +#: src/glossary.md:71 +#, fuzzy +msgid "method:" +msgstr "متدها" + +#: src/glossary.md:72 +#, fuzzy +msgid "module:" +msgstr "ماژول‌ها" + +#: src/glossary.md:73 +msgid "move:" msgstr "" -#: src/exercises/concurrency/chat-app.md:46 -msgid "Two binaries" +#: src/glossary.md:74 +msgid "mutable:" msgstr "" -#: src/exercises/concurrency/chat-app.md:48 -msgid "" -"Normally in a Cargo project, you can have only one binary, and one `src/main." -"rs` file. In this project, we need two binaries. One for the client, and one " -"for the server. You could potentially make them two separate Cargo projects, " -"but we are going to put them in a single Cargo project with two binaries. " -"For this to work, the client and the server code should go under `src/bin` " -"(see the [documentation](https://doc.rust-lang.org/cargo/reference/cargo-" -"targets.html#binaries)). " -msgstr "" +#: src/glossary.md:75 +#, fuzzy +msgid "ownership:" +msgstr "مالکیت" -#: src/exercises/concurrency/chat-app.md:55 -msgid "" -"Copy the following server and client code into `src/bin/server.rs` and `src/" -"bin/client.rs`, respectively. Your task is to complete these files as " -"described below. " +#: src/glossary.md:76 +#, fuzzy +msgid "panic:" +msgstr "Panics" + +#: src/glossary.md:77 +msgid "parameter:" msgstr "" -#: src/exercises/concurrency/chat-app.md:59 -#: src/exercises/concurrency/solutions-afternoon.md:123 -msgid "`src/bin/server.rs`:" +#: src/glossary.md:78 +msgid "pattern:" msgstr "" -#: src/exercises/concurrency/chat-app.md:63 -msgid "" -"```rust,compile_fail\n" -"use futures_util::sink::SinkExt;\n" -"use std::error::Error;\n" -"use std::net::SocketAddr;\n" -"use tokio::net::{TcpListener, TcpStream};\n" -"use tokio::sync::broadcast::{channel, Sender};\n" -"use tokio_websockets::{Message, ServerBuilder, WebsocketStream};\n" -"\n" -"async fn handle_connection(\n" -" addr: SocketAddr,\n" -" mut ws_stream: WebsocketStream,\n" -" bcast_tx: Sender,\n" -") -> Result<(), Box> {\n" -"\n" -" // TODO: For a hint, see the description of the task below.\n" -"\n" -"}\n" -"\n" -"#[tokio::main]\n" -"async fn main() -> Result<(), Box> {\n" -" let (bcast_tx, _) = channel(16);\n" -"\n" -" let listener = TcpListener::bind(\"127.0.0.1:2000\").await?;\n" -" println!(\"listening on port 2000\");\n" -"\n" -" loop {\n" -" let (socket, addr) = listener.accept().await?;\n" -" println!(\"New connection from {addr:?}\");\n" -" let bcast_tx = bcast_tx.clone();\n" -" tokio::spawn(async move {\n" -" // Wrap the raw TCP stream into a websocket.\n" -" let ws_stream = ServerBuilder::new().accept(socket).await?;\n" -"\n" -" handle_connection(addr, ws_stream, bcast_tx).await\n" -" });\n" -" }\n" -"}\n" -"```" +#: src/glossary.md:79 +msgid "payload:" msgstr "" -#: src/exercises/concurrency/chat-app.md:102 -#: src/exercises/concurrency/solutions-afternoon.md:208 -msgid "`src/bin/client.rs`:" +#: src/glossary.md:80 +msgid "program:" msgstr "" -#: src/exercises/concurrency/chat-app.md:106 -msgid "" -"```rust,compile_fail\n" -"use futures_util::SinkExt;\n" -"use http::Uri;\n" -"use tokio::io::{AsyncBufReadExt, BufReader};\n" -"use tokio_websockets::{ClientBuilder, Message};\n" -"\n" -"#[tokio::main]\n" -"async fn main() -> Result<(), tokio_websockets::Error> {\n" -" let mut ws_stream = ClientBuilder::from_uri(Uri::" -"from_static(\"ws://127.0.0.1:2000\"))\n" -" .connect()\n" -" .await?;\n" -"\n" -" let stdin = tokio::io::stdin();\n" -" let mut stdin = BufReader::new(stdin).lines();\n" -"\n" -"\n" -" // TODO: For a hint, see the description of the task below.\n" -"\n" -"}\n" -"```" +#: src/glossary.md:81 +msgid "programming language:" msgstr "" -#: src/exercises/concurrency/chat-app.md:127 -msgid "Running the binaries" +#: src/glossary.md:82 +#, fuzzy +msgid "receiver:" +msgstr "درایور" + +#: src/glossary.md:83 +msgid "reference counting:" msgstr "" -#: src/exercises/concurrency/chat-app.md:128 -msgid "Run the server with:" +#: src/glossary.md:84 +msgid "return:" msgstr "" -#: src/exercises/concurrency/chat-app.md:130 +#: src/glossary.md:85 +#, fuzzy +msgid "Rust:" +msgstr "Rustdoc" + +#: src/glossary.md:86 msgid "" -"```shell\n" -"cargo run --bin server\n" -"```" +"Rust Fundamentals: \n" +"Days 1 to 3 of this course." msgstr "" -#: src/exercises/concurrency/chat-app.md:134 -msgid "and the client with:" +#: src/glossary.md:88 +msgid "" +"Rust in Android: \n" +"See [Rust in Android](android.md)." msgstr "" -#: src/exercises/concurrency/chat-app.md:136 -msgid "" -"```shell\n" -"cargo run --bin client\n" -"```" +#: src/glossary.md:90 +msgid "safe:" msgstr "" -#: src/exercises/concurrency/chat-app.md:142 -msgid "Implement the `handle_connection` function in `src/bin/server.rs`." +#: src/glossary.md:91 +msgid "scope:" msgstr "" -#: src/exercises/concurrency/chat-app.md:143 -msgid "" -"Hint: Use `tokio::select!` for concurrently performing two tasks in a " -"continuous loop. One task receives messages from the client and broadcasts " -"them. The other sends messages received by the server to the client." +#: src/glossary.md:92 +#, fuzzy +msgid "standard library:" +msgstr "کتابخانه‌های استاندارد" + +#: src/glossary.md:93 +msgid "static:" msgstr "" -#: src/exercises/concurrency/chat-app.md:146 -msgid "Complete the main function in `src/bin/client.rs`." +#: src/glossary.md:94 +#, fuzzy +msgid "string:" +msgstr "String" + +#: src/glossary.md:95 +#, fuzzy +msgid "struct:" +msgstr "ساختارها" + +#: src/glossary.md:96 +msgid "test:" msgstr "" -#: src/exercises/concurrency/chat-app.md:147 -msgid "" -"Hint: As before, use `tokio::select!` in a continuous loop for concurrently " -"performing two tasks: (1) reading user messages from standard input and " -"sending them to the server, and (2) receiving messages from the server, and " -"displaying them for the user." +#: src/glossary.md:97 +#, fuzzy +msgid "thread:" +msgstr "تردها" + +#: src/glossary.md:98 +msgid "thread safety:" msgstr "" -#: src/exercises/concurrency/chat-app.md:151 -msgid "" -"Optional: Once you are done, change the code to broadcast messages to all " -"clients, but the sender of the message." +#: src/glossary.md:99 +#, fuzzy +msgid "trait:" +msgstr "صفت‌ها" + +#: src/glossary.md:100 +msgid "type:" msgstr "" -#: src/thanks.md:3 -msgid "" -"_Thank you for taking Comprehensive Rust 🦀!_ We hope you enjoyed it and " -"that it was useful." +#: src/glossary.md:101 +#, fuzzy +msgid "type inference:" +msgstr "Type Inference" + +#: src/glossary.md:102 +msgid "undefined behavior:" msgstr "" -#: src/thanks.md:6 -msgid "" -"We've had a lot of fun putting the course together. The course is not " -"perfect, so if you spotted any mistakes or have ideas for improvements, " -"please get in [contact with us on GitHub](https://github.com/google/" -"comprehensive-rust/discussions). We would love to hear from you." +#: src/glossary.md:103 +#, fuzzy +msgid "union:" +msgstr "Unions" + +#: src/glossary.md:104 +#, fuzzy +msgid "unit test:" +msgstr "تست‌های واحد (Unit Tests)" + +#: src/glossary.md:105 +msgid "unsafe:" msgstr "" +#: src/glossary.md:106 +#, fuzzy +msgid "variable:\\" +msgstr "متغیرها" + #: src/other-resources.md:1 msgid "Other Rust Resources" msgstr "" @@ -18750,6 +17753,15 @@ msgid "" "directory for details, including the license terms." msgstr "" +#: src/credits.md:34 +msgid "" +"The [Why Rust? - An Example in C](why-rust/an-example-in-c.md) section has " +"been taken from the presentation slides of [Colin Finck's Master Thesis]" +"(https://colinfinck.de/Master_Thesis_Colin_Finck.pdf). It has been " +"relicensed under the terms of the Apache 2.0 license for this course by the " +"author." +msgstr "" + #: src/exercises/solutions.md:3 msgid "You will find solutions to the exercises on the following pages." msgstr "" @@ -18761,13 +17773,6 @@ msgid "" "different or better solution than what is presented here." msgstr "" -#: src/exercises/solutions.md:10 -msgid "" -"**Note:** Please ignore the `// ANCHOR: label` and `// ANCHOR_END: label` " -"comments you see in the solutions. They are there to make it possible to re-" -"use parts of the solutions as the exercises." -msgstr "" - #: src/exercises/day-1/solutions-morning.md:1 msgid "Day 1 Morning Exercises" msgstr "" @@ -18779,23 +17784,7 @@ msgstr "" #: src/exercises/day-1/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: transpose\n" "fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {\n" -" // ANCHOR_END: transpose\n" " let mut result = [[0; 3]; 3];\n" " for i in 0..3 {\n" " for j in 0..3 {\n" @@ -18805,15 +17794,12 @@ msgid "" " return result;\n" "}\n" "\n" -"// ANCHOR: pretty_print\n" "fn pretty_print(matrix: &[[i32; 3]; 3]) {\n" -" // ANCHOR_END: pretty_print\n" " for row in matrix {\n" " println!(\"{row:?}\");\n" " }\n" "}\n" "\n" -"// ANCHOR: tests\n" "#[test]\n" "fn test_transpose() {\n" " let matrix = [\n" @@ -18831,9 +17817,7 @@ msgid "" " ]\n" " );\n" "}\n" -"// ANCHOR_END: tests\n" "\n" -"// ANCHOR: main\n" "fn main() {\n" " let matrix = [\n" " [101, 102, 103], // <-- the comment makes rustfmt add a newline\n" @@ -18851,11 +17835,11 @@ msgid "" "```" msgstr "" -#: src/exercises/day-1/solutions-morning.md:78 +#: src/exercises/day-1/solutions-morning.md:57 msgid "Bonus question" msgstr "" -#: src/exercises/day-1/solutions-morning.md:80 +#: src/exercises/day-1/solutions-morning.md:59 msgid "" "It requires more advanced concepts. It might seem that we could use a slice-" "of-slices (`&[&[i32]]`) as the input type to transpose and thus make our " @@ -18863,21 +17847,21 @@ msgid "" "return type cannot be `&[&[i32]]` since it needs to own the data you return." msgstr "" -#: src/exercises/day-1/solutions-morning.md:82 +#: src/exercises/day-1/solutions-morning.md:61 msgid "" "You can attempt to use something like `Vec>`, but this doesn't work " "out-of-the-box either: it's hard to convert from `Vec>` to " "`&[&[i32]]` so now you cannot easily use `pretty_print` either." msgstr "" -#: src/exercises/day-1/solutions-morning.md:84 +#: src/exercises/day-1/solutions-morning.md:63 msgid "" "Once we get to traits and generics, we'll be able to use the [`std::convert::" "AsRef`](https://doc.rust-lang.org/std/convert/trait.AsRef.html) trait to " "abstract over anything that can be referenced as a slice." msgstr "" -#: src/exercises/day-1/solutions-morning.md:86 +#: src/exercises/day-1/solutions-morning.md:65 msgid "" "```rust\n" "use std::convert::AsRef;\n" @@ -18907,7 +17891,7 @@ msgid "" "```" msgstr "" -#: src/exercises/day-1/solutions-morning.md:113 +#: src/exercises/day-1/solutions-morning.md:92 msgid "" "In addition, the type itself would not enforce that the child slices are of " "the same length, so such variable could contain an invalid matrix." @@ -18917,208 +17901,225 @@ msgstr "" msgid "Day 1 Afternoon Exercises" msgstr "" -#: src/exercises/day-1/solutions-afternoon.md:3 -msgid "Designing a Library" -msgstr "" - #: src/exercises/day-1/solutions-afternoon.md:5 -msgid "([back to exercise](book-library.md))" +msgid "([back to exercise](luhn.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" +"pub fn luhn(cc_number: &str) -> bool {\n" +" let mut sum = 0;\n" +" let mut double = false;\n" +" let mut digit_seen = 0;\n" +"\n" +" for c in cc_number.chars().filter(|&f| f != ' ').rev() {\n" +" if let Some(digit) = c.to_digit(10) {\n" +" if double {\n" +" let double_digit = digit * 2;\n" +" sum += if double_digit > 9 {\n" +" double_digit - 9\n" +" } else {\n" +" double_digit\n" +" };\n" +" } else {\n" +" sum += digit;\n" " }\n" +" double = !double;\n" +" digit_seen += 1;\n" +" } else {\n" +" return false;\n" " }\n" +" }\n" "\n" -" oldest\n" +" if digit_seen < 2 {\n" +" return false;\n" " }\n" +"\n" +" sum % 10 == 0\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" +" let cc_number = \"1234 5678 1234 5670\";\n" +" println!(\n" +" \"Is {cc_number} a valid credit card number? {}\",\n" +" if luhn(cc_number) { \"yes\" } else { \"no\" }\n" +" );\n" +"}\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" +"#[test]\n" +"fn test_non_digit_cc_number() {\n" +" assert!(!luhn(\"foo\"));\n" +" assert!(!luhn(\"foo 0 0\"));\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" +"fn test_empty_cc_number() {\n" +" assert!(!luhn(\"\"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +" assert!(!luhn(\" \"));\n" +"}\n" "\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" -" assert_eq!(library.len(), 2);\n" -" assert!(!library.is_empty());\n" +"#[test]\n" +"fn test_single_digit_cc_number() {\n" +" assert!(!luhn(\"0\"));\n" "}\n" "\n" "#[test]\n" -"fn test_library_is_empty() {\n" -" let mut library = Library::new();\n" -" assert!(library.is_empty());\n" +"fn test_two_digit_cc_number() {\n" +" assert!(luhn(\" 0 0 \"));\n" +"}\n" "\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" -" assert!(!library.is_empty());\n" +"#[test]\n" +"fn test_valid_cc_number() {\n" +" assert!(luhn(\"4263 9826 4026 9299\"));\n" +" assert!(luhn(\"4539 3195 0343 6467\"));\n" +" assert!(luhn(\"7992 7398 713\"));\n" "}\n" "\n" "#[test]\n" -"fn test_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" +"fn test_invalid_cc_number() {\n" +" assert!(!luhn(\"4223 9826 4026 9299\"));\n" +" assert!(!luhn(\"4539 3195 0343 6476\"));\n" +" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-1/solutions-afternoon.md:86 +#, fuzzy +msgid "Pattern matching" +msgstr "تطبیق الگو" + +#: src/exercises/day-1/solutions-afternoon.md:88 +msgid "" +"```rust\n" +"/// An operation to perform on two subexpressions.\n" +"#[derive(Debug)]\n" +"enum Operation {\n" +" Add,\n" +" Sub,\n" +" Mul,\n" +" Div,\n" +"}\n" +"\n" +"/// An expression, in tree form.\n" +"#[derive(Debug)]\n" +"enum Expression {\n" +" /// An operation on two subexpressions.\n" +" Op {\n" +" op: Operation,\n" +" left: Box,\n" +" right: Box,\n" +" },\n" +"\n" +" /// A literal value\n" +" Value(i64),\n" +"}\n" +"\n" +"/// The result of evaluating an expression.\n" +"#[derive(Debug, PartialEq, Eq)]\n" +"enum Res {\n" +" /// Evaluation was successful, with the given result.\n" +" Ok(i64),\n" +" /// Evaluation failed, with the given error message.\n" +" Err(String),\n" +"}\n" +"// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.\n" +"use Res::{Err, Ok};\n" +"\n" +"fn eval(e: Expression) -> Res {\n" +" match e {\n" +" Expression::Op { op, left, right } => {\n" +" let left = match eval(*left) {\n" +" Ok(v) => v,\n" +" Err(msg) => return Err(msg),\n" +" };\n" +" let right = match eval(*right) {\n" +" Ok(v) => v,\n" +" Err(msg) => return Err(msg),\n" +" };\n" +" Ok(match op {\n" +" Operation::Add => left + right,\n" +" Operation::Sub => left - right,\n" +" Operation::Mul => left * right,\n" +" Operation::Div => {\n" +" if right == 0 {\n" +" return Err(String::from(\"division by zero\"));\n" +" } else {\n" +" left / right\n" +" }\n" +" }\n" +" })\n" +" }\n" +" Expression::Value(v) => Ok(v),\n" +" }\n" "}\n" "\n" "#[test]\n" -"fn test_library_oldest_book() {\n" -" let mut library = Library::new();\n" -" assert!(library.oldest_book().is_none());\n" +"fn test_value() {\n" +" assert_eq!(eval(Expression::Value(19)), Ok(19));\n" +"}\n" "\n" -" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +"#[test]\n" +"fn test_sum() {\n" " assert_eq!(\n" -" library.oldest_book().map(|b| b.title.as_str()),\n" -" Some(\"Lord of the Rings\")\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(20)),\n" +" }),\n" +" Ok(30)\n" " );\n" +"}\n" "\n" -" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " -"1865));\n" +"#[test]\n" +"fn test_recursion() {\n" +" let term1 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Value(10)),\n" +" right: Box::new(Expression::Value(9)),\n" +" };\n" +" let term2 = Expression::Op {\n" +" op: Operation::Mul,\n" +" left: Box::new(Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(3)),\n" +" right: Box::new(Expression::Value(4)),\n" +" }),\n" +" right: Box::new(Expression::Value(5)),\n" +" };\n" " assert_eq!(\n" -" library.oldest_book().map(|b| b.title.as_str()),\n" -" Some(\"Alice's Adventures in Wonderland\")\n" +" eval(Expression::Op {\n" +" op: Operation::Add,\n" +" left: Box::new(term1),\n" +" right: Box::new(term2),\n" +" }),\n" +" Ok(85)\n" +" );\n" +"}\n" +"\n" +"#[test]\n" +"fn test_error() {\n" +" assert_eq!(\n" +" eval(Expression::Op {\n" +" op: Operation::Div,\n" +" left: Box::new(Expression::Value(99)),\n" +" right: Box::new(Expression::Value(0)),\n" +" }),\n" +" Err(String::from(\"division by zero\"))\n" " );\n" "}\n" +"fn main() {\n" +" let expr = Expression::Op {\n" +" op: Operation::Sub,\n" +" left: Box::new(Expression::Value(20)),\n" +" right: Box::new(Expression::Value(10)),\n" +" };\n" +" println!(\"expr: {:?}\", expr);\n" +" println!(\"result: {:?}\", eval(expr));\n" +"}\n" "```" msgstr "" @@ -19126,379 +18127,303 @@ msgstr "" 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](points-polygons.md))" +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" -"#[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" +"struct Library {\n" +" books: Vec,\n" "}\n" "\n" -"// ANCHOR: Polygon\n" -"pub struct Polygon {\n" -" // ANCHOR_END: Polygon\n" -" points: Vec,\n" +"struct Book {\n" +" title: String,\n" +" year: u16,\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" +"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" -" 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" +"// Implement the methods below. Notice how the `self` parameter\n" +"// changes type to indicate the method's required level of ownership\n" +"// over the object:\n" +"//\n" +"// - `&self` for shared read-only access,\n" +"// - `&mut self` for unique and mutable access,\n" +"// - `self` for unique access by value.\n" +"impl Library {\n" "\n" -"// 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" +" fn new() -> Library {\n" +" Library { books: Vec::new() }\n" " }\n" "\n" -" pub fn circumference(&self) -> f64 {\n" -" 2.0 * std::f64::consts::PI * f64::from(self.radius)\n" +" fn len(&self) -> usize {\n" +" self.books.len()\n" " }\n" "\n" -" pub fn dist(&self, other: &Self) -> f64 {\n" -" self.center.dist(other.center)\n" +" fn is_empty(&self) -> bool {\n" +" self.books.is_empty()\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" +" fn add_book(&mut self, book: Book) {\n" +" self.books.push(book)\n" " }\n" -"}\n" "\n" -"impl From for Shape {\n" -" fn from(circle: Circle) -> Self {\n" -" Shape::Circle(circle)\n" +" fn print_books(&self) {\n" +" for book in &self.books {\n" +" println!(\"{}, published in {}\", book.title, book.year);\n" +" }\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" +" 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: unit-tests\n" -"#[cfg(test)]\n" -"mod tests {\n" -" use super::*;\n" +"fn main() {\n" +" let mut library = Library::new();\n" "\n" -" fn round_two_digits(x: f64) -> f64 {\n" -" (x * 100.0).round() / 100.0\n" -" }\n" +" println!(\n" +" \"The library is empty: library.is_empty() -> {}\",\n" +" library.is_empty()\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" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" library.add_book(Book::new(\"Alice's Adventures in Wonderland\", " +"1865));\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" +" println!(\n" +" \"The library is no longer empty: library.is_empty() -> {}\",\n" +" library.is_empty()\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" +" 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" -" #[test]\n" -" fn test_polygon_left_most_point() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\n" +" println!(\"The library has {} books\", library.len());\n" +" library.print_books();\n" +"}\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" +"#[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" -" #[test]\n" -" fn test_polygon_iter() {\n" -" let p1 = Point::new(12, 13);\n" -" let p2 = Point::new(16, 16);\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" -" let mut poly = Polygon::new();\n" -" poly.add_point(p1);\n" -" poly.add_point(p2);\n" +"#[test]\n" +"fn test_library_is_empty() {\n" +" let mut library = Library::new();\n" +" assert!(library.is_empty());\n" "\n" -" let points = poly.iter().cloned().collect::>();\n" -" assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);\n" -" }\n" +" library.add_book(Book::new(\"Lord of the Rings\", 1954));\n" +" assert!(!library.is_empty());\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" +"#[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" -"// ANCHOR_END: unit-tests\n" "\n" -"fn main() {}\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:1 -msgid "Day 2 Afternoon Exercises" -msgstr "" - -#: src/exercises/day-2/solutions-afternoon.md:5 -msgid "([back to exercise](luhn.md))" +#: src/exercises/day-2/solutions-morning.md:153 +msgid "([back to exercise](health-statistics.md))" msgstr "" -#: src/exercises/day-2/solutions-afternoon.md:7 +#: src/exercises/day-2/solutions-morning.md:155 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" +"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" -"// ANCHOR: luhn\n" -"pub fn luhn(cc_number: &str) -> bool {\n" -" // ANCHOR_END: luhn\n" -" let mut digits_seen = 0;\n" -" let mut sum = 0;\n" -" for (i, ch) in cc_number.chars().rev().filter(|&ch| ch != ' ')." -"enumerate() {\n" -" match ch.to_digit(10) {\n" -" Some(d) => {\n" -" sum += if i % 2 == 1 {\n" -" let dd = d * 2;\n" -" dd / 10 + dd % 10\n" -" } else {\n" -" d\n" -" };\n" -" digits_seen += 1;\n" -" }\n" -" None => return false,\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" +" Self {\n" +" name,\n" +" age,\n" +" height,\n" +" visit_count: 0,\n" +" last_blood_pressure: None,\n" " }\n" " }\n" "\n" -" if digits_seen < 2 {\n" -" return false;\n" +" pub fn name(&self) -> &str {\n" +" &self.name\n" " }\n" "\n" -" sum % 10 == 0\n" -"}\n" +" pub fn age(&self) -> u32 {\n" +" self.age\n" +" }\n" "\n" -"fn main() {\n" -" let cc_number = \"1234 5678 1234 5670\";\n" -" println!(\n" -" \"Is {cc_number} a valid credit card number? {}\",\n" -" if luhn(cc_number) { \"yes\" } else { \"no\" }\n" -" );\n" +" pub fn height(&self) -> f32 {\n" +" self.height\n" +" }\n" +"\n" +" pub fn doctor_visits(&self) -> u32 {\n" +" self.visit_count as u32\n" +" }\n" +"\n" +" pub fn set_age(&mut self, new_age: u32) {\n" +" self.age = new_age\n" +" }\n" +"\n" +" pub fn set_height(&mut self, new_height: f32) {\n" +" self.height = new_height\n" +" }\n" +"\n" +" pub fn visit_doctor(&mut self, measurements: Measurements) -> " +"HealthReport {\n" +" self.visit_count += 1;\n" +" let bp = measurements.blood_pressure;\n" +" let report = HealthReport {\n" +" patient_name: &self.name,\n" +" visit_count: self.visit_count as u32,\n" +" height_change: measurements.height - self.height,\n" +" blood_pressure_change: match self.last_blood_pressure {\n" +" Some(lbp) => Some((\n" +" bp.0 as i32 - lbp.0 as i32,\n" +" bp.1 as i32 - lbp.1 as i32\n" +" )),\n" +" None => None,\n" +" }\n" +" };\n" +" self.height = measurements.height;\n" +" self.last_blood_pressure = Some(bp);\n" +" report\n" +" }\n" "}\n" "\n" -"// ANCHOR: unit-tests\n" -"#[test]\n" -"fn test_non_digit_cc_number() {\n" -" assert!(!luhn(\"foo\"));\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_empty_cc_number() {\n" -" assert!(!luhn(\"\"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\n" -" assert!(!luhn(\" \"));\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" "#[test]\n" -"fn test_single_digit_cc_number() {\n" -" assert!(!luhn(\"0\"));\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" "#[test]\n" -"fn test_two_digit_cc_number() {\n" -" assert!(luhn(\" 0 0 \"));\n" -"}\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" -"#[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" +" let report = bob.visit_doctor(Measurements {\n" +" height: 156.1,\n" +" blood_pressure: (115, 76),\n" +" });\n" "\n" -"#[test]\n" -"fn test_invalid_cc_number() {\n" -" assert!(!luhn(\"4223 9826 4026 9299\"));\n" -" assert!(!luhn(\"4539 3195 0343 6476\"));\n" -" assert!(!luhn(\"8273 1232 7352 0569\"));\n" +" assert_eq!(report.visit_count, 2);\n" +" assert_eq!(report.blood_pressure_change, Some((-5, -4)));\n" "}\n" -"// ANCHOR_END: unit-tests\n" "```" msgstr "" -#: src/exercises/day-2/solutions-afternoon.md:99 +#: src/exercises/day-2/solutions-afternoon.md:1 +msgid "Day 2 Afternoon Exercises" +msgstr "" + +#: src/exercises/day-2/solutions-afternoon.md:5 msgid "([back to exercise](strings-iterators.md))" msgstr "" -#: src/exercises/day-2/solutions-afternoon.md:101 +#: src/exercises/day-2/solutions-afternoon.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: prefix_matches\n" "pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {\n" -" // ANCHOR_END: prefix_matches\n" "\n" " let mut request_segments = request_path.split('/');\n" "\n" @@ -19525,7 +18450,6 @@ msgid "" " // and then returns None indefinitely.\n" "}\n" "\n" -"// ANCHOR: unit-tests\n" "#[test]\n" "fn test_matches_without_wildcard() {\n" " assert!(prefix_matches(\"/v1/publishers\", \"/v1/publishers\"));\n" @@ -19562,7 +18486,6 @@ msgid "" " \"/v1/publishers/foo/booksByAuthor\"\n" " ));\n" "}\n" -"// ANCHOR_END: unit-tests\n" "\n" "fn main() {}\n" "```" @@ -19579,21 +18502,6 @@ msgstr "" #: src/exercises/day-3/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" "pub trait Widget {\n" " /// Natural width of `self`.\n" " fn width(&self) -> usize;\n" @@ -19623,14 +18531,12 @@ msgid "" "\n" "pub struct Button {\n" " label: Label,\n" -" callback: Box,\n" "}\n" "\n" "impl Button {\n" -" fn new(label: &str, callback: Box) -> Button {\n" +" fn new(label: &str) -> Button {\n" " Button {\n" " label: Label::new(label),\n" -" callback,\n" " }\n" " }\n" "}\n" @@ -19648,103 +18554,304 @@ msgid "" " }\n" " }\n" "\n" -" fn add_widget(&mut self, widget: Box) {\n" -" self.widgets.push(widget);\n" +" fn add_widget(&mut self, widget: Box) {\n" +" self.widgets.push(widget);\n" +" }\n" +"\n" +" fn inner_width(&self) -> usize {\n" +" std::cmp::max(\n" +" self.title.chars().count(),\n" +" self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),\n" +" )\n" +" }\n" +"}\n" +"\n" +"\n" +"impl Widget for Window {\n" +" fn width(&self) -> usize {\n" +" // Add 4 paddings for borders\n" +" self.inner_width() + 4\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\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" +" self.label.width() + 8 // add a bit of padding\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" +" let width = self.width();\n" +" let mut label = String::new();\n" +" self.label.draw_into(&mut label);\n" +"\n" +" writeln!(buffer, \"+{:- usize {\n" +" self.label\n" +" .lines()\n" +" .map(|line| line.chars().count())\n" +" .max()\n" +" .unwrap_or(0)\n" +" }\n" +"\n" +" fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {\n" +" writeln!(buffer, \"{}\", &self.label).unwrap();\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let mut window = Window::new(\"Rust GUI Demo 1.23\");\n" +" window.add_widget(Box::new(Label::new(\"This is a small text GUI demo." +"\")));\n" +" window.add_widget(Box::new(Button::new(\n" +" \"Click me!\"\n" +" )));\n" +" window.draw();\n" +"}\n" +"```" +msgstr "" + +#: src/exercises/day-3/solutions-morning.md:144 +msgid "([back to exercise](points-polygons.md))" +msgstr "" + +#: src/exercises/day-3/solutions-morning.md:146 +msgid "" +"```rust\n" +"#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n" +"pub struct Point {\n" +" x: i32,\n" +" y: i32,\n" +"}\n" +"\n" +"impl Point {\n" +" pub fn new(x: i32, y: i32) -> Point {\n" +" Point { x, y }\n" +" }\n" +"\n" +" pub fn magnitude(self) -> f64 {\n" +" f64::from(self.x.pow(2) + self.y.pow(2)).sqrt()\n" +" }\n" +"\n" +" pub fn dist(self, other: Point) -> f64 {\n" +" (self - other).magnitude()\n" +" }\n" +"}\n" +"\n" +"impl std::ops::Add for Point {\n" +" type Output = Self;\n" +"\n" +" fn add(self, other: Self) -> Self::Output {\n" +" Self {\n" +" x: self.x + other.x,\n" +" y: self.y + other.y,\n" +" }\n" +" }\n" +"}\n" +"\n" +"impl std::ops::Sub for Point {\n" +" type Output = Self;\n" +"\n" +" fn sub(self, other: Self) -> Self::Output {\n" +" Self {\n" +" x: self.x - other.x,\n" +" y: self.y - other.y,\n" +" }\n" +" }\n" +"}\n" +"\n" +"pub struct Polygon {\n" +" points: Vec,\n" +"}\n" +"\n" +"impl Polygon {\n" +" pub fn new() -> Polygon {\n" +" Polygon { points: Vec::new() }\n" +" }\n" +"\n" +" pub fn add_point(&mut self, point: Point) {\n" +" self.points.push(point);\n" +" }\n" +"\n" +" pub fn left_most_point(&self) -> Option {\n" +" self.points.iter().min_by_key(|p| p.x).copied()\n" +" }\n" +"\n" +" pub fn iter(&self) -> impl Iterator {\n" +" self.points.iter()\n" +" }\n" +"\n" +" pub fn length(&self) -> f64 {\n" +" if self.points.is_empty() {\n" +" return 0.0;\n" +" }\n" +"\n" +" let mut result = 0.0;\n" +" let mut last_point = self.points[0];\n" +" for point in &self.points[1..] {\n" +" result += last_point.dist(*point);\n" +" last_point = *point;\n" +" }\n" +" result += last_point.dist(self.points[0]);\n" +" result\n" +" // Alternatively, Iterator::zip() lets us iterate over the points as " +"pairs\n" +" // but we need to pair each point with the next one, and the last " +"point\n" +" // with the first point. The zip() iterator is finished as soon as " +"one of \n" +" // the source iterators is finished, a neat trick is to combine " +"Iterator::cycle\n" +" // with Iterator::skip to create the second iterator for the zip and " +"using map \n" +" // and sum to calculate the total length.\n" +" }\n" +"}\n" +"\n" +"pub struct Circle {\n" +" center: Point,\n" +" radius: i32,\n" +"}\n" +"\n" +"impl Circle {\n" +" pub fn new(center: Point, radius: i32) -> Circle {\n" +" Circle { center, radius }\n" +" }\n" +"\n" +" pub fn circumference(&self) -> f64 {\n" +" 2.0 * std::f64::consts::PI * f64::from(self.radius)\n" +" }\n" +"\n" +" pub fn dist(&self, other: &Self) -> f64 {\n" +" self.center.dist(other.center)\n" +" }\n" +"}\n" +"\n" +"pub enum Shape {\n" +" Polygon(Polygon),\n" +" Circle(Circle),\n" +"}\n" +"\n" +"impl From for Shape {\n" +" fn from(poly: Polygon) -> Self {\n" +" Shape::Polygon(poly)\n" +" }\n" +"}\n" +"\n" +"impl From for Shape {\n" +" fn from(circle: Circle) -> Self {\n" +" Shape::Circle(circle)\n" " }\n" +"}\n" "\n" -" 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" +"#[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" "\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 "" @@ -19759,21 +18866,6 @@ msgstr "" #: src/exercises/day-3/solutions-afternoon.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: ffi\n" "mod ffi {\n" " use std::os::raw::{c_char, c_int};\n" " #[cfg(not(target_os = \"macos\"))]\n" @@ -19843,14 +18935,11 @@ msgid "" " path: CString,\n" " dir: *mut ffi::DIR,\n" "}\n" -"// ANCHOR_END: ffi\n" "\n" -"// ANCHOR: DirectoryIterator\n" "impl DirectoryIterator {\n" " fn new(path: &str) -> Result {\n" " // Call opendir and return a Ok value if that worked,\n" " // otherwise return Err with a message.\n" -" // ANCHOR_END: DirectoryIterator\n" " let path = CString::new(path).map_err(|err| format!(\"Invalid path: " "{err}\"))?;\n" " // SAFETY: path.as_ptr() cannot be NULL.\n" @@ -19863,12 +18952,10 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Iterator\n" "impl Iterator for DirectoryIterator {\n" " type Item = OsString;\n" " fn next(&mut self) -> Option {\n" " // Keep calling readdir until we get a NULL pointer back.\n" -" // ANCHOR_END: Iterator\n" " // SAFETY: self.dir is never NULL.\n" " let dirent = unsafe { ffi::readdir(self.dir) };\n" " if dirent.is_null() {\n" @@ -19883,11 +18970,9 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: Drop\n" "impl Drop for DirectoryIterator {\n" " fn drop(&mut self) {\n" " // Call closedir as needed.\n" -" // ANCHOR_END: Drop\n" " if !self.dir.is_null() {\n" " // SAFETY: self.dir is not NULL.\n" " if unsafe { ffi::closedir(self.dir) } != 0 {\n" @@ -19897,13 +18982,11 @@ msgid "" " }\n" "}\n" "\n" -"// ANCHOR: main\n" "fn main() -> Result<(), String> {\n" " let iter = DirectoryIterator::new(\".\")?;\n" " println!(\"files: {:#?}\", iter.collect::>());\n" " Ok(())\n" "}\n" -"// ANCHOR_END: main\n" "\n" "#[cfg(test)]\n" "mod tests {\n" @@ -19959,21 +19042,6 @@ msgstr "" #: src/exercises/bare-metal/solutions-morning.md:7 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: top\n" "#![no_main]\n" "#![no_std]\n" "\n" @@ -19981,7 +19049,6 @@ msgid "" "\n" "use core::fmt::Write;\n" "use cortex_m_rt::entry;\n" -"// ANCHOR_END: top\n" "use core::cmp::{max, min};\n" "use lsm303agr::{AccelOutputDataRate, Lsm303agr, MagOutputDataRate};\n" "use microbit::display::blocking::Display;\n" @@ -19995,7 +19062,6 @@ msgid "" "const COMPASS_SCALE: i32 = 30000;\n" "const ACCELEROMETER_SCALE: i32 = 700;\n" "\n" -"// ANCHOR: main\n" "#[entry]\n" "fn main() -> ! {\n" " let board = Board::take().unwrap();\n" @@ -20009,7 +19075,6 @@ msgid "" " );\n" "\n" " // Set up the I2C controller and Inertial Measurement Unit.\n" -" // ANCHOR_END: main\n" " writeln!(serial, \"Setting up IMU...\").unwrap();\n" " let i2c = Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::" "K100);\n" @@ -20026,12 +19091,10 @@ msgid "" " let mut mode = Mode::Compass;\n" " let mut button_pressed = false;\n" "\n" -" // ANCHOR: loop\n" " writeln!(serial, \"Ready.\").unwrap();\n" "\n" " loop {\n" " // Read compass data and log it to the serial port.\n" -" // ANCHOR_END: loop\n" " while !(imu.mag_status().unwrap().xyz_new_data\n" " && imu.accel_status().unwrap().xyz_new_data)\n" " {}\n" @@ -20128,34 +19191,18 @@ msgid "([back to exercise](rtc.md))" msgstr "" #: src/exercises/bare-metal/solutions-afternoon.md:7 -msgid "`main.rs`:" +msgid "_main.rs_:" msgstr "" #: src/exercises/bare-metal/solutions-afternoon.md:9 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: top\n" "#![no_main]\n" "#![no_std]\n" "\n" "mod exceptions;\n" "mod logger;\n" "mod pl011;\n" -"// ANCHOR_END: top\n" "mod pl031;\n" "\n" "use crate::pl031::Rtc;\n" @@ -20163,7 +19210,6 @@ msgid "" "use arm_gic::{irq_enable, wfi};\n" "use chrono::{TimeZone, Utc};\n" "use core::hint::spin_loop;\n" -"// ANCHOR: imports\n" "use crate::pl011::Uart;\n" "use arm_gic::gicv3::GicV3;\n" "use core::panic::PanicInfo;\n" @@ -20177,14 +19223,12 @@ msgid "" "\n" "/// Base address of the primary PL011 UART.\n" "const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;\n" -"// ANCHOR_END: imports\n" "\n" "/// Base address of the PL031 RTC.\n" "const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;\n" "/// The IRQ used by the PL031 RTC.\n" "const PL031_IRQ: IntId = IntId::spi(2);\n" "\n" -"// ANCHOR: main\n" "#[no_mangle]\n" "extern \"C\" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {\n" " // Safe because `PL011_BASE_ADDRESS` is the base address of a PL011 " @@ -20202,7 +19246,6 @@ msgid "" " let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, " "GICR_BASE_ADDRESS) };\n" " gic.setup();\n" -" // ANCHOR_END: main\n" "\n" " // Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 " "device,\n" @@ -20264,7 +19307,6 @@ msgid "" " );\n" " info!(\"Finished waiting\");\n" "\n" -" // ANCHOR: main_end\n" " system_off::().unwrap();\n" "}\n" "\n" @@ -20274,31 +19316,16 @@ msgid "" " system_off::().unwrap();\n" " loop {}\n" "}\n" -"// ANCHOR_END: main_end\n" "```" msgstr "" -#: src/exercises/bare-metal/solutions-afternoon.md:149 -msgid "`pl031.rs`:" +#: src/exercises/bare-metal/solutions-afternoon.md:127 +msgid "_pl031.rs_:" msgstr "" -#: src/exercises/bare-metal/solutions-afternoon.md:151 +#: src/exercises/bare-metal/solutions-afternoon.md:129 msgid "" "```rust\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" "use core::ptr::{addr_of, addr_of_mut};\n" "\n" "#[repr(C, align(4))]\n" @@ -20379,8 +19406,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" @@ -20427,21 +19454,6 @@ msgstr "" #: src/exercises/concurrency/solutions-morning.md:7 msgid "" "```rust\n" -"// Copyright 2022 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: Philosopher\n" "use std::sync::{mpsc, Arc, Mutex};\n" "use std::thread;\n" "use std::time::Duration;\n" @@ -20450,39 +19462,32 @@ msgid "" "\n" "struct Philosopher {\n" " name: String,\n" -" // ANCHOR_END: Philosopher\n" " left_fork: Arc>,\n" " right_fork: Arc>,\n" " thoughts: mpsc::SyncSender,\n" "}\n" "\n" -"// ANCHOR: Philosopher-think\n" "impl Philosopher {\n" " fn think(&self) {\n" " self.thoughts\n" " .send(format!(\"Eureka! {} has a new idea!\", &self.name))\n" " .unwrap();\n" " }\n" -" // ANCHOR_END: Philosopher-think\n" "\n" -" // ANCHOR: Philosopher-eat\n" " fn eat(&self) {\n" -" // ANCHOR_END: Philosopher-eat\n" " println!(\"{} is trying to eat\", &self.name);\n" " let left = self.left_fork.lock().unwrap();\n" " let right = self.right_fork.lock().unwrap();\n" "\n" -" // ANCHOR: Philosopher-eat-end\n" " println!(\"{} is eating...\", &self.name);\n" " thread::sleep(Duration::from_millis(10));\n" " }\n" "}\n" "\n" "static PHILOSOPHERS: &[&str] =\n" -" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n" +" &[\"Socrates\", \"Hypatia\", \"Plato\", \"Aristotle\", \"Pythagoras\"];\n" "\n" "fn main() {\n" -" // ANCHOR_END: Philosopher-eat-end\n" " let (tx, rx) = mpsc::sync_channel(10);\n" "\n" " let forks = (0..PHILOSOPHERS.len())\n" @@ -20524,34 +19529,19 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/solutions-morning.md:104 +#: src/exercises/concurrency/solutions-morning.md:82 msgid "Link Checker" msgstr "" -#: src/exercises/concurrency/solutions-morning.md:106 +#: src/exercises/concurrency/solutions-morning.md:84 msgid "([back to exercise](link-checker.md))" msgstr "" -#: src/exercises/concurrency/solutions-morning.md:108 +#: src/exercises/concurrency/solutions-morning.md:86 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" @@ -20563,9 +19553,7 @@ msgid "" " #[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" @@ -20606,7 +19594,6 @@ msgid "" " }\n" " Ok(link_urls)\n" "}\n" -"// ANCHOR_END: visit_page\n" "\n" "struct CrawlState {\n" " domain: String,\n" @@ -20631,7 +19618,7 @@ msgid "" " url_domain == self.domain\n" " }\n" "\n" -" /// Mark the given page as visited, returning true if it had already\n" +" /// Mark the given page as visited, returning false 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" @@ -20738,21 +19725,6 @@ msgstr "" #: src/exercises/concurrency/solutions-afternoon.md:7 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: Philosopher\n" "use std::sync::Arc;\n" "use tokio::time;\n" "use tokio::sync::mpsc::{self, Sender};\n" @@ -20762,13 +19734,11 @@ msgid "" "\n" "struct Philosopher {\n" " name: String,\n" -" // ANCHOR_END: Philosopher\n" " left_fork: Arc>,\n" " right_fork: Arc>,\n" " thoughts: Sender,\n" "}\n" "\n" -"// ANCHOR: Philosopher-think\n" "impl Philosopher {\n" " async fn think(&self) {\n" " self.thoughts\n" @@ -20776,12 +19746,9 @@ msgid "" "await\n" " .unwrap();\n" " }\n" -" // ANCHOR_END: Philosopher-think\n" "\n" -" // ANCHOR: Philosopher-eat\n" " async fn eat(&self) {\n" " // Pick up forks...\n" -" // ANCHOR_END: Philosopher-eat\n" " let _first_lock = self.left_fork.lock().await;\n" " // Add a delay before picking the second fork to allow the " "execution\n" @@ -20789,22 +19756,18 @@ msgid "" " time::sleep(time::Duration::from_millis(1)).await;\n" " let _second_lock = self.right_fork.lock().await;\n" "\n" -" // ANCHOR: Philosopher-eat-body\n" " println!(\"{} is eating...\", &self.name);\n" " time::sleep(time::Duration::from_millis(5)).await;\n" -" // ANCHOR_END: Philosopher-eat-body\n" "\n" " // The locks are dropped here\n" -" // ANCHOR: Philosopher-eat-end\n" " }\n" "}\n" "\n" "static PHILOSOPHERS: &[&str] =\n" -" &[\"Socrates\", \"Plato\", \"Aristotle\", \"Thales\", \"Pythagoras\"];\n" +" &[\"Socrates\", \"Hypatia\", \"Plato\", \"Aristotle\", \"Pythagoras\"];\n" "\n" "#[tokio::main]\n" "async fn main() {\n" -" // ANCHOR_END: Philosopher-eat-end\n" " // Create forks\n" " let mut forks = vec![];\n" " (0..PHILOSOPHERS.len()).for_each(|_| forks.push(Arc::new(Mutex::" @@ -20854,43 +19817,26 @@ msgid "" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:121 +#: src/exercises/concurrency/solutions-afternoon.md:97 msgid "([back to exercise](chat-app.md))" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:125 +#: src/exercises/concurrency/solutions-afternoon.md:101 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" "use futures_util::sink::SinkExt;\n" +"use futures_util::stream::StreamExt;\n" "use std::error::Error;\n" "use std::net::SocketAddr;\n" "use tokio::net::{TcpListener, TcpStream};\n" "use tokio::sync::broadcast::{channel, Sender};\n" "use tokio_websockets::{Message, ServerBuilder, WebsocketStream};\n" -"// ANCHOR_END: setup\n" "\n" -"// ANCHOR: handle_connection\n" "async fn handle_connection(\n" " addr: SocketAddr,\n" " mut ws_stream: WebsocketStream,\n" " bcast_tx: Sender,\n" ") -> Result<(), Box> {\n" -" // ANCHOR_END: handle_connection\n" "\n" " ws_stream\n" " .send(Message::text(\"Welcome to chat! Type a message\".into()))\n" @@ -20906,9 +19852,10 @@ msgid "" " incoming = ws_stream.next() => {\n" " match incoming {\n" " Some(Ok(msg)) => {\n" -" let msg = msg.as_text()?;\n" -" println!(\"From client {addr:?} {msg:?}\");\n" -" bcast_tx.send(msg.into())?;\n" +" if let Some(text) = msg.as_text() {\n" +" println!(\"From client {addr:?} {text:?}\");\n" +" bcast_tx.send(text.into())?;\n" +" }\n" " }\n" " Some(Err(err)) => return Err(err.into()),\n" " None => return Ok(()),\n" @@ -20919,7 +19866,6 @@ msgid "" " }\n" " }\n" " }\n" -" // ANCHOR: main\n" "}\n" "\n" "#[tokio::main]\n" @@ -20941,28 +19887,13 @@ msgid "" " });\n" " }\n" "}\n" -"// ANCHOR_END: main\n" "```" msgstr "" -#: src/exercises/concurrency/solutions-afternoon.md:210 +#: src/exercises/concurrency/solutions-afternoon.md:168 msgid "" "```rust,compile_fail\n" -"// Copyright 2023 Google LLC\n" -"//\n" -"// Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"// you may not use this file except in compliance with the License.\n" -"// You may obtain a copy of the License at\n" -"//\n" -"// http://www.apache.org/licenses/LICENSE-2.0\n" -"//\n" -"// Unless required by applicable law or agreed to in writing, software\n" -"// distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"// See the License for the specific language governing permissions and\n" -"// limitations under the License.\n" -"\n" -"// ANCHOR: setup\n" +"use futures_util::stream::StreamExt;\n" "use futures_util::SinkExt;\n" "use http::Uri;\n" "use tokio::io::{AsyncBufReadExt, BufReader};\n" @@ -20970,22 +19901,24 @@ msgid "" "\n" "#[tokio::main]\n" "async fn main() -> Result<(), tokio_websockets::Error> {\n" -" let mut ws_stream = ClientBuilder::from_uri(Uri::" -"from_static(\"ws://127.0.0.1:2000\"))\n" -" .connect()\n" -" .await?;\n" +" let (mut ws_stream, _) =\n" +" ClientBuilder::from_uri(Uri::from_static(\"ws://127.0.0.1:2000\"))\n" +" .connect()\n" +" .await?;\n" "\n" " let stdin = tokio::io::stdin();\n" " let mut stdin = BufReader::new(stdin).lines();\n" "\n" -" // ANCHOR_END: setup\n" " // Continuous loop for concurrently sending and receiving messages.\n" " loop {\n" " tokio::select! {\n" " incoming = ws_stream.next() => {\n" " match incoming {\n" -" Some(Ok(msg)) => println!(\"From server: {}\", msg." -"as_text()?),\n" +" Some(Ok(msg)) => {\n" +" if let Some(text) = msg.as_text() {\n" +" println!(\"From server: {}\", text);\n" +" }\n" +" },\n" " Some(Err(err)) => return Err(err.into()),\n" " None => return Ok(()),\n" " }\n"