From bd08e7e34dd7f173b23624b818c294aa9532edd6 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sun, 6 Apr 2025 14:11:13 +0900 Subject: [PATCH 01/11] Upgrade src-en MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed commit of the following: commit fc6809983337d26b5c41eee3cf77d758357bb193 Author: Hoto Ras Date: Sat Mar 29 03:13:34 2025 +0000 Upgrade src-en Target: rust-lang/rust-by-example@d5f826a3d6ef3e06e7868039640c4f49047b9a12 Version: likely 2024 commit 8e0ba0bfba96a28cf7937a74fe1bfbd01582ec17 Author: Hoto Ras Date: Sat Mar 29 11:21:30 2025 +0900 Update gh-pages.yml enable to call build manually commit 34f652da92874e6965d62b596c1bc390b28542fb Author: HotoRas Date: Fri Aug 23 19:05:52 2024 +0900 Revert "Update gh-pages.yml" This reverts commit b5094132484efef39647789209c8d825f7ccc3c7. commit b5094132484efef39647789209c8d825f7ccc3c7 Author: Hoto Ras Date: Fri Aug 23 18:58:06 2024 +0900 Update gh-pages.yml commit 48a146fd5b5caaa8907bd4e4821df15ce6066795 Author: HotoRas Date: Fri Aug 23 18:42:14 2024 +0900 Update original files 여기 있는 파일들이 약 3년 전에 업데이트가 끊겼더라고요 러스트 공부도 할 겸 새로 싹 다 번역해 보겠습니당 --- .github/workflows/gh-pages.yml | 1 + src-en/SUMMARY.md | 9 +- src-en/attribute.md | 36 +- src-en/attribute/cfg.md | 2 + src-en/cargo/conventions.md | 6 +- src-en/cargo/deps.md | 25 +- src-en/cargo/test.md | 102 +++- src-en/compatibility.md | 2 +- src-en/conversion/from_into.md | 43 +- src-en/conversion/string.md | 45 +- src-en/crates/lib.md | 4 +- src-en/crates/using_lib.md | 4 +- src-en/custom_types.md | 2 +- src-en/custom_types/constants.md | 2 +- src-en/custom_types/enum.md | 6 +- src-en/custom_types/enum/enum_use.md | 40 +- .../custom_types/enum/testcase_linked_list.md | 5 +- src-en/custom_types/structs.md | 23 +- src-en/error/abort_unwind.md | 58 +++ src-en/error/iter_result.md | 24 +- .../multiple_error_types/boxing_errors.md | 2 +- .../multiple_error_types/define_error_type.md | 8 +- .../multiple_error_types/option_result.md | 5 +- .../reenter_question_mark.md | 4 +- .../error/multiple_error_types/wrap_error.md | 5 +- src-en/error/option_unwrap.md | 48 +- src-en/error/option_unwrap/and_then.md | 28 +- src-en/error/option_unwrap/defaults.md | 124 +++++ src-en/error/option_unwrap/map.md | 12 +- src-en/error/option_unwrap/question_mark.md | 7 +- src-en/error/panic.md | 5 +- src-en/error/result.md | 1 - src-en/error/result/enter_question_mark.md | 3 +- src-en/error/result/result_map.md | 2 +- src-en/expression.md | 4 +- src-en/flow_control.md | 2 +- src-en/flow_control/for.md | 12 +- src-en/flow_control/if_let.md | 6 +- src-en/flow_control/let_else.md | 62 +++ src-en/flow_control/loop.md | 2 +- src-en/flow_control/loop/nested.md | 4 +- src-en/flow_control/match.md | 5 +- src-en/flow_control/match/binding.md | 1 + src-en/flow_control/match/destructuring.md | 3 +- .../destructuring/destructure_pointers.md | 6 +- .../match/destructuring/destructure_slice.md | 48 ++ .../destructuring/destructure_structures.md | 15 + .../match/destructuring/destructure_tuple.md | 4 +- src-en/flow_control/match/guard.md | 43 +- src-en/flow_control/while_let.md | 2 +- src-en/fn.md | 2 +- src-en/fn/closures.md | 38 +- src-en/fn/closures/capture.md | 2 +- src-en/fn/closures/closure_examples.md | 2 +- .../fn/closures/closure_examples/iter_any.md | 16 +- .../fn/closures/closure_examples/iter_find.md | 21 +- src-en/fn/closures/input_parameters.md | 11 +- src-en/fn/diverging.md | 12 +- src-en/fn/hof.md | 4 +- src-en/fn/methods.md | 29 +- src-en/generics.md | 12 +- src-en/generics/assoc_items.md | 4 +- src-en/generics/assoc_items/the_problem.md | 10 +- src-en/generics/assoc_items/types.md | 4 +- src-en/generics/bounds.md | 2 +- src-en/generics/gen_fn.md | 6 +- src-en/generics/impl.md | 1 - src-en/generics/multi_bounds.md | 2 +- src-en/generics/new_types.md | 18 +- src-en/generics/phantom.md | 18 +- src-en/generics/phantom/testcase_units.md | 2 +- src-en/generics/where.md | 6 +- src-en/hello.md | 14 +- src-en/hello/comment.md | 30 +- src-en/hello/print.md | 91 ++-- src-en/hello/print/fmt.md | 27 +- src-en/hello/print/print_debug.md | 7 +- src-en/hello/print/print_display.md | 15 +- .../print/print_display/testcase_list.md | 3 +- src-en/index.md | 17 +- src-en/macros.md | 8 +- src-en/macros/dsl.md | 6 +- src-en/macros/overload.md | 4 +- src-en/macros/repeat.md | 6 +- src-en/macros/variadics.md | 4 +- src-en/meta.md | 6 +- src-en/meta/doc.md | 24 +- src-en/meta/playground.md | 60 +++ src-en/meta/playpen.md | 46 -- src-en/mod.md | 2 +- src-en/mod/split.md | 14 +- src-en/mod/struct_visibility.md | 9 +- src-en/primitives.md | 47 +- src-en/primitives/array.md | 69 ++- src-en/primitives/literals.md | 11 +- src-en/primitives/tuples.md | 99 ++-- src-en/scope.md | 4 +- src-en/scope/borrow.md | 8 +- src-en/scope/borrow/mut.md | 5 +- src-en/scope/borrow/ref.md | 4 +- src-en/scope/lifetime.md | 2 +- src-en/scope/lifetime/explicit.md | 16 +- src-en/scope/lifetime/fn.md | 7 +- src-en/scope/lifetime/lifetime_bounds.md | 6 +- src-en/scope/lifetime/lifetime_coercion.md | 4 +- src-en/scope/lifetime/static_lifetime.md | 39 +- src-en/scope/lifetime/struct.md | 1 - src-en/scope/lifetime/trait.md | 7 +- src-en/scope/move.md | 8 +- src-en/scope/move/mut.md | 2 +- src-en/scope/move/partial_move.md | 40 +- src-en/scope/raii.md | 3 + src-en/std/arc.md | 38 +- src-en/std/box.md | 4 +- src-en/std/hash.md | 18 +- src-en/std/hash/alt_key_types.md | 18 +- src-en/std/hash/hashset.md | 16 +- src-en/std/panic.md | 3 + src-en/std/rc.md | 12 +- src-en/std/result.md | 2 +- src-en/std/result/question_mark.md | 6 +- src-en/std/str.md | 6 +- src-en/std/vec.md | 9 +- src-en/std_misc/arg.md | 7 +- src-en/std_misc/arg/matching.md | 3 + src-en/std_misc/ffi.md | 10 + src-en/std_misc/file.md | 2 +- src-en/std_misc/file/create.md | 5 +- src-en/std_misc/file/open.md | 2 +- src-en/std_misc/file/read_lines.md | 71 ++- src-en/std_misc/fs.md | 12 +- src-en/std_misc/path.md | 25 +- src-en/std_misc/process/pipe.md | 13 +- src-en/std_misc/threads/testcase_mapreduce.md | 32 +- src-en/testing/dev_dependencies.md | 11 +- src-en/testing/doc_testing.md | 20 +- src-en/testing/integration_testing.md | 11 +- src-en/testing/unit_testing.md | 3 +- src-en/trait.md | 10 +- src-en/trait/clone.md | 4 +- src-en/trait/derive.md | 4 +- src-en/trait/disambiguating.md | 12 +- src-en/trait/drop.md | 56 +- src-en/trait/dyn.md | 16 +- src-en/trait/impl_trait.md | 56 ++ src-en/trait/iter.md | 25 +- src-en/types.md | 1 + src-en/types/alias.md | 18 +- src-en/types/cast.md | 40 +- src-en/types/inference.md | 2 +- src-en/types/literals.md | 2 +- src-en/unsafe.md | 9 +- src-en/unsafe/asm.md | 489 ++++++++++++++++++ src-en/variable_bindings.md | 3 +- src-en/variable_bindings/declare.md | 11 +- src-en/variable_bindings/freeze.md | 2 +- src-en/variable_bindings/mut.md | 3 +- src-en/variable_bindings/scope.md | 6 +- 158 files changed, 2227 insertions(+), 810 deletions(-) create mode 100644 src-en/error/abort_unwind.md create mode 100644 src-en/error/option_unwrap/defaults.md create mode 100644 src-en/flow_control/let_else.md create mode 100644 src-en/flow_control/match/destructuring/destructure_slice.md create mode 100644 src-en/meta/playground.md delete mode 100644 src-en/meta/playpen.md create mode 100644 src-en/unsafe/asm.md diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 77162d7..ff35582 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -4,6 +4,7 @@ on: push: branches: - main + workflow_dispatch: jobs: deploy: diff --git a/src-en/SUMMARY.md b/src-en/SUMMARY.md index 216bcea..b8e6ada 100644 --- a/src-en/SUMMARY.md +++ b/src-en/SUMMARY.md @@ -52,12 +52,14 @@ - [match](flow_control/match.md) - [Destructuring](flow_control/match/destructuring.md) - [tuples](flow_control/match/destructuring/destructure_tuple.md) + - [arrays/slices](flow_control/match/destructuring/destructure_slice.md) - [enums](flow_control/match/destructuring/destructure_enum.md) - [pointers/ref](flow_control/match/destructuring/destructure_pointers.md) - [structs](flow_control/match/destructuring/destructure_structures.md) - [Guards](flow_control/match/guard.md) - [Binding](flow_control/match/binding.md) - [if let](flow_control/if_let.md) + - [let-else](flow_control/let_else.md) - [while let](flow_control/while_let.md) - [Functions](fn.md) @@ -154,10 +156,12 @@ - [Error handling](error.md) - [`panic`](error/panic.md) + - [`abort` & `unwind`](error/abort_unwind.md) - [`Option` & `unwrap`](error/option_unwrap.md) - [Unpacking options with `?`](error/option_unwrap/question_mark.md) - [Combinators: `map`](error/option_unwrap/map.md) - [Combinators: `and_then`](error/option_unwrap/and_then.md) + - [Defaults: `or`, `or_else`, `get_or_insert`, `get_or_insert_with`](error/option_unwrap/defaults.md) - [`Result`](error/result.md) - [`map` for `Result`](error/result/result_map.md) - [aliases for `Result`](error/result/result_alias.md) @@ -193,7 +197,7 @@ - [File I/O](std_misc/file.md) - [`open`](std_misc/file/open.md) - [`create`](std_misc/file/create.md) - - [`read lines`](std_misc/file/read_lines.md) + - [`read_lines`](std_misc/file/read_lines.md) - [Child processes](std_misc/process.md) - [Pipes](std_misc/process/pipe.md) - [Wait](std_misc/process/wait.md) @@ -209,10 +213,11 @@ - [Dev-dependencies](testing/dev_dependencies.md) - [Unsafe Operations](unsafe.md) + - [Inline assembly](unsafe/asm.md) - [Compatibility](compatibility.md) - [Raw identifiers](compatibility/raw_identifiers.md) - [Meta](meta.md) - [Documentation](meta/doc.md) - - [Playpen](meta/playpen.md) + - [Playground](meta/playground.md) diff --git a/src-en/attribute.md b/src-en/attribute.md index 13b0a60..195b6e1 100644 --- a/src-en/attribute.md +++ b/src-en/attribute.md @@ -12,10 +12,38 @@ can be used to/for: * link to a foreign library * mark functions as unit tests * mark functions that will be part of a benchmark +* [attribute like macros][macros] -When attributes apply to a whole crate, their syntax is `#![crate_attribute]`, -and when they apply to a module or item, the syntax is `#[item_attribute]` -(notice the missing bang `!`). +Attributes look like `#[outer_attribute]` or `#![inner_attribute]`, +with the difference between them being where they apply. + +* `#[outer_attribute]` applies to the [item][item] immediately + following it. Some examples of items are: a function, a module + declaration, a constant, a structure, an enum. Here is an example + where attribute `#[derive(Debug)]` applies to the struct + `Rectangle`: + + ```rust + #[derive(Debug)] + struct Rectangle { + width: u32, + height: u32, + } + ``` + +* `#![inner_attribute]` applies to the enclosing [item][item] (typically a + module or a crate). In other words, this attribute is interpreted as + applying to the entire scope in which it's placed. Here is an example + where `#![allow(unused_variables)]` applies to the whole crate (if + placed in `main.rs`): + + ```rust + #![allow(unused_variables)] + + fn main() { + let x = 3; // This would normally warn about an unused variable. + } + ``` Attributes can take arguments with different syntaxes: @@ -35,4 +63,6 @@ Attributes can have multiple values and can be separated over multiple lines, to [cfg]: attribute/cfg.md [crate]: attribute/crate.md +[item]: https://doc.rust-lang.org/stable/reference/items.html [lint]: https://en.wikipedia.org/wiki/Lint_%28software%29 +[macros]: https://doc.rust-lang.org/book/ch19-06-macros.html#attribute-like-macros diff --git a/src-en/attribute/cfg.md b/src-en/attribute/cfg.md index d0a4a10..e4aeb1d 100644 --- a/src-en/attribute/cfg.md +++ b/src-en/attribute/cfg.md @@ -9,6 +9,8 @@ While the former enables conditional compilation, the latter conditionally evaluates to `true` or `false` literals allowing for checks at run-time. Both utilize identical argument syntax. +`cfg!`, unlike `#[cfg]`, does not remove any code and only evaluates to true or false. For example, all blocks in an if/else expression need to be valid when `cfg!` is used for the condition, regardless of what `cfg!` is evaluating. + ```rust,editable // This function only gets compiled if the target OS is linux #[cfg(target_os = "linux")] diff --git a/src-en/cargo/conventions.md b/src-en/cargo/conventions.md index 6e81966..2335104 100644 --- a/src-en/cargo/conventions.md +++ b/src-en/cargo/conventions.md @@ -25,9 +25,9 @@ foo └── my_other_bin.rs ``` -To tell `cargo` to compile or run this binary as opposed to the default or other -binaries, we just pass `cargo` the `--bin my_other_bin` flag, where `my_other_bin` -is the name of the binary we want to work with. +To tell `cargo` to only compile or run this binary, we just pass `cargo` the +`--bin my_other_bin` flag, where `my_other_bin` is the name of the binary we +want to work with. In addition to extra binaries, `cargo` supports [more features] such as benchmarks, tests, and examples. diff --git a/src-en/cargo/deps.md b/src-en/cargo/deps.md index 7403a2a..0a1c498 100644 --- a/src-en/cargo/deps.md +++ b/src-en/cargo/deps.md @@ -11,8 +11,8 @@ To create a new Rust project, # A binary cargo new foo -# OR A library -cargo new --lib foo +# A library +cargo new --lib bar ``` For the rest of this chapter, let's assume we are making a binary, rather than @@ -21,14 +21,19 @@ a library, but all of the concepts are the same. After the above commands, you should see a file hierarchy like this: ```txt -foo -├── Cargo.toml -└── src - └── main.rs +. +├── bar +│ ├── Cargo.toml +│ └── src +│ └── lib.rs +└── foo + ├── Cargo.toml + └── src + └── main.rs ``` -The `main.rs` is the root source file for your new project -- nothing new there. -The `Cargo.toml` is the config file for `cargo` for this project (`foo`). If you +The `main.rs` is the root source file for your new `foo` project -- nothing new there. +The `Cargo.toml` is the config file for `cargo` for this project. If you look inside it, you should see something like this: ```toml @@ -56,8 +61,7 @@ lots of great packages on [crates.io](https://crates.io) (the official Rust package registry). One popular choice is [clap](https://crates.io/crates/clap). As of this writing, the most recent published version of `clap` is `2.27.1`. To add a dependency to our program, we can simply add the following to our -`Cargo.toml` under `[dependencies]`: `clap = "2.27.1"`. And of course, `extern -crate clap` in `main.rs`, just like normal. And that's it! You can start using +`Cargo.toml` under `[dependencies]`: `clap = "2.27.1"`. And that's it! You can start using `clap` in your program. `cargo` also supports [other types of dependencies][dependencies]. Here is just @@ -87,6 +91,5 @@ rebuilds what it has not already built, similar to `make`). Voila! That's all there is to it! - [manifest]: https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html diff --git a/src-en/cargo/test.md b/src-en/cargo/test.md index b4bc69d..5f53acf 100644 --- a/src-en/cargo/test.md +++ b/src-en/cargo/test.md @@ -2,8 +2,7 @@ As we know testing is integral to any piece of software! Rust has first-class support for unit and integration testing ([see this -chapter](https://doc.rust-lang.org/book/ch11-00-testing.html) in -TRPL). +chapter](https://doc.rust-lang.org/book/ch11-00-testing.html) in TRPL). From the testing chapters linked above, we see how to write unit tests and integration tests. Organizationally, we can place unit tests in the modules they @@ -14,12 +13,19 @@ foo ├── Cargo.toml ├── src │ └── main.rs +│ └── lib.rs └── tests ├── my_test.rs └── my_other_test.rs ``` -Each file in `tests` is a separate integration test. +Each file in `tests` is a separate +[integration test](https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests), +i.e. a test that is meant to test your library as if it were being called from a dependent +crate. + +The [Testing][testing] chapter elaborates on the three different testing styles: +[Unit][unit_testing], [Doc][doc_testing], and [Integration][integration_testing]. `cargo` naturally provides an easy way to run all of your tests! @@ -35,13 +41,13 @@ $ cargo test Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs Running target/debug/deps/blah-d3b32b97275ec472 -running 3 tests +running 4 tests test test_bar ... ok test test_baz ... ok test test_foo_bar ... ok test test_foo ... ok -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` You can also run tests whose name matches a pattern: @@ -64,5 +70,87 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out ``` One word of caution: Cargo may run multiple tests concurrently, so make sure -that they don't race with each other. For example, if they all output to a -file, you should make them write to different files. +that they don't race with each other. + +One example of this concurrency causing issues is if two tests output to a +file, such as below: + +```rust +#[cfg(test)] +mod tests { + // Import the necessary modules + use std::fs::OpenOptions; + use std::io::Write; + + // This test writes to a file + #[test] + fn test_file() { + // Opens the file ferris.txt or creates one if it doesn't exist. + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open("ferris.txt") + .expect("Failed to open ferris.txt"); + + // Print "Ferris" 5 times. + for _ in 0..5 { + file.write_all("Ferris\n".as_bytes()) + .expect("Could not write to ferris.txt"); + } + } + + // This test tries to write to the same file + #[test] + fn test_file_also() { + // Opens the file ferris.txt or creates one if it doesn't exist. + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open("ferris.txt") + .expect("Failed to open ferris.txt"); + + // Print "Corro" 5 times. + for _ in 0..5 { + file.write_all("Corro\n".as_bytes()) + .expect("Could not write to ferris.txt"); + } + } +} +``` + +Although the intent is to get the following: + +```shell +$ cat ferris.txt +Ferris +Ferris +Ferris +Ferris +Ferris +Corro +Corro +Corro +Corro +Corro +``` + +What actually gets put into `ferris.txt` is this: + +```shell +$ cargo test test_file && cat ferris.txt +Corro +Ferris +Corro +Ferris +Corro +Ferris +Corro +Ferris +Corro +Ferris +``` + +[testing]: ../testing.md +[unit_testing]: ../testing/unit_testing.md +[integration_testing]: ../testing/integration_testing.md +[doc_testing]: ../testing/doc_testing.md diff --git a/src-en/compatibility.md b/src-en/compatibility.md index 00424ce..a33f88e 100644 --- a/src-en/compatibility.md +++ b/src-en/compatibility.md @@ -1,6 +1,6 @@ # Compatibility -The Rust language is fastly evolving, and because of this certain compatibility +The Rust language is evolving rapidly, and because of this certain compatibility issues can arise, despite efforts to ensure forwards-compatibility wherever possible. diff --git a/src-en/conversion/from_into.md b/src-en/conversion/from_into.md index 266d10f..9ab5da9 100644 --- a/src-en/conversion/from_into.md +++ b/src-en/conversion/from_into.md @@ -18,7 +18,7 @@ let my_str = "hello"; let my_string = String::from(my_str); ``` -We can do similar for defining a conversion for our own type. +We can do something similar for defining a conversion for our own type. ```rust,editable use std::convert::From; @@ -42,13 +42,39 @@ fn main() { ## `Into` -The [`Into`] trait is simply the reciprocal of the `From` trait. That is, if you -have implemented the `From` trait for your type, `Into` will call it when -necessary. +The [`Into`] trait is simply the reciprocal of the `From` trait. It +defines how to convert a type into another type. -Using the `Into` trait will typically require specification of the type to -convert into as the compiler is unable to determine this most of the time. -However this is a small trade-off considering we get the functionality for free. +Calling `into()` typically requires us to specify the result type as the compiler is unable to determine this most of the time. + +```rust,editable +use std::convert::Into; + +#[derive(Debug)] +struct Number { + value: i32, +} + +impl Into for i32 { + fn into(self) -> Number { + Number { value: self } + } +} + +fn main() { + let int = 5; + // Try removing the type annotation + let num: Number = int.into(); + println!("My number is {:?}", num); +} +``` + +## `From` and `Into` are interchangeable + +`From` and `Into` are designed to be complementary. +We do not need to provide an implementation for both traits. +If you have implemented the `From` trait for your type, `Into` will call it +when necessary. Note, however, that the converse is not true: implementing `Into` for your type will not automatically provide it with an implementation of `From`. ```rust,editable use std::convert::From; @@ -58,6 +84,7 @@ struct Number { value: i32, } +// Define `From` impl From for Number { fn from(item: i32) -> Self { Number { value: item } @@ -66,7 +93,7 @@ impl From for Number { fn main() { let int = 5; - // Try removing the type declaration + // use `Into` let num: Number = int.into(); println!("My number is {:?}", num); } diff --git a/src-en/conversion/string.md b/src-en/conversion/string.md index ab14521..7b8726c 100644 --- a/src-en/conversion/string.md +++ b/src-en/conversion/string.md @@ -4,7 +4,7 @@ To convert any type to a `String` is as simple as implementing the [`ToString`] trait for the type. Rather than doing so directly, you should implement the -[`fmt::Display`][Display] trait which automagically provides [`ToString`] and +[`fmt::Display`][Display] trait which automatically provides [`ToString`] and also allows printing the type as discussed in the section on [`print!`][print]. ```rust,editable @@ -28,15 +28,15 @@ fn main() { ## Parsing a String -One of the more common types to convert a string into is a number. The idiomatic -approach to this is to use the [`parse`] function and either to arrange for -type inference or to specify the type to parse using the 'turbofish' syntax. -Both alternatives are shown in the following example. +It's useful to convert strings into many types, but one of the more common string +operations is to convert them from string to number. The idiomatic approach to +this is to use the [`parse`] function and either to arrange for type inference or +to specify the type to parse using the 'turbofish' syntax. Both alternatives are +shown in the following example. -This will convert the string into the type specified so long as the [`FromStr`] +This will convert the string into the type specified as long as the [`FromStr`] trait is implemented for that type. This is implemented for numerous types -within the standard library. To obtain this functionality on a user defined type -simply implement the [`FromStr`] trait for that type. +within the standard library. ```rust,editable fn main() { @@ -48,6 +48,35 @@ fn main() { } ``` +To obtain this functionality on a user defined type simply implement the +[`FromStr`] trait for that type. + +```rust,editable +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug)] +struct Circle { + radius: i32, +} + +impl FromStr for Circle { + type Err = ParseIntError; + fn from_str(s: &str) -> Result { + match s.trim().parse() { + Ok(num) => Ok(Circle{ radius: num }), + Err(e) => Err(e), + } + } +} + +fn main() { + let radius = " 3 "; + let circle: Circle = radius.parse().unwrap(); + println!("{:?}", circle); +} +``` + [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html [print]: ../hello/print.md diff --git a/src-en/crates/lib.md b/src-en/crates/lib.md index 44593f3..b85e435 100644 --- a/src-en/crates/lib.md +++ b/src-en/crates/lib.md @@ -2,6 +2,8 @@ Let's create a library, and then see how to link it to another crate. +In `rary.rs`: + ```rust,ignore pub fn public_function() { println!("called rary's `public_function()`"); @@ -29,4 +31,4 @@ crate file, but this default name can be overridden by passing the `--crate-name` option to `rustc` or by using the [`crate_name` attribute][crate-name]. -[crate-name]: ../attribute/crate.md \ No newline at end of file +[crate-name]: ../attribute/crate.md diff --git a/src-en/crates/using_lib.md b/src-en/crates/using_lib.md index 1020807..3195f17 100644 --- a/src-en/crates/using_lib.md +++ b/src-en/crates/using_lib.md @@ -1,6 +1,6 @@ # Using a Library -To link a crate to this new library you may use `rustc`'s `--extern` flag. All +To link a crate to this new library you may use `rustc`'s `--extern` flag. All of its items will then be imported under a module named the same as the library. This module generally behaves the same way as any other module. @@ -20,7 +20,7 @@ fn main() { ```txt # Where library.rlib is the path to the compiled library, assumed that it's # in the same directory here: -$ rustc executable.rs --extern rary=library.rlib --edition=2018 && ./executable +$ rustc executable.rs --extern rary=library.rlib && ./executable called rary's `public_function()` called rary's `indirect_access()`, that > called rary's `private_function()` diff --git a/src-en/custom_types.md b/src-en/custom_types.md index 20a408b..2a58c4a 100644 --- a/src-en/custom_types.md +++ b/src-en/custom_types.md @@ -5,4 +5,4 @@ Rust custom data types are formed mainly through the two keywords: * `struct`: define a structure * `enum`: define an enumeration -Constants can also be created via the `const` and `static` keywords. \ No newline at end of file +Constants can also be created via the `const` and `static` keywords. diff --git a/src-en/custom_types/constants.md b/src-en/custom_types/constants.md index 8878ba8..c060db7 100644 --- a/src-en/custom_types/constants.md +++ b/src-en/custom_types/constants.md @@ -4,7 +4,7 @@ Rust has two different types of constants which can be declared in any scope including global. Both require explicit type annotation: * `const`: An unchangeable value (the common case). -* `static`: A possibly `mut`able variable with [`'static`][static] lifetime. +* `static`: A possibly mutable variable with [`'static`][static] lifetime. The static lifetime is inferred and does not have to be specified. Accessing or modifying a mutable static variable is [`unsafe`][unsafe]. diff --git a/src-en/custom_types/enum.md b/src-en/custom_types/enum.md index e861df9..b7fb7e4 100644 --- a/src-en/custom_types/enum.md +++ b/src-en/custom_types/enum.md @@ -1,7 +1,7 @@ # Enums The `enum` keyword allows the creation of a type which may be one of a few -different variants. Any variant which is valid as a `struct` is also valid as +different variants. Any variant which is valid as a `struct` is also valid in an `enum`. ```rust,editable @@ -10,7 +10,7 @@ an `enum`. // `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`. // Each is different and independent. enum WebEvent { - // An `enum` may either be `unit-like`, + // An `enum` variant may either be `unit-like`, PageLoad, PageUnload, // like tuple structs, @@ -26,7 +26,7 @@ fn inspect(event: WebEvent) { match event { WebEvent::PageLoad => println!("page loaded"), WebEvent::PageUnload => println!("page unloaded"), - // Destructure `c` from inside the `enum`. + // Destructure `c` from inside the `enum` variant. WebEvent::KeyPress(c) => println!("pressed '{}'.", c), WebEvent::Paste(s) => println!("pasted \"{}\".", s), // Destructure `Click` into `x` and `y`. diff --git a/src-en/custom_types/enum/enum_use.md b/src-en/custom_types/enum/enum_use.md index cf75c67..80a86e1 100644 --- a/src-en/custom_types/enum/enum_use.md +++ b/src-en/custom_types/enum/enum_use.md @@ -6,45 +6,45 @@ The `use` declaration can be used so manual scoping isn't needed: // An attribute to hide warnings for unused code. #![allow(dead_code)] -enum Status { - Rich, - Poor, +enum Stage { + Beginner, + Advanced, } -enum Work { - Civilian, - Soldier, +enum Role { + Student, + Teacher, } fn main() { // Explicitly `use` each name so they are available without // manual scoping. - use crate::Status::{Poor, Rich}; - // Automatically `use` each name inside `Work`. - use crate::Work::*; + use crate::Stage::{Beginner, Advanced}; + // Automatically `use` each name inside `Role`. + use crate::Role::*; - // Equivalent to `Status::Poor`. - let status = Poor; - // Equivalent to `Work::Civilian`. - let work = Civilian; + // Equivalent to `Stage::Beginner`. + let stage = Beginner; + // Equivalent to `Role::Student`. + let role = Student; - match status { + match stage { // Note the lack of scoping because of the explicit `use` above. - Rich => println!("The rich have lots of money!"), - Poor => println!("The poor have no money..."), + Beginner => println!("Beginners are starting their learning journey!"), + Advanced => println!("Advanced learners are mastering their subjects..."), } - match work { + match role { // Note again the lack of scoping. - Civilian => println!("Civilians work!"), - Soldier => println!("Soldiers fight!"), + Student => println!("Students are acquiring knowledge!"), + Teacher => println!("Teachers are spreading knowledge!"), } } ``` ### See also: -[`match`][match] and [`use`][use] +[`match`][match] and [`use`][use] [use]: ../../mod/use.md [match]: ../../flow_control/match.md diff --git a/src-en/custom_types/enum/testcase_linked_list.md b/src-en/custom_types/enum/testcase_linked_list.md index 84855d1..cd4e3e6 100644 --- a/src-en/custom_types/enum/testcase_linked_list.md +++ b/src-en/custom_types/enum/testcase_linked_list.md @@ -1,6 +1,6 @@ # Testcase: linked-list -A common use for `enums` is to create a linked-list: +A common way to implement a linked-list is via `enums`: ```rust,editable use crate::List::*; @@ -32,6 +32,9 @@ impl List { // depends on the variant of `self` // `self` has type `&List`, and `*self` has type `List`, matching on a // concrete type `T` is preferred over a match on a reference `&T` + // after Rust 2018 you can use self here and tail (with no ref) below as well, + // rust will infer &s and ref tail. + // See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html match *self { // Can't take ownership of the tail, because `self` is borrowed; // instead take a reference to the tail diff --git a/src-en/custom_types/structs.md b/src-en/custom_types/structs.md index 0dfc261..eccd30e 100644 --- a/src-en/custom_types/structs.md +++ b/src-en/custom_types/structs.md @@ -8,6 +8,9 @@ There are three types of structures ("structs") that can be created using the * Unit structs, which are field-less, are useful for generics. ```rust,editable +// An attribute to hide warnings for unused code. +#![allow(dead_code)] + #[derive(Debug)] struct Person { name: String, @@ -27,7 +30,6 @@ struct Point { } // Structs can be reused as fields of another struct -#[allow(dead_code)] struct Rectangle { // A rectangle can be specified by where the top left and bottom right // corners are in space. @@ -44,23 +46,23 @@ fn main() { // Print debug struct println!("{:?}", peter); - // Instantiate a `Point` - let point: Point = Point { x: 10.3, y: 0.4 }; + let point: Point = Point { x: 5.2, y: 0.4 }; + let another_point: Point = Point { x: 10.3, y: 0.2 }; // Access the fields of the point println!("point coordinates: ({}, {})", point.x, point.y); // Make a new point by using struct update syntax to use the fields of our // other one - let bottom_right = Point { x: 5.2, ..point }; + let bottom_right = Point { x: 10.3, ..another_point }; - // `bottom_right.y` will be the same as `point.y` because we used that field - // from `point` + // `bottom_right.y` will be the same as `another_point.y` because we used that field + // from `another_point` println!("second point: ({}, {})", bottom_right.x, bottom_right.y); // Destructure the point using a `let` binding - let Point { x: top_edge, y: left_edge } = point; + let Point { x: left_edge, y: top_edge } = point; let _rectangle = Rectangle { // struct instantiation is an expression too @@ -86,16 +88,17 @@ fn main() { ### Activity -1. Add a function `rect_area` which calculates the area of a rectangle (try +1. Add a function `rect_area` which calculates the area of a `Rectangle` (try using nested destructuring). 2. Add a function `square` which takes a `Point` and a `f32` as arguments, and - returns a `Rectangle` with its lower left corner on the point, and a width and + returns a `Rectangle` with its top left corner on the point, and a width and height corresponding to the `f32`. ### See also -[`attributes`][attributes], and [destructuring][destructuring] +[`attributes`][attributes], [raw identifiers][raw_identifiers] and [destructuring][destructuring] [attributes]: ../attribute.md [c_struct]: https://en.wikipedia.org/wiki/Struct_(C_programming_language) [destructuring]: ../flow_control/match/destructuring.md +[raw_identifiers]: ../compatibility/raw_identifiers.md diff --git a/src-en/error/abort_unwind.md b/src-en/error/abort_unwind.md new file mode 100644 index 0000000..e178566 --- /dev/null +++ b/src-en/error/abort_unwind.md @@ -0,0 +1,58 @@ +# `abort` and `unwind` + +The previous section illustrates the error handling mechanism `panic`. Different code paths can be conditionally compiled based on the panic setting. The current values available are `unwind` and `abort`. + +Building on the prior lemonade example, we explicitly use the panic strategy to exercise different lines of code. + +```rust,editable,mdbook-runnable +fn drink(beverage: &str) { + // You shouldn't drink too much sugary beverages. + if beverage == "lemonade" { + if cfg!(panic = "abort") { + println!("This is not your party. Run!!!!"); + } else { + println!("Spit it out!!!!"); + } + } else { + println!("Some refreshing {} is all I need.", beverage); + } +} + +fn main() { + drink("water"); + drink("lemonade"); +} +``` + +Here is another example focusing on rewriting `drink()` and explicitly use the `unwind` keyword. + +```rust,editable +#[cfg(panic = "unwind")] +fn ah() { + println!("Spit it out!!!!"); +} + +#[cfg(not(panic = "unwind"))] +fn ah() { + println!("This is not your party. Run!!!!"); +} + +fn drink(beverage: &str) { + if beverage == "lemonade" { + ah(); + } else { + println!("Some refreshing {} is all I need.", beverage); + } +} + +fn main() { + drink("water"); + drink("lemonade"); +} +``` + +The panic strategy can be set from the command line by using `abort` or `unwind`. + +```console +rustc lemonade.rs -C panic=abort +``` diff --git a/src-en/error/iter_result.md b/src-en/error/iter_result.md index cef97ae..288f4fc 100644 --- a/src-en/error/iter_result.md +++ b/src-en/error/iter_result.md @@ -24,16 +24,34 @@ fn main() { let strings = vec!["tofu", "93", "18"]; let numbers: Vec<_> = strings .into_iter() - .map(|s| s.parse::()) - .filter_map(Result::ok) + .filter_map(|s| s.parse::().ok()) .collect(); println!("Results: {:?}", numbers); } ``` +## Collect the failed items with `map_err()` and `filter_map()` + +`map_err` calls a function with the error, so by adding that to the previous +`filter_map` solution we can save them off to the side while iterating. + +```rust,editable +fn main() { + let strings = vec!["42", "tofu", "93", "999", "18"]; + let mut errors = vec![]; + let numbers: Vec<_> = strings + .into_iter() + .map(|s| s.parse::()) + .filter_map(|r| r.map_err(|e| errors.push(e)).ok()) + .collect(); + println!("Numbers: {:?}", numbers); + println!("Errors: {:?}", errors); +} +``` + ## Fail the entire operation with `collect()` -`Result` implements `FromIter` so that a vector of results (`Vec>`) +`Result` implements `FromIterator` so that a vector of results (`Vec>`) can be turned into a result with a vector (`Result, E>`). Once an `Result::Err` is found, the iteration will terminate. diff --git a/src-en/error/multiple_error_types/boxing_errors.md b/src-en/error/multiple_error_types/boxing_errors.md index 84b0c41..ca506d4 100644 --- a/src-en/error/multiple_error_types/boxing_errors.md +++ b/src-en/error/multiple_error_types/boxing_errors.md @@ -12,7 +12,7 @@ via [`From`][from]. use std::error; use std::fmt; -// Change the alias to `Box`. +// Change the alias to use `Box`. type Result = std::result::Result>; #[derive(Debug, Clone)] diff --git a/src-en/error/multiple_error_types/define_error_type.md b/src-en/error/multiple_error_types/define_error_type.md index 4c37427..10efa82 100644 --- a/src-en/error/multiple_error_types/define_error_type.md +++ b/src-en/error/multiple_error_types/define_error_type.md @@ -8,11 +8,11 @@ Rust allows us to define our own error types. In general, a "good" error type: * Represents different errors with the same type * Presents nice error messages to the user * Is easy to compare with other types - - Good: `Err(EmptyVec)` - - Bad: `Err("Please use a vector with at least one element".to_owned())` + * Good: `Err(EmptyVec)` + * Bad: `Err("Please use a vector with at least one element".to_owned())` * Can hold information about the error - - Good: `Err(BadChar(c, position))` - - Bad: `Err("+ cannot be used here".to_owned())` + * Good: `Err(BadChar(c, position))` + * Bad: `Err("+ cannot be used here".to_owned())` * Composes well with other errors ```rust,editable diff --git a/src-en/error/multiple_error_types/option_result.md b/src-en/error/multiple_error_types/option_result.md index d2273f6..938f934 100644 --- a/src-en/error/multiple_error_types/option_result.md +++ b/src-en/error/multiple_error_types/option_result.md @@ -28,8 +28,7 @@ fn main() { ``` There are times when we'll want to stop processing on errors (like with -[`?`][enter_question_mark]) but keep going when the `Option` is `None`. A -couple of combinators come in handy to swap the `Result` and `Option`. +[`?`][enter_question_mark]) but keep going when the `Option` is `None`. The `transpose` function comes in handy to swap the `Result` and `Option`. ```rust,editable use std::num::ParseIntError; @@ -39,7 +38,7 @@ fn double_first(vec: Vec<&str>) -> Result, ParseIntError> { first.parse::().map(|n| 2 * n) }); - opt.map_or(Ok(None), |r| r.map(Some)) + opt.transpose() } fn main() { diff --git a/src-en/error/multiple_error_types/reenter_question_mark.md b/src-en/error/multiple_error_types/reenter_question_mark.md index 61f80fc..e001464 100644 --- a/src-en/error/multiple_error_types/reenter_question_mark.md +++ b/src-en/error/multiple_error_types/reenter_question_mark.md @@ -5,7 +5,7 @@ Notice in the previous example that our immediate reaction to calling error: ```rust,ignore -.and_then(|s| s.parse::() +.and_then(|s| s.parse::()) .map_err(|e| e.into()) ``` @@ -26,7 +26,7 @@ Here, we rewrite the previous example using `?`. As a result, the use std::error; use std::fmt; -// Change the alias to `Box`. +// Change the alias to use `Box`. type Result = std::result::Result>; #[derive(Debug)] diff --git a/src-en/error/multiple_error_types/wrap_error.md b/src-en/error/multiple_error_types/wrap_error.md index 392b783..71f6262 100644 --- a/src-en/error/multiple_error_types/wrap_error.md +++ b/src-en/error/multiple_error_types/wrap_error.md @@ -4,7 +4,7 @@ An alternative to boxing errors is to wrap them in your own error type. ```rust,editable use std::error; -use std::error::Error as _; +use std::error::Error; use std::num::ParseIntError; use std::fmt; @@ -92,5 +92,8 @@ for you. [`From::from`][from] and [`Enums`][enums] +[`Crates for handling errors`][crates-errors] + [from]: https://doc.rust-lang.org/std/convert/trait.From.html [enums]: ../../custom_types/enum.md +[crates-errors]: https://crates.io/keywords/error-handling diff --git a/src-en/error/option_unwrap.md b/src-en/error/option_unwrap.md index df02b8c..26df9b8 100644 --- a/src-en/error/option_unwrap.md +++ b/src-en/error/option_unwrap.md @@ -1,13 +1,13 @@ # `Option` & `unwrap` In the last example, we showed that we can induce program failure at will. -We told our program to `panic` if the royal received an inappropriate -gift - a snake. But what if the royal expected a gift and didn't receive -one? That case would be just as bad, so it needs to be handled! +We told our program to `panic` if we drink a sugary lemonade. +But what if we expect _some_ drink but don't receive one? +That case would be just as bad, so it needs to be handled! -We *could* test this against the null string (`""`) as we do with a snake. +We _could_ test this against the null string (`""`) as we do with a lemonade. Since we're using Rust, let's instead have the compiler point out cases -where there's no gift. +where there's no drink. An `enum` called `Option` in the `std` library is used when absence is a possibility. It manifests itself as one of two "options": @@ -24,41 +24,41 @@ handling. In the following example, explicit handling yields a more controlled result while retaining the option to `panic` if desired. ```rust,editable,ignore,mdbook-runnable -// The commoner has seen it all, and can handle any gift well. -// All gifts are handled explicitly using `match`. -fn give_commoner(gift: Option<&str>) { +// The adult has seen it all, and can handle any drink well. +// All drinks are handled explicitly using `match`. +fn give_adult(drink: Option<&str>) { // Specify a course of action for each case. - match gift { - Some("snake") => println!("Yuck! I'm putting this snake back in the forest."), + match drink { + Some("lemonade") => println!("Yuck! Too sugary."), Some(inner) => println!("{}? How nice.", inner), - None => println!("No gift? Oh well."), + None => println!("No drink? Oh well."), } } -// Our sheltered royal will `panic` at the sight of snakes. -// All gifts are handled implicitly using `unwrap`. -fn give_royal(gift: Option<&str>) { +// Others will `panic` before drinking sugary drinks. +// All drinks are handled implicitly using `unwrap`. +fn drink(drink: Option<&str>) { // `unwrap` returns a `panic` when it receives a `None`. - let inside = gift.unwrap(); - if inside == "snake" { panic!("AAAaaaaa!!!!"); } + let inside = drink.unwrap(); + if inside == "lemonade" { panic!("AAAaaaaa!!!!"); } println!("I love {}s!!!!!", inside); } fn main() { - let food = Some("cabbage"); - let snake = Some("snake"); + let water = Some("water"); + let lemonade = Some("lemonade"); let void = None; - give_commoner(food); - give_commoner(snake); - give_commoner(void); + give_adult(water); + give_adult(lemonade); + give_adult(void); - let bird = Some("robin"); + let coffee = Some("coffee"); let nothing = None; - give_royal(bird); - give_royal(nothing); + drink(coffee); + drink(nothing); } ``` diff --git a/src-en/error/option_unwrap/and_then.md b/src-en/error/option_unwrap/and_then.md index c065f20..7dee272 100644 --- a/src-en/error/option_unwrap/and_then.md +++ b/src-en/error/option_unwrap/and_then.md @@ -1,14 +1,14 @@ # Combinators: `and_then` -`map()` was described as a chainable way to simplify `match` statements. -However, using `map()` on a function that returns an `Option` results -in the nested `Option>`. Chaining multiple calls together can -then become confusing. That's where another combinator called `and_then()`, +`map()` was described as a chainable way to simplify `match` statements. +However, using `map()` on a function that returns an `Option` results +in the nested `Option>`. Chaining multiple calls together can +then become confusing. That's where another combinator called `and_then()`, known in some languages as flatmap, comes in. `and_then()` calls its function input with the wrapped value and returns the result. If the `Option` is `None`, then it returns `None` instead. -In the following example, `cookable_v2()` results in an `Option`. +In the following example, `cookable_v3()` results in an `Option`. Using `map()` instead of `and_then()` would have given an `Option>`, which is an invalid type for `eat()`. @@ -39,20 +39,23 @@ fn have_recipe(food: Food) -> Option { fn cookable_v1(food: Food) -> Option { match have_recipe(food) { None => None, - Some(food) => match have_ingredients(food) { - None => None, - Some(food) => Some(food), - }, + Some(food) => have_ingredients(food), } } // This can conveniently be rewritten more compactly with `and_then()`: -fn cookable_v2(food: Food) -> Option { +fn cookable_v3(food: Food) -> Option { have_recipe(food).and_then(have_ingredients) } +// Otherwise we'd need to `flatten()` an `Option>` +// to get an `Option`: +fn cookable_v2(food: Food) -> Option { + have_recipe(food).map(have_ingredients).flatten() +} + fn eat(food: Food, day: Day) { - match cookable_v2(food) { + match cookable_v3(food) { Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food), None => println!("Oh no. We don't get to eat on {:?}?", day), } @@ -69,8 +72,9 @@ fn main() { ### See also: -[closures][closures], [`Option`][option], and [`Option::and_then()`][and_then] +[closures][closures], [`Option`][option], [`Option::and_then()`][and_then], and [`Option::flatten()`][flatten] [closures]: ../../fn/closures.md [option]: https://doc.rust-lang.org/std/option/enum.Option.html [and_then]: https://doc.rust-lang.org/std/option/enum.Option.html#method.and_then +[flatten]: https://doc.rust-lang.org/std/option/enum.Option.html#method.flatten diff --git a/src-en/error/option_unwrap/defaults.md b/src-en/error/option_unwrap/defaults.md new file mode 100644 index 0000000..e749c14 --- /dev/null +++ b/src-en/error/option_unwrap/defaults.md @@ -0,0 +1,124 @@ +# Unpacking options and defaults + +There is more than one way to unpack an `Option` and fall back on a default if it is `None`. To choose the one that meets our needs, we need to consider the following: + +* do we need eager or lazy evaluation? +* do we need to keep the original empty value intact, or modify it in place? + +## `or()` is chainable, evaluates eagerly, keeps empty value intact + +`or()`is chainable and eagerly evaluates its argument, as is shown in the following example. Note that because `or`'s arguments are evaluated eagerly, the variable passed to `or` is moved. + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let apple = Some(Fruit::Apple); + let orange = Some(Fruit::Orange); + let no_fruit: Option = None; + + let first_available_fruit = no_fruit.or(orange).or(apple); + println!("first_available_fruit: {:?}", first_available_fruit); + // first_available_fruit: Some(Orange) + + // `or` moves its argument. + // In the example above, `or(orange)` returned a `Some`, so `or(apple)` was not invoked. + // But the variable named `apple` has been moved regardless, and cannot be used anymore. + // println!("Variable apple was moved, so this line won't compile: {:?}", apple); + // TODO: uncomment the line above to see the compiler error + } +``` + +## `or_else()` is chainable, evaluates lazily, keeps empty value intact + +Another alternative is to use `or_else`, which is also chainable, and evaluates lazily, as is shown in the following example: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let no_fruit: Option = None; + let get_kiwi_as_fallback = || { + println!("Providing kiwi as fallback"); + Some(Fruit::Kiwi) + }; + let get_lemon_as_fallback = || { + println!("Providing lemon as fallback"); + Some(Fruit::Lemon) + }; + + let first_available_fruit = no_fruit + .or_else(get_kiwi_as_fallback) + .or_else(get_lemon_as_fallback); + println!("first_available_fruit: {:?}", first_available_fruit); + // Providing kiwi as fallback + // first_available_fruit: Some(Kiwi) +} +``` + +## `get_or_insert()` evaluates eagerly, modifies empty value in place + +To make sure that an `Option` contains a value, we can use `get_or_insert` to modify it in place with a fallback value, as is shown in the following example. Note that `get_or_insert` eagerly evaluates its parameter, so variable `apple` is moved: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let mut my_fruit: Option = None; + let apple = Fruit::Apple; + let first_available_fruit = my_fruit.get_or_insert(apple); + println!("first_available_fruit is: {:?}", first_available_fruit); + println!("my_fruit is: {:?}", my_fruit); + // first_available_fruit is: Apple + // my_fruit is: Some(Apple) + //println!("Variable named `apple` is moved: {:?}", apple); + // TODO: uncomment the line above to see the compiler error +} +``` + +## `get_or_insert_with()` evaluates lazily, modifies empty value in place + +Instead of explicitly providing a value to fall back on, we can pass a closure to `get_or_insert_with`, as follows: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let mut my_fruit: Option = None; + let get_lemon_as_fallback = || { + println!("Providing lemon as fallback"); + Fruit::Lemon + }; + let first_available_fruit = my_fruit + .get_or_insert_with(get_lemon_as_fallback); + println!("first_available_fruit is: {:?}", first_available_fruit); + println!("my_fruit is: {:?}", my_fruit); + // Providing lemon as fallback + // first_available_fruit is: Lemon + // my_fruit is: Some(Lemon) + + // If the Option has a value, it is left unchanged, and the closure is not invoked + let mut my_apple = Some(Fruit::Apple); + let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback); + println!("should_be_apple is: {:?}", should_be_apple); + println!("my_apple is unchanged: {:?}", my_apple); + // The output is a follows. Note that the closure `get_lemon_as_fallback` is not invoked + // should_be_apple is: Apple + // my_apple is unchanged: Some(Apple) +} +``` + +### See also: + +[`closures`][closures], [`get_or_insert`][get_or_insert], [`get_or_insert_with`][get_or_insert_with], [`moved variables`][moved], [`or`][or], [`or_else`][or_else] + +[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html +[get_or_insert]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert +[get_or_insert_with]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert_with +[moved]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html +[or]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or +[or_else]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or_else diff --git a/src-en/error/option_unwrap/map.md b/src-en/error/option_unwrap/map.md index c458e7d..c063115 100644 --- a/src-en/error/option_unwrap/map.md +++ b/src-en/error/option_unwrap/map.md @@ -1,12 +1,12 @@ # Combinators: `map` -`match` is a valid method for handling `Option`s. However, you may -eventually find heavy usage tedious, especially with operations only valid -with an input. In these cases, [combinators][combinators] can be used to +`match` is a valid method for handling `Option`s. However, you may +eventually find heavy usage tedious, especially with operations only valid +with an input. In these cases, [combinators][combinators] can be used to manage control flow in a modular fashion. -`Option` has a built in method called `map()`, a combinator for the simple -mapping of `Some -> Some` and `None -> None`. Multiple `map()` calls can be +`Option` has a built in method called `map()`, a combinator for the simple +mapping of `Some -> Some` and `None -> None`. Multiple `map()` calls can be chained together for even more flexibility. In the following example, `process()` replaces all functions previous @@ -80,7 +80,7 @@ fn main() { [closures][closures], [`Option`][option], [`Option::map()`][map] -[combinators]: https://doc.rust-lang.org/book/glossary.html#combinators +[combinators]: https://doc.rust-lang.org/reference/glossary.html#combinator [closures]: ../../fn/closures.md [option]: https://doc.rust-lang.org/std/option/enum.Option.html [map]: https://doc.rust-lang.org/std/option/enum.Option.html#method.map diff --git a/src-en/error/option_unwrap/question_mark.md b/src-en/error/option_unwrap/question_mark.md index 7437ff6..a848356 100644 --- a/src-en/error/option_unwrap/question_mark.md +++ b/src-en/error/option_unwrap/question_mark.md @@ -7,9 +7,10 @@ function is being executed and return `None`. ```rust,editable fn next_birthday(current_age: Option) -> Option { - // If `current_age` is `None`, this returns `None`. - // If `current_age` is `Some`, the inner `u8` gets assigned to `next_age` - let next_age: u8 = current_age?; + // If `current_age` is `None`, this returns `None`. + // If `current_age` is `Some`, the inner `u8` value + 1 + // gets assigned to `next_age` + let next_age: u8 = current_age? + 1; Some(format!("Next year I will be {}", next_age)) } ``` diff --git a/src-en/error/panic.md b/src-en/error/panic.md index 5524dbf..0544f22 100644 --- a/src-en/error/panic.md +++ b/src-en/error/panic.md @@ -6,7 +6,7 @@ Here, we explicitly call `panic` on our error condition: ```rust,editable,ignore,mdbook-runnable fn drink(beverage: &str) { - // You shouldn't drink too much sugary beverages. + // You shouldn't drink too many sugary beverages. if beverage == "lemonade" { panic!("AAAaaaaa!!!!"); } println!("Some refreshing {} is all I need.", beverage); @@ -15,5 +15,8 @@ fn drink(beverage: &str) { fn main() { drink("water"); drink("lemonade"); + drink("still water"); } ``` + +The first call to `drink` works. The second panics and thus the third is never called. diff --git a/src-en/error/result.md b/src-en/error/result.md index d779bfd..4eab431 100644 --- a/src-en/error/result.md +++ b/src-en/error/result.md @@ -75,7 +75,6 @@ fn main() -> Result<(), ParseIntError> { } ``` - [option]: https://doc.rust-lang.org/std/option/enum.Option.html [result]: https://doc.rust-lang.org/std/result/enum.Result.html [parse]: https://doc.rust-lang.org/std/primitive.str.html#method.parse diff --git a/src-en/error/result/enter_question_mark.md b/src-en/error/result/enter_question_mark.md index 8101e3e..2b86862 100644 --- a/src-en/error/result/enter_question_mark.md +++ b/src-en/error/result/enter_question_mark.md @@ -43,7 +43,7 @@ The `?` operator is now recommended, but you may still find `try!` when looking at older code. The same `multiply` function from the previous example would look like this using `try!`: -```rust,editable +```rust,editable,edition2015 // To compile and run this example without errors, while using Cargo, change the value // of the `edition` field, in the `[package]` section of the `Cargo.toml` file, to "2015". @@ -69,7 +69,6 @@ fn main() { } ``` - [^†]: See [re-enter ?][re_enter_?] for more details. [re_enter_?]: ../multiple_error_types/reenter_question_mark.md diff --git a/src-en/error/result/result_map.md b/src-en/error/result/result_map.md index 24537c3..c453d9f 100644 --- a/src-en/error/result/result_map.md +++ b/src-en/error/result/result_map.md @@ -56,7 +56,7 @@ use std::num::ParseIntError; // As with `Option`, we can use combinators such as `map()`. // This function is otherwise identical to the one above and reads: -// Modify n if the value is valid, otherwise pass on the error. +// Multiply if both values can be parsed from str, otherwise pass on the error. fn multiply(first_number_str: &str, second_number_str: &str) -> Result { first_number_str.parse::().and_then(|first_number| { second_number_str.parse::().map(|second_number| first_number * second_number) diff --git a/src-en/expression.md b/src-en/expression.md index 467bc10..27a278d 100644 --- a/src-en/expression.md +++ b/src-en/expression.md @@ -2,7 +2,7 @@ A Rust program is (mostly) made up of a series of statements: -``` +```rust,editable fn main() { // statement // statement @@ -13,7 +13,7 @@ fn main() { There are a few kinds of statements in Rust. The most common two are declaring a variable binding, and using a `;` with an expression: -``` +```rust,editable fn main() { // variable binding let x = 5; diff --git a/src-en/flow_control.md b/src-en/flow_control.md index c8a2f9e..79ef7e1 100644 --- a/src-en/flow_control.md +++ b/src-en/flow_control.md @@ -1,4 +1,4 @@ # Flow of Control -An essential part of any programming languages are ways to modify control flow: +An integral part of any programming language are ways to modify control flow: `if`/`else`, `for`, and others. Let's talk about them in Rust. diff --git a/src-en/flow_control/for.md b/src-en/flow_control/for.md index 99e6458..b5d5d62 100644 --- a/src-en/flow_control/for.md +++ b/src-en/flow_control/for.md @@ -60,16 +60,19 @@ within. * `iter` - This borrows each element of the collection through each iteration. Thus leaving the collection untouched and available for reuse after the loop. -```rust, editable +```rust,editable fn main() { let names = vec!["Bob", "Frank", "Ferris"]; for name in names.iter() { match name { &"Ferris" => println!("There is a rustacean among us!"), + // TODO ^ Try deleting the & and matching just "Ferris" _ => println!("Hello {}", name), } } + + println!("names: {:?}", names); } ``` @@ -77,7 +80,7 @@ fn main() { data is provided. Once the collection has been consumed it is no longer available for reuse as it has been 'moved' within the loop. -```rust, editable +```rust,editable,ignore,mdbook-runnable fn main() { let names = vec!["Bob", "Frank", "Ferris"]; @@ -87,13 +90,16 @@ fn main() { _ => println!("Hello {}", name), } } + + println!("names: {:?}", names); + // FIXME ^ Comment out this line } ``` * `iter_mut` - This mutably borrows each element of the collection, allowing for the collection to be modified in place. -```rust, editable +```rust,editable fn main() { let mut names = vec!["Bob", "Frank", "Ferris"]; diff --git a/src-en/flow_control/if_let.md b/src-en/flow_control/if_let.md index 3742b6f..8936792 100644 --- a/src-en/flow_control/if_let.md +++ b/src-en/flow_control/if_let.md @@ -7,11 +7,7 @@ For some use cases, when matching enums, `match` is awkward. For example: let optional = Some(7); match optional { - Some(i) => { - println!("This is a really long string and `{:?}`", i); - // ^ Needed 2 indentations just so we could destructure - // `i` from the option. - }, + Some(i) => println!("This is a really long string and `{:?}`", i), _ => {}, // ^ Required because `match` is exhaustive. Doesn't it seem // like wasted space? diff --git a/src-en/flow_control/let_else.md b/src-en/flow_control/let_else.md new file mode 100644 index 0000000..2d07de7 --- /dev/null +++ b/src-en/flow_control/let_else.md @@ -0,0 +1,62 @@ +# let-else + +> 🛈 stable since: rust 1.65 +> +> 🛈 you can target specific edition by compiling like this +> `rustc --edition=2021 main.rs` + +With `let`-`else`, a refutable pattern can match and bind variables +in the surrounding scope like a normal `let`, or else diverge (e.g. `break`, +`return`, `panic!`) when the pattern doesn't match. + +```rust +use std::str::FromStr; + +fn get_count_item(s: &str) -> (u64, &str) { + let mut it = s.split(' '); + let (Some(count_str), Some(item)) = (it.next(), it.next()) else { + panic!("Can't segment count item pair: '{s}'"); + }; + let Ok(count) = u64::from_str(count_str) else { + panic!("Can't parse integer: '{count_str}'"); + }; + (count, item) +} + +fn main() { + assert_eq!(get_count_item("3 chairs"), (3, "chairs")); +} +``` + +The scope of name bindings is the main thing that makes this different from +`match` or `if let`-`else` expressions. You could previously approximate these +patterns with an unfortunate bit of repetition and an outer `let`: + +```rust +# use std::str::FromStr; +# +# fn get_count_item(s: &str) -> (u64, &str) { +# let mut it = s.split(' '); + let (count_str, item) = match (it.next(), it.next()) { + (Some(count_str), Some(item)) => (count_str, item), + _ => panic!("Can't segment count item pair: '{s}'"), + }; + let count = if let Ok(count) = u64::from_str(count_str) { + count + } else { + panic!("Can't parse integer: '{count_str}'"); + }; +# (count, item) +# } +# +# assert_eq!(get_count_item("3 chairs"), (3, "chairs")); +``` + +### See also: + +[option][option], [match][match], [if let][if_let] and the [let-else RFC][let_else_rfc]. + +[match]: ./match.md +[if_let]: ./if_let.md +[let_else_rfc]: https://rust-lang.github.io/rfcs/3137-let-else.html +[option]: ../std/option.md diff --git a/src-en/flow_control/loop.md b/src-en/flow_control/loop.md index 4a2405b..93350c8 100644 --- a/src-en/flow_control/loop.md +++ b/src-en/flow_control/loop.md @@ -33,4 +33,4 @@ fn main() { } } } -``` \ No newline at end of file +``` diff --git a/src-en/flow_control/loop/nested.md b/src-en/flow_control/loop/nested.md index 01c55a5..01e8f39 100644 --- a/src-en/flow_control/loop/nested.md +++ b/src-en/flow_control/loop/nested.md @@ -5,7 +5,7 @@ loops. In these cases, the loops must be annotated with some `'label`, and the label must be passed to the `break`/`continue` statement. ```rust,editable -#![allow(unreachable_code)] +#![allow(unreachable_code, unused_labels)] fn main() { 'outer: loop { @@ -26,4 +26,4 @@ fn main() { println!("Exited the outer loop"); } -``` \ No newline at end of file +``` diff --git a/src-en/flow_control/match.md b/src-en/flow_control/match.md index 02a8067..6beeea3 100644 --- a/src-en/flow_control/match.md +++ b/src-en/flow_control/match.md @@ -1,7 +1,8 @@ # match Rust provides pattern matching via the `match` keyword, which can be used like -a C `switch`. +a C `switch`. The first matching arm is evaluated and all possible values must be +covered. ```rust,editable fn main() { @@ -14,10 +15,12 @@ fn main() { 1 => println!("One!"), // Match several values 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), + // TODO ^ Try adding 13 to the list of prime values // Match an inclusive range 13..=19 => println!("A teen"), // Handle the rest of cases _ => println!("Ain't special"), + // TODO ^ Try commenting out this catch-all arm } let boolean = true; diff --git a/src-en/flow_control/match/binding.md b/src-en/flow_control/match/binding.md index ccbb7c2..7c8ecfe 100644 --- a/src-en/flow_control/match/binding.md +++ b/src-en/flow_control/match/binding.md @@ -47,6 +47,7 @@ fn main() { ``` ### See also: + [`functions`][functions], [`enums`][enums] and [`Option`][option] [functions]: ../../fn.md diff --git a/src-en/flow_control/match/destructuring.md b/src-en/flow_control/match/destructuring.md index 5e82e6c..763d1f8 100644 --- a/src-en/flow_control/match/destructuring.md +++ b/src-en/flow_control/match/destructuring.md @@ -3,12 +3,13 @@ A `match` block can destructure items in a variety of ways. * [Destructuring Tuples][tuple] +* [Destructuring Arrays and Slices][slice] * [Destructuring Enums][enum] * [Destructuring Pointers][refs] * [Destructuring Structures][struct] - [enum]: destructuring/destructure_enum.md [refs]: destructuring/destructure_pointers.md [struct]: destructuring/destructure_structures.md [tuple]: destructuring/destructure_tuple.md +[slice]: destructuring/destructure_slice.md diff --git a/src-en/flow_control/match/destructuring/destructure_pointers.md b/src-en/flow_control/match/destructuring/destructure_pointers.md index 43ca3bd..4d22bba 100644 --- a/src-en/flow_control/match/destructuring/destructure_pointers.md +++ b/src-en/flow_control/match/destructuring/destructure_pointers.md @@ -2,10 +2,10 @@ For pointers, a distinction needs to be made between destructuring and dereferencing as they are different concepts which are used -differently from a language like `C`. +differently from languages like C/C++. - * Dereferencing uses `*` - * Destructuring uses `&`, `ref`, and `ref mut` +* Dereferencing uses `*` +* Destructuring uses `&`, `ref`, and `ref mut` ```rust,editable fn main() { diff --git a/src-en/flow_control/match/destructuring/destructure_slice.md b/src-en/flow_control/match/destructuring/destructure_slice.md new file mode 100644 index 0000000..93b7e42 --- /dev/null +++ b/src-en/flow_control/match/destructuring/destructure_slice.md @@ -0,0 +1,48 @@ +# arrays/slices + +Like tuples, arrays and slices can be destructured this way: + +```rust,editable +fn main() { + // Try changing the values in the array, or make it a slice! + let array = [1, -2, 6]; + + match array { + // Binds the second and the third elements to the respective variables + [0, second, third] => + println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third), + + // Single values can be ignored with _ + [1, _, third] => println!( + "array[0] = 1, array[2] = {} and array[1] was ignored", + third + ), + + // You can also bind some and ignore the rest + [-1, second, ..] => println!( + "array[0] = -1, array[1] = {} and all the other ones were ignored", + second + ), + // The code below would not compile + // [-1, second] => ... + + // Or store them in another array/slice (the type depends on + // that of the value that is being matched against) + [3, second, tail @ ..] => println!( + "array[0] = 3, array[1] = {} and the other elements were {:?}", + second, tail + ), + + // Combining these patterns, we can, for example, bind the first and + // last values, and store the rest of them in a single array + [first, middle @ .., last] => println!( + "array[0] = {}, middle = {:?}, array[2] = {}", + first, middle, last + ), + } +} +``` + +### See also: + +[Arrays and Slices](../../../primitives/array.md) and [Binding](../binding.md) for `@` sigil diff --git a/src-en/flow_control/match/destructuring/destructure_structures.md b/src-en/flow_control/match/destructuring/destructure_structures.md index 9e43b70..96b2436 100644 --- a/src-en/flow_control/match/destructuring/destructure_structures.md +++ b/src-en/flow_control/match/destructuring/destructure_structures.md @@ -24,6 +24,21 @@ fn main() { // this will give an error: pattern does not mention field `x` //Foo { y } => println!("y = {}", y), } + + let faa = Foo { x: (1, 2), y: 3 }; + + // You do not need a match block to destructure structs: + let Foo { x : x0, y: y0 } = faa; + println!("Outside: x0 = {x0:?}, y0 = {y0}"); + + // Destructuring works with nested structs as well: + struct Bar { + foo: Foo, + } + + let bar = Bar { foo: faa }; + let Bar { foo: Foo { x: nested_x, y: nested_y } } = bar; + println!("Nested: nested_x = {nested_x:?}, nested_y = {nested_y:?}"); } ``` diff --git a/src-en/flow_control/match/destructuring/destructure_tuple.md b/src-en/flow_control/match/destructuring/destructure_tuple.md index 30ae68f..32a77da 100644 --- a/src-en/flow_control/match/destructuring/destructure_tuple.md +++ b/src-en/flow_control/match/destructuring/destructure_tuple.md @@ -13,7 +13,9 @@ fn main() { // Destructure the second and third elements (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z), (1, ..) => println!("First is `1` and the rest doesn't matter"), - // `..` can be the used ignore the rest of the tuple + (.., 2) => println!("last is `2` and the rest doesn't matter"), + (3, .., 4) => println!("First is `3`, last is `4`, and the rest doesn't matter"), + // `..` can be used to ignore the rest of the tuple _ => println!("It doesn't matter what they are"), // `_` means don't bind the value to a variable } diff --git a/src-en/flow_control/match/guard.md b/src-en/flow_control/match/guard.md index 336b7f1..df9fb2c 100644 --- a/src-en/flow_control/match/guard.md +++ b/src-en/flow_control/match/guard.md @@ -3,17 +3,39 @@ A `match` *guard* can be added to filter the arm. ```rust,editable +#[allow(dead_code)] +enum Temperature { + Celsius(i32), + Fahrenheit(i32), +} + fn main() { - let pair = (2, -2); - // TODO ^ Try different values for `pair` - - println!("Tell me about {:?}", pair); - match pair { - (x, y) if x == y => println!("These are twins"), - // The ^ `if condition` part is a guard - (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), - (x, _) if x % 2 == 1 => println!("The first one is odd"), - _ => println!("No correlation..."), + let temperature = Temperature::Celsius(35); + // ^ TODO try different values for `temperature` + + match temperature { + Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t), + // The `if condition` part ^ is a guard + Temperature::Celsius(t) => println!("{}C is equal to or below 30 Celsius", t), + + Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t), + Temperature::Fahrenheit(t) => println!("{}F is equal to or below 86 Fahrenheit", t), + } +} +``` + +Note that the compiler won't take guard conditions into account when checking +if all patterns are covered by the match expression. + +```rust,editable,ignore,mdbook-runnable +fn main() { + let number: u8 = 4; + + match number { + i if i == 0 => println!("Zero"), + i if i > 0 => println!("Greater than zero"), + // _ => unreachable!("Should never happen."), + // TODO ^ uncomment to fix compilation } } ``` @@ -21,3 +43,4 @@ fn main() { ### See also: [Tuples](../../primitives/tuples.md) +[Enums](../../custom_types/enum.md) diff --git a/src-en/flow_control/while_let.md b/src-en/flow_control/while_let.md index 897375a..f6f8eff 100644 --- a/src-en/flow_control/while_let.md +++ b/src-en/flow_control/while_let.md @@ -34,7 +34,7 @@ Using `while let` makes this sequence much nicer: fn main() { // Make `optional` of type `Option` let mut optional = Some(0); - + // This reads: "while `let` destructures `optional` into // `Some(i)`, evaluate the block (`{}`). Else `break`. while let Some(i) = optional { diff --git a/src-en/fn.md b/src-en/fn.md index 3516066..e775522 100644 --- a/src-en/fn.md +++ b/src-en/fn.md @@ -44,7 +44,7 @@ fn fizzbuzz(n: u32) -> () { // When a function returns `()`, the return type can be omitted from the // signature fn fizzbuzz_to(n: u32) { - for n in 1..n + 1 { + for n in 1..=n { fizzbuzz(n); } } diff --git a/src-en/fn/closures.md b/src-en/fn/closures.md index baf1c42..16669fb 100644 --- a/src-en/fn/closures.md +++ b/src-en/fn/closures.md @@ -1,39 +1,45 @@ # Closures Closures are functions that can capture the enclosing environment. For -example, a closure that captures the x variable: +example, a closure that captures the `x` variable: ```Rust |val| val + x ``` -The syntax and capabilities of closures make them very convenient for +The syntax and capabilities of closures make them very convenient for on the fly usage. Calling a closure is exactly like calling a function. -However, both input and return types *can* be inferred and input +However, both input and return types *can* be inferred and input variable names *must* be specified. Other characteristics of closures include: + * using `||` instead of `()` around input variables. -* optional body delimination (`{}`) for a single expression (mandatory otherwise). +* optional body delimitation (`{}`) for a single line expression (mandatory otherwise). * the ability to capture the outer environment variables. ```rust,editable fn main() { - // Increment via closures and functions. - fn function (i: i32) -> i32 { i + 1 } - - // Closures are anonymous, here we are binding them to references + let outer_var = 42; + + // A regular function can't refer to variables in the enclosing environment + //fn function(i: i32) -> i32 { i + outer_var } + // TODO: uncomment the line above and see the compiler error. The compiler + // suggests that we define a closure instead. + + // Closures are anonymous, here we are binding them to references. // Annotation is identical to function annotation but is optional // as are the `{}` wrapping the body. These nameless functions // are assigned to appropriately named variables. - let closure_annotated = |i: i32| -> i32 { i + 1 }; - let closure_inferred = |i | i + 1 ; - - let i = 1; - // Call the function and closures. - println!("function: {}", function(i)); - println!("closure_annotated: {}", closure_annotated(i)); - println!("closure_inferred: {}", closure_inferred(i)); + let closure_annotated = |i: i32| -> i32 { i + outer_var }; + let closure_inferred = |i | i + outer_var ; + + // Call the closures. + println!("closure_annotated: {}", closure_annotated(1)); + println!("closure_inferred: {}", closure_inferred(1)); + // Once closure's type has been inferred, it cannot be inferred again with another type. + //println!("cannot reuse closure_inferred with another type: {}", closure_inferred(42i64)); + // TODO: uncomment the line above and see the compiler error. // A closure taking no arguments which returns an `i32`. // The return type is inferred. diff --git a/src-en/fn/closures/capture.md b/src-en/fn/closures/capture.md index 061ef1c..c2f0c62 100644 --- a/src-en/fn/closures/capture.md +++ b/src-en/fn/closures/capture.md @@ -44,7 +44,7 @@ fn main() { // borrows `count`. // // A `mut` is required on `inc` because a `&mut` is stored inside. Thus, - // calling the closure mutates the closure which requires a `mut`. + // calling the closure mutates `count` which requires a `mut`. let mut inc = || { count += 1; println!("`count`: {}", count); diff --git a/src-en/fn/closures/closure_examples.md b/src-en/fn/closures/closure_examples.md index 2455523..627783e 100644 --- a/src-en/fn/closures/closure_examples.md +++ b/src-en/fn/closures/closure_examples.md @@ -1,3 +1,3 @@ # Examples in `std` -This section contains a few examples of using closures from the `std` library. \ No newline at end of file +This section contains a few examples of using closures from the `std` library. diff --git a/src-en/fn/closures/closure_examples/iter_any.md b/src-en/fn/closures/closure_examples/iter_any.md index 1fb64e9..4c19cac 100644 --- a/src-en/fn/closures/closure_examples/iter_any.md +++ b/src-en/fn/closures/closure_examples/iter_any.md @@ -15,7 +15,7 @@ pub trait Iterator { // `FnMut` meaning any captured variable may at most be // modified, not consumed. `Self::Item` states it takes // arguments to the closure by value. - F: FnMut(Self::Item) -> bool {} + F: FnMut(Self::Item) -> bool; } ``` @@ -27,15 +27,23 @@ fn main() { // `iter()` for vecs yields `&i32`. Destructure to `i32`. println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2)); // `into_iter()` for vecs yields `i32`. No destructuring required. - println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2)); + println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2)); + + // `iter()` only borrows `vec1` and its elements, so they can be used again + println!("vec1 len: {}", vec1.len()); + println!("First element of vec1 is: {}", vec1[0]); + // `into_iter()` does move `vec2` and its elements, so they cannot be used again + // println!("First element of vec2 is: {}", vec2[0]); + // println!("vec2 len: {}", vec2.len()); + // TODO: uncomment two lines above and see compiler errors. let array1 = [1, 2, 3]; let array2 = [4, 5, 6]; // `iter()` for arrays yields `&i32`. println!("2 in array1: {}", array1.iter() .any(|&x| x == 2)); - // `into_iter()` for arrays unusually yields `&i32`. - println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2)); + // `into_iter()` for arrays yields `i32`. + println!("2 in array2: {}", array2.into_iter().any(|x| x == 2)); } ``` diff --git a/src-en/fn/closures/closure_examples/iter_find.md b/src-en/fn/closures/closure_examples/iter_find.md index 9eb5072..62fc57e 100644 --- a/src-en/fn/closures/closure_examples/iter_find.md +++ b/src-en/fn/closures/closure_examples/iter_find.md @@ -1,7 +1,7 @@ # Searching through iterators -`Iterator::find` is a function which iterates over an iterator and searches for the -first value which satisfies some condition. If none of the values satisfy the +`Iterator::find` is a function which iterates over an iterator and searches for the +first value which satisfies some condition. If none of the values satisfy the condition, it returns `None`. Its signature: ```rust,ignore @@ -15,7 +15,7 @@ pub trait Iterator { // `FnMut` meaning any captured variable may at most be // modified, not consumed. `&Self::Item` states it takes // arguments to the closure by reference. - P: FnMut(&Self::Item) -> bool {} + P: FnMut(&Self::Item) -> bool; } ``` @@ -39,10 +39,10 @@ fn main() { let array1 = [1, 2, 3]; let array2 = [4, 5, 6]; - // `iter()` for arrays yields `&i32` + // `iter()` for arrays yields `&&i32` println!("Find 2 in array1: {:?}", array1.iter() .find(|&&x| x == 2)); - // `into_iter()` for arrays unusually yields `&i32` - println!("Find 2 in array2: {:?}", array2.into_iter().find(|&&x| x == 2)); + // `into_iter()` for arrays yields `&i32` + println!("Find 2 in array2: {:?}", array2.into_iter().find(|&x| x == 2)); } ``` @@ -53,11 +53,14 @@ item, use `Iterator::position`. fn main() { let vec = vec![1, 9, 3, 3, 13, 2]; - let index_of_first_even_number = vec.iter().position(|x| x % 2 == 0); + // `iter()` for vecs yields `&i32` and `position()` does not take a reference, so + // we have to destructure `&i32` to `i32` + let index_of_first_even_number = vec.iter().position(|&x| x % 2 == 0); assert_eq!(index_of_first_even_number, Some(5)); - - let index_of_first_negative_number = vec.iter().position(|x| x < &0); + // `into_iter()` for vecs yields `i32` and `position()` does not take a reference, so + // we do not have to destructure + let index_of_first_negative_number = vec.into_iter().position(|x| x < 0); assert_eq!(index_of_first_negative_number, None); } ``` diff --git a/src-en/fn/closures/input_parameters.md b/src-en/fn/closures/input_parameters.md index be36c6d..4149717 100644 --- a/src-en/fn/closures/input_parameters.md +++ b/src-en/fn/closures/input_parameters.md @@ -3,12 +3,13 @@ While Rust chooses how to capture variables on the fly mostly without type annotation, this ambiguity is not allowed when writing functions. When taking a closure as an input parameter, the closure's complete type must be -annotated using one of a few `traits`. In order of decreasing restriction, +annotated using one of a few `traits`, and they're determined by what the +closure does with captured value. In order of decreasing restriction, they are: -* `Fn`: the closure captures by reference (`&T`) -* `FnMut`: the closure captures by mutable reference (`&mut T`) -* `FnOnce`: the closure captures by value (`T`) +* `Fn`: the closure uses the captured value by reference (`&T`) +* `FnMut`: the closure uses the captured value by mutable reference (`&mut T`) +* `FnOnce`: the closure uses the captured value by value (`T`) On a variable-by-variable basis, the compiler will capture variables in the least restrictive manner possible. @@ -21,7 +22,7 @@ closure. This is because if a move is possible, then any type of borrow should also be possible. Note that the reverse is not true. If the parameter is annotated as `Fn`, then capturing variables by `&mut T` or `T` are not -allowed. +allowed. However, `&T` is allowed. In the following example, try swapping the usage of `Fn`, `FnMut`, and `FnOnce` to see what happens: diff --git a/src-en/fn/diverging.md b/src-en/fn/diverging.md index 19cf27c..caef7af 100644 --- a/src-en/fn/diverging.md +++ b/src-en/fn/diverging.md @@ -21,8 +21,8 @@ fn some_fn() { } fn main() { - let a: () = some_fn(); - println!("This function returns and you can see this line.") + let _a: () = some_fn(); + println!("This function returns and you can see this line."); } ``` @@ -37,10 +37,10 @@ fn main() { } ``` -Although this might seem like an abstract concept, it is in fact very useful and +Although this might seem like an abstract concept, it is actually very useful and often handy. The main advantage of this type is that it can be cast to any other -one and therefore used at places where an exact type is required, for instance -in `match` branches. This allows us to write code like this: +type, making it versatile in situations where an exact type is required, such as +in match branches. This flexibility allows us to write code like this: ```rust fn main() { @@ -66,4 +66,4 @@ fn main() { ``` It is also the return type of functions that loop forever (e.g. `loop {}`) like -network servers or functions that terminates the process (e.g. `exit()`). +network servers or functions that terminate the process (e.g. `exit()`). diff --git a/src-en/fn/hof.md b/src-en/fn/hof.md index 568c79d..9be5b41 100644 --- a/src-en/fn/hof.md +++ b/src-en/fn/hof.md @@ -10,7 +10,7 @@ fn is_odd(n: u32) -> bool { } fn main() { - println!("Find the sum of all the squared odd numbers under 1000"); + println!("Find the sum of all the numbers with odd squares under 1000"); let upper = 1000; // Imperative approach @@ -36,7 +36,7 @@ fn main() { (0..).map(|n| n * n) // All natural numbers squared .take_while(|&n_squared| n_squared < upper) // Below upper limit .filter(|&n_squared| is_odd(n_squared)) // That are odd - .fold(0, |acc, n_squared| acc + n_squared); // Sum them + .sum(); // Sum them println!("functional style: {}", sum_of_squared_odd_numbers); } ``` diff --git a/src-en/fn/methods.md b/src-en/fn/methods.md index 8e8c10c..bd5d997 100644 --- a/src-en/fn/methods.md +++ b/src-en/fn/methods.md @@ -1,8 +1,9 @@ -# Methods +# Associated functions & Methods -Methods are functions attached to objects. These methods have access to the -data of the object and its other methods via the `self` keyword. Methods are -defined under an `impl` block. +Some functions are connected to a particular type. These come in two forms: +associated functions, and methods. Associated functions are functions that +are defined on a type generally, while methods are associated functions that are +called on a particular instance of a type. ```rust,editable struct Point { @@ -10,16 +11,18 @@ struct Point { y: f64, } -// Implementation block, all `Point` methods go in here +// Implementation block, all `Point` associated functions & methods go in here impl Point { - // This is a static method - // Static methods don't need to be called by an instance - // These methods are generally used as constructors + // This is an "associated function" because this function is associated with + // a particular type, that is, Point. + // + // Associated functions don't need to be called with an instance. + // These functions are generally used like constructors. fn origin() -> Point { Point { x: 0.0, y: 0.0 } } - // Another static method, taking two arguments: + // Another associated function, taking two arguments: fn new(x: f64, y: f64) -> Point { Point { x: x, y: y } } @@ -31,7 +34,7 @@ struct Rectangle { } impl Rectangle { - // This is an instance method + // This is a method // `&self` is sugar for `self: &Self`, where `Self` is the type of the // caller object. In this case `Self` = `Rectangle` fn area(&self) -> f64 { @@ -80,12 +83,12 @@ impl Pair { fn main() { let rectangle = Rectangle { - // Static methods are called using double colons + // Associated functions are called using double colons p1: Point::origin(), p2: Point::new(3.0, 4.0), }; - // Instance methods are called using the dot operator + // Methods are called using the dot operator // Note that the first argument `&self` is implicitly passed, i.e. // `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)` println!("Rectangle perimeter: {}", rectangle.perimeter()); @@ -112,4 +115,4 @@ fn main() { //pair.destroy(); // TODO ^ Try uncommenting this line } -``` \ No newline at end of file +``` diff --git a/src-en/generics.md b/src-en/generics.md index 86ecc58..f0ddee8 100644 --- a/src-en/generics.md +++ b/src-en/generics.md @@ -2,15 +2,15 @@ *Generics* is the topic of generalizing types and functionalities to broader cases. This is extremely useful for reducing code duplication in many ways, -but can call for rather involving syntax. Namely, being generic requires -taking great care to specify over which types a generic type -is actually considered valid. The simplest and most common use of generics +but can call for rather involved syntax. Namely, being generic requires +taking great care to specify over which types a generic type +is actually considered valid. The simplest and most common use of generics is for type parameters. A type parameter is specified as generic by the use of angle brackets and upper [camel case][camelcase]: ``. "Generic type parameters" are typically represented as ``. In Rust, "generic" also describes anything that -accepts one or more generic type parameters ``. Any type specified as a +accepts one or more generic type parameters ``. Any type specified as a generic type parameter is generic, and everything else is concrete (non-generic). For example, defining a *generic function* named `foo` that takes an argument @@ -20,8 +20,8 @@ For example, defining a *generic function* named `foo` that takes an argument fn foo(arg: T) { ... } ``` -Because `T` has been specified as a generic type parameter using ``, it -is considered generic when used here as `(arg: T)`. This is the case even if `T` +Because `T` has been specified as a generic type parameter using ``, it +is considered generic when used here as `(arg: T)`. This is the case even if `T` has previously been defined as a `struct`. This example shows some of the syntax in action: diff --git a/src-en/generics/assoc_items.md b/src-en/generics/assoc_items.md index 7a3871b..92ab9c3 100644 --- a/src-en/generics/assoc_items.md +++ b/src-en/generics/assoc_items.md @@ -1,10 +1,10 @@ # Associated items "Associated Items" refers to a set of rules pertaining to [`item`][items]s -of various types. It is an extension to `trait` generics, and allows +of various types. It is an extension to `trait` generics, and allows `trait`s to internally define new items. -One such item is called an *associated type*, providing simpler usage +One such item is called an *associated type*, providing simpler usage patterns when the `trait` is generic over its container type. ### See also: diff --git a/src-en/generics/assoc_items/the_problem.md b/src-en/generics/assoc_items/the_problem.md index bc6f763..1b58dfd 100644 --- a/src-en/generics/assoc_items/the_problem.md +++ b/src-en/generics/assoc_items/the_problem.md @@ -3,13 +3,13 @@ A `trait` that is generic over its container type has type specification requirements - users of the `trait` *must* specify all of its generic types. -In the example below, the `Contains` `trait` allows the use of the generic -types `A` and `B`. The trait is then implemented for the `Container` type, +In the example below, the `Contains` `trait` allows the use of the generic +types `A` and `B`. The trait is then implemented for the `Container` type, specifying `i32` for `A` and `B` so that it can be used with `fn difference()`. -Because `Contains` is generic, we are forced to explicitly state *all* of the -generic types for `fn difference()`. In practice, we want a way to express that -`A` and `B` are determined by the *input* `C`. As you will see in the next +Because `Contains` is generic, we are forced to explicitly state *all* of the +generic types for `fn difference()`. In practice, we want a way to express that +`A` and `B` are determined by the *input* `C`. As you will see in the next section, associated types provide exactly that capability. ```rust,editable diff --git a/src-en/generics/assoc_items/types.md b/src-en/generics/assoc_items/types.md index 0358fce..dbf6d78 100644 --- a/src-en/generics/assoc_items/types.md +++ b/src-en/generics/assoc_items/types.md @@ -1,6 +1,6 @@ # Associated types -The use of "Associated types" improves the overall readability of code +The use of "Associated types" improves the overall readability of code by moving inner types locally into a trait as *output* types. Syntax for the `trait` definition is as follows: @@ -13,7 +13,7 @@ trait Contains { type B; // Updated syntax to refer to these new types generically. - fn contains(&self, &Self::A, &Self::B) -> bool; + fn contains(&self, _: &Self::A, _: &Self::B) -> bool; } ``` diff --git a/src-en/generics/bounds.md b/src-en/generics/bounds.md index 86e54e6..a3913f6 100644 --- a/src-en/generics/bounds.md +++ b/src-en/generics/bounds.md @@ -23,7 +23,7 @@ struct S(T); let s = S(vec![1]); ``` -Another effect of bounding is that generic instances are allowed to access the +Another effect of bounding is that generic instances are allowed to access the [methods] of traits specified in the bounds. For example: ```rust,editable diff --git a/src-en/generics/gen_fn.md b/src-en/generics/gen_fn.md index 2640576..f1985ff 100644 --- a/src-en/generics/gen_fn.md +++ b/src-en/generics/gen_fn.md @@ -3,9 +3,9 @@ The same set of rules can be applied to functions: a type `T` becomes generic when preceded by ``. -Using generic functions sometimes requires explicitly specifying type -parameters. This may be the case if the function is called where the return type -is generic, or if the compiler doesn't have enough information to infer +Using generic functions sometimes requires explicitly specifying type +parameters. This may be the case if the function is called where the return type +is generic, or if the compiler doesn't have enough information to infer the necessary type parameters. A function call with explicitly specified type parameters looks like: diff --git a/src-en/generics/impl.md b/src-en/generics/impl.md index 6955431..e39ed93 100644 --- a/src-en/generics/impl.md +++ b/src-en/generics/impl.md @@ -49,7 +49,6 @@ fn main() { [functions returning references][fn], [`impl`][methods], and [`struct`][structs] - [fn]: ../scope/lifetime/fn.md [methods]: ../fn/methods.md [specialization_plans]: https://blog.rust-lang.org/2015/05/11/traits.html#the-future diff --git a/src-en/generics/multi_bounds.md b/src-en/generics/multi_bounds.md index 5f51a6c..969ba64 100644 --- a/src-en/generics/multi_bounds.md +++ b/src-en/generics/multi_bounds.md @@ -1,6 +1,6 @@ # Multiple bounds -Multiple bounds can be applied with a `+`. Like normal, different types are +Multiple bounds for a single type can be applied with a `+`. Like normal, different types are separated with `,`. ```rust,editable diff --git a/src-en/generics/new_types.md b/src-en/generics/new_types.md index ccbbbb0..dc0b15b 100644 --- a/src-en/generics/new_types.md +++ b/src-en/generics/new_types.md @@ -17,7 +17,6 @@ impl Years { } } - impl Days { /// truncates partial years pub fn to_years(&self) -> Years { @@ -25,28 +24,30 @@ impl Days { } } -fn old_enough(age: &Years) -> bool { +fn is_adult(age: &Years) -> bool { age.0 >= 18 } fn main() { - let age = Years(5); + let age = Years(25); let age_days = age.to_days(); - println!("Old enough {}", old_enough(&age)); - println!("Old enough {}", old_enough(&age_days.to_years())); - // println!("Old enough {}", old_enough(&age_days)); + println!("Is an adult? {}", is_adult(&age)); + println!("Is an adult? {}", is_adult(&age_days.to_years())); + // println!("Is an adult? {}", is_adult(&age_days)); } ``` Uncomment the last print statement to observe that the type supplied must be `Years`. -To obtain the `newtype`'s value as the base type, you may use tuple syntax like so: +To obtain the `newtype`'s value as the base type, you may use the tuple or destructuring syntax like so: + ```rust, editable struct Years(i64); fn main() { let years = Years(42); - let years_as_primitive: i64 = years.0; + let years_as_primitive_1: i64 = years.0; // Tuple + let Years(years_as_primitive_2) = years; // Destructuring } ``` @@ -55,4 +56,3 @@ fn main() { [`structs`][struct] [struct]: ../custom_types/structs.md - diff --git a/src-en/generics/phantom.md b/src-en/generics/phantom.md index 59cf125..8743a3e 100644 --- a/src-en/generics/phantom.md +++ b/src-en/generics/phantom.md @@ -4,7 +4,7 @@ A phantom type parameter is one that doesn't show up at runtime, but is checked statically (and only) at compile time. Data types can use extra generic type parameters to act as markers -or to perform type checking at compile time. These extra parameters +or to perform type checking at compile time. These extra parameters hold no storage values, and have no runtime behavior. In the following example, we combine [std::marker::PhantomData] @@ -16,7 +16,7 @@ use std::marker::PhantomData; // A phantom tuple struct which is generic over `A` with hidden parameter `B`. #[derive(PartialEq)] // Allow equality test for this type. -struct PhantomTuple(A,PhantomData); +struct PhantomTuple(A, PhantomData); // A phantom type struct which is generic over `A` with hidden parameter `B`. #[derive(PartialEq)] // Allow equality test for this type. @@ -42,14 +42,14 @@ fn main() { first: 'Q', phantom: PhantomData, }; - + // Compile-time Error! Type mismatch so these cannot be compared: - //println!("_tuple1 == _tuple2 yields: {}", - // _tuple1 == _tuple2); - + // println!("_tuple1 == _tuple2 yields: {}", + // _tuple1 == _tuple2); + // Compile-time Error! Type mismatch so these cannot be compared: - //println!("_struct1 == _struct2 yields: {}", - // _struct1 == _struct2); + // println!("_struct1 == _struct2 yields: {}", + // _struct1 == _struct2); } ``` @@ -60,4 +60,4 @@ fn main() { [Derive]: ../trait/derive.md [struct]: ../custom_types/structs.md [TupleStructs]: ../custom_types/structs.md -[std::marker::PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html \ No newline at end of file +[std::marker::PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html diff --git a/src-en/generics/phantom/testcase_units.md b/src-en/generics/phantom/testcase_units.md index 00f59fe..c3b9cbd 100644 --- a/src-en/generics/phantom/testcase_units.md +++ b/src-en/generics/phantom/testcase_units.md @@ -40,7 +40,7 @@ struct Length(f64, PhantomData); /// The `Add` trait defines the behavior of the `+` operator. impl Add for Length { - type Output = Length; + type Output = Length; // add() returns a new `Length` struct containing the sum. fn add(self, rhs: Length) -> Length { diff --git a/src-en/generics/where.md b/src-en/generics/where.md index f35f03f..08e0734 100644 --- a/src-en/generics/where.md +++ b/src-en/generics/where.md @@ -1,8 +1,8 @@ # Where clauses A bound can also be expressed using a `where` clause immediately -before the opening `{`, rather than at the type's first mention. -Additionally, `where` clauses can apply bounds to arbitrary types, +before the opening `{`, rather than at the type's first mention. +Additionally, `where` clauses can apply bounds to arbitrary types, rather than just to type parameters. Some cases that a `where` clause is useful: @@ -18,7 +18,7 @@ impl MyTrait for YourType where D: TraitE + TraitF {} ``` -* When using a `where` clause is more expressive than using normal syntax. +* When using a `where` clause is more expressive than using normal syntax. The `impl` in this example cannot be directly expressed without a `where` clause: ```rust,editable diff --git a/src-en/hello.md b/src-en/hello.md index 2a8d315..4aaddeb 100644 --- a/src-en/hello.md +++ b/src-en/hello.md @@ -3,18 +3,19 @@ This is the source code of the traditional Hello World program. ```rust,editable -// This is a comment, and is ignored by the compiler +// This is a comment, and is ignored by the compiler. // You can test this code by clicking the "Run" button over there -> -// or if you prefer to use your keyboard, you can use the "Ctrl + Enter" shortcut +// or if you prefer to use your keyboard, you can use the "Ctrl + Enter" +// shortcut. // This code is editable, feel free to hack it! // You can always return to the original code by clicking the "Reset" button -> -// This is the main function +// This is the main function. fn main() { - // Statements here are executed when the compiled binary is called + // Statements here are executed when the compiled binary is called. - // Print text to the console + // Print text to the console. println!("Hello World!"); } ``` @@ -38,8 +39,7 @@ Hello World! ### Activity Click 'Run' above to see the expected output. Next, add a new -line with a second `println!` macro so that the output -shows: +line with a second `println!` macro so that the output shows: ```text Hello World! diff --git a/src-en/hello/comment.md b/src-en/hello/comment.md index 8cd1ccf..4ea6dcd 100644 --- a/src-en/hello/comment.md +++ b/src-en/hello/comment.md @@ -4,30 +4,29 @@ Any program requires comments, and Rust supports a few different varieties: * *Regular comments* which are ignored by the compiler: - * `// Line comments which go to the end of the line.` - * `/* Block comments which go to the closing delimiter. */` -* *Doc comments* which are parsed into HTML library - [documentation][docs]: - * `/// Generate library docs for the following item.` - * `//! Generate library docs for the enclosing item.` + * `// Line comments which go to the end of the line.` + * `/* Block comments which go to the closing delimiter. */` +* *Doc comments* which are parsed into HTML library [documentation][docs]: + * `/// Generate library docs for the following item.` + * `//! Generate library docs for the enclosing item.` ```rust,editable fn main() { - // This is an example of a line comment - // There are two slashes at the beginning of the line - // And nothing written inside these will be read by the compiler + // This is an example of a line comment. + // There are two slashes at the beginning of the line. + // And nothing written after these will be read by the compiler. // println!("Hello, world!"); // Run it. See? Now try deleting the two slashes, and run it again. - /* + /* * This is another type of comment, a block comment. In general, - * line comments are the recommended comment style. But - * block comments are extremely useful for temporarily disabling - * chunks of code. /* Block comments can be /* nested, */ */ - * so it takes only a few keystrokes to comment out everything - * in this main() function. /*/*/* Try it yourself! */*/*/ + * line comments are the recommended comment style. But block comments + * are extremely useful for temporarily disabling chunks of code. + * /* Block comments can be /* nested, */ */ so it takes only a few + * keystrokes to comment out everything in this main() function. + * /*/*/* Try it yourself! */*/*/ */ /* @@ -41,7 +40,6 @@ fn main() { let x = 5 + /* 90 + */ 5; println!("Is `x` 10 or 100? x = {}", x); } - ``` ### See also: diff --git a/src-en/hello/print.md b/src-en/hello/print.md index e09f128..b7ab862 100644 --- a/src-en/hello/print.md +++ b/src-en/hello/print.md @@ -1,13 +1,15 @@ # Formatted print -Printing is handled by a series of [`macros`][macros] defined in [`std::fmt`][fmt] -some of which include: +Printing is handled by a series of [`macros`][macros] defined in +[`std::fmt`][fmt] some of which are: * `format!`: write formatted text to [`String`][string] -* `print!`: same as `format!` but the text is printed to the console (io::stdout). +* `print!`: same as `format!` but the text is printed to the console + (io::stdout). * `println!`: same as `print!` but a newline is appended. -* `eprint!`: same as `format!` but the text is printed to the standard error (io::stderr). -* `eprintln!`: same as `eprint!`but a newline is appended. +* `eprint!`: same as `print!` but the text is printed to the standard error + (io::stderr). +* `eprintln!`: same as `eprint!` but a newline is appended. All parse text in the same fashion. As a plus, Rust checks formatting correctness at compile time. @@ -18,11 +20,9 @@ fn main() { // arguments. These will be stringified. println!("{} days", 31); - // Without a suffix, 31 becomes an i32. You can change what type 31 is - // by providing a suffix. The number 31i64 for example has the type i64. - - // There are various optional patterns this works with. Positional - // arguments can be used. + // Positional arguments can be used. Specifying an integer inside `{}` + // determines which additional argument will be replaced. Arguments start + // at 0 immediately after the format string. println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); // As can named arguments. @@ -31,29 +31,46 @@ fn main() { subject="the quick brown fox", verb="jumps over"); - // Special formatting can be specified after a `:`. - println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + // Different formatting can be invoked by specifying the format character + // after a `:`. + println!("Base 10: {}", 69420); // 69420 + println!("Base 2 (binary): {:b}", 69420); // 10000111100101100 + println!("Base 8 (octal): {:o}", 69420); // 207454 + println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c + + // You can right-justify text with a specified width. This will + // output " 1". (Four white spaces and a "1", for a total width of 5.) + println!("{number:>5}", number=1); - // You can right-align text with a specified width. This will output - // " 1". 5 white spaces and a "1". - println!("{number:>width$}", number=1, width=6); + // You can pad numbers with extra zeroes, + println!("{number:0>5}", number=1); // 00001 + // and left-adjust by flipping the sign. This will output "10000". + println!("{number:0<5}", number=1); // 10000 - // You can pad numbers with extra zeroes. This will output "000001". - println!("{number:>0width$}", number=1, width=6); + // You can use named arguments in the format specifier by appending a `$`. + println!("{number:0>width$}", number=1, width=5); - // Rust even checks to make sure the correct number of arguments are - // used. + // Rust even checks to make sure the correct number of arguments are used. println!("My name is {0}, {1} {0}", "Bond"); // FIXME ^ Add the missing argument: "James" - // Create a structure named `Structure` which contains an `i32`. - #[allow(dead_code)] + // Only types that implement fmt::Display can be formatted with `{}`. User- + // defined types do not implement fmt::Display by default. + + #[allow(dead_code)] // disable `dead_code` which warn against unused module struct Structure(i32); - // However, custom types such as this structure require more complicated - // handling. This will not work. - println!("This struct `{}` won't print...", Structure(3)); - // FIXME ^ Comment out this line. + // This will not compile because `Structure` does not implement + // fmt::Display. + // println!("This struct `{}` won't print...", Structure(3)); + // TODO ^ Try uncommenting this line + + // For Rust 1.58 and above, you can directly capture the argument from a + // surrounding variable. Just like the above, this will output + // " 1", 4 white spaces and a "1". + let number: f64 = 1.0; + let width: usize = 5; + println!("{number:>width$}"); } ``` @@ -62,7 +79,7 @@ of text. The base form of two important ones are listed below: * `fmt::Debug`: Uses the `{:?}` marker. Format text for debugging purposes. * `fmt::Display`: Uses the `{}` marker. Format text in a more elegant, user -friendly fashion. + friendly fashion. Here, we used `fmt::Display` because the std library provides implementations for these types. To print text for custom types, more steps are required. @@ -70,20 +87,22 @@ for these types. To print text for custom types, more steps are required. Implementing the `fmt::Display` trait automatically implements the [`ToString`] trait which allows us to [convert] the type to [`String`][string]. +In *line 43*, `#[allow(dead_code)]` is an [attribute] which only applies to the module after it. + ### Activities - * Fix the two issues in the above code (see FIXME) so that it runs without - error. - * Add a `println!` macro that prints: `Pi is roughly 3.142` by controlling - the number of decimal places shown. For the purposes of this exercise, - use `let pi = 3.141592` as an estimate for pi. (Hint: you may need to - check the [`std::fmt`][fmt] documentation for setting the number of - decimals to display) +* Fix the issue in the above code (see FIXME) so that it runs without + error. +* Try uncommenting the line that attempts to format the `Structure` struct + (see TODO) +* Add a `println!` macro call that prints: `Pi is roughly 3.142` by controlling + the number of decimal places shown. For the purposes of this exercise, use + `let pi = 3.141592` as an estimate for pi. (Hint: you may need to check the + [`std::fmt`][fmt] documentation for setting the number of decimals to display) ### See also: -[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], -and [`traits`][traits] +[`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], [`traits`][traits], and [`dead_code`][dead_code] [fmt]: https://doc.rust-lang.org/std/fmt/ [macros]: ../macros.md @@ -92,3 +111,5 @@ and [`traits`][traits] [traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html [convert]: ../conversion/string.md +[attribute]: ../attribute.md +[dead_code]: ../attribute/unused.md diff --git a/src-en/hello/print/fmt.md b/src-en/hello/print/fmt.md index dd698d2..b6f6760 100644 --- a/src-en/hello/print/fmt.md +++ b/src-en/hello/print/fmt.md @@ -3,8 +3,7 @@ We've seen that formatting is specified via a *format string*: * `format!("{}", foo)` -> `"3735928559"` -* `format!("0x{:X}", foo)` -> - [`"0xDEADBEEF"`][deadbeef] +* `format!("0x{:X}", foo)` -> [`"0xDEADBEEF"`][deadbeef] * `format!("0o{:o}", foo)` -> `"0o33653337357"` The same variable (`foo`) can be formatted differently depending on which @@ -26,13 +25,13 @@ struct City { } impl Display for City { - // `f` is a buffer, and this method must write the formatted string into it + // `f` is a buffer, and this method must write the formatted string into it. fn fmt(&self, f: &mut Formatter) -> fmt::Result { let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' }; let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' }; // `write!` is like `format!`, but it will write the formatted string - // into a buffer (the first argument) + // into a buffer (the first argument). write!(f, "{}: {:.3}°{} {:.3}°{}", self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c) } @@ -50,17 +49,17 @@ fn main() { City { name: "Dublin", lat: 53.347778, lon: -6.259722 }, City { name: "Oslo", lat: 59.95, lon: 10.75 }, City { name: "Vancouver", lat: 49.25, lon: -123.1 }, - ].iter() { - println!("{}", *city); + ] { + println!("{}", city); } for color in [ Color { red: 128, green: 255, blue: 90 }, Color { red: 0, green: 3, blue: 254 }, Color { red: 0, green: 0, blue: 0 }, - ].iter() { + ] { // Switch this to use {} once you've added an implementation // for fmt::Display. - println!("{:?}", *color); + println!("{:?}", color); } } ``` @@ -69,6 +68,7 @@ You can view a [full list of formatting traits][fmt_traits] and their argument types in the [`std::fmt`][fmt] documentation. ### Activity + Add an implementation of the `fmt::Display` trait for the `Color` struct above so that the output displays as: @@ -78,14 +78,19 @@ RGB (0, 3, 254) 0x0003FE RGB (0, 0, 0) 0x000000 ``` -Two hints if you get stuck: - * You [may need to list each color more than once][named_parameters], - * You can [pad with zeros to a width of 2][fmt_width] with `:02`. +Three hints if you get stuck: + +* The formula for calculating a color in the RGB color space is: +`RGB = (R*65536)+(G*256)+B , (when R is RED, G is GREEN and B is BLUE)`. +For more see [RGB color format & calculation][rgb_color]. +* You [may need to list each color more than once][named_parameters]. +* You can [pad with zeros to a width of 2][fmt_width] with `:0>2`. ### See also: [`std::fmt`][fmt] +[rgb_color]: https://www.rapidtables.com/web/color/RGB_Color.html#rgb-format [named_parameters]: https://doc.rust-lang.org/std/fmt/#named-parameters [deadbeef]: https://en.wikipedia.org/wiki/Deadbeef#Magic_debug_values [fmt]: https://doc.rust-lang.org/std/fmt/ diff --git a/src-en/hello/print/print_debug.md b/src-en/hello/print/print_debug.md index 161b0b7..715366a 100644 --- a/src-en/hello/print/print_debug.md +++ b/src-en/hello/print/print_debug.md @@ -43,15 +43,15 @@ fn main() { // `Structure` is printable! println!("Now {:?} will print!", Structure(3)); - + // The problem with `derive` is there is no control over how // the results look. What if I want this to just show a `7`? println!("Now {:?} will print!", Deep(Structure(7))); } ``` -So `fmt::Debug` definitely makes this printable but sacrifices some -elegance. Rust also provides "pretty printing" with `{:#?}`. +So `fmt::Debug` definitely makes this printable but sacrifices some elegance. +Rust also provides "pretty printing" with `{:#?}`. ```rust,editable #[derive(Debug)] @@ -81,4 +81,3 @@ and [`struct`][structs] [derive]: ../../trait/derive.md [fmt]: https://doc.rust-lang.org/std/fmt/ [structs]: ../../custom_types/structs.md - diff --git a/src-en/hello/print/print_display.md b/src-en/hello/print/print_display.md index 1ff31f2..5d8a45b 100644 --- a/src-en/hello/print/print_display.md +++ b/src-en/hello/print/print_display.md @@ -41,7 +41,7 @@ or for any other generic containers. `fmt::Debug` must then be used for these generic cases. This is not a problem though because for any new *container* type which is -*not* generic,`fmt::Display` can be implemented. +*not* generic, `fmt::Display` can be implemented. ```rust,editable use std::fmt; // Import `fmt` @@ -66,7 +66,7 @@ struct Point2D { y: f64, } -// Similarly, implement `Display` for `Point2D` +// Similarly, implement `Display` for `Point2D`. impl fmt::Display for Point2D { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Customize so only `x` and `y` are denoted. @@ -100,15 +100,14 @@ fn main() { } ``` -So, `fmt::Display` has been implemented but `fmt::Binary` has not, and -therefore cannot be used. `std::fmt` has many such [`traits`][traits] and -each requires its own implementation. This is detailed further in -[`std::fmt`][fmt]. +So, `fmt::Display` has been implemented but `fmt::Binary` has not, and therefore +cannot be used. `std::fmt` has many such [`traits`][traits] and each requires +its own implementation. This is detailed further in [`std::fmt`][fmt]. ### Activity After checking the output of the above example, use the `Point2D` struct as a -guide to add a Complex struct to the example. When printed in the same +guide to add a `Complex` struct to the example. When printed in the same way, the output should be: ```txt @@ -125,5 +124,5 @@ Debug: Complex { real: 3.3, imag: 7.2 } [fmt]: https://doc.rust-lang.org/std/fmt/ [macros]: ../../macros.md [structs]: ../../custom_types/structs.md -[traits]: ../../trait.md +[traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits [use]: ../../mod/use.md diff --git a/src-en/hello/print/print_display/testcase_list.md b/src-en/hello/print/print_display/testcase_list.md index 63e400b..d07316b 100644 --- a/src-en/hello/print/print_display/testcase_list.md +++ b/src-en/hello/print/print_display/testcase_list.md @@ -52,7 +52,8 @@ fn main() { ### Activity -Try changing the program so that the index of each element in the vector is also printed. The new output should look like this: +Try changing the program so that the index of each element in the vector is also +printed. The new output should look like this: ```rust,ignore [0: 1, 1: 2, 2: 3] diff --git a/src-en/index.md b/src-en/index.md index fecc190..51216fc 100644 --- a/src-en/index.md +++ b/src-en/index.md @@ -1,12 +1,12 @@ # Rust by Example [Rust][rust] is a modern systems programming language focusing on safety, speed, -and concurrency. It accomplishes these goals by being memory safe without using +and concurrency. It accomplishes these goals by being memory safe without using garbage collection. Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries. To get even more out of these examples, don't forget -to [install Rust locally][install] and check out the [official docs][std]. +to [install Rust locally][install] and check out the [official docs][std]. Additionally for the curious, you can also [check out the source code for this site][home]. Now let's begin! @@ -21,13 +21,13 @@ Now let's begin! - [Types](types.md) - Learn about changing and defining types. -- [Conversion](conversion.md) +- [Conversion](conversion.md) - Convert between different types, such as strings, integers, and floats. -- [Expressions](expression.md) +- [Expressions](expression.md) - Learn about Expressions & how to use them. - [Flow of Control](flow_control.md) - `if`/`else`, `for`, and others. -- [Functions](fn.md) - Learn about Methods, Closures and High Order Functions. +- [Functions](fn.md) - Learn about Methods, Closures and Higher Order Functions. - [Modules](mod.md) - Organize code using modules @@ -43,7 +43,7 @@ Now let's begin! - [Traits](trait.md) - A trait is a collection of methods defined for an unknown type: `Self` -- [Macros](macros.md) +- [Macros](macros.md) - Macros are a way of writing code that writes other code, which is known as metaprogramming. - [Error handling](error.md) - Learn Rust way of handling failures. @@ -53,13 +53,12 @@ Now let's begin! - [Testing](testing.md) - All sorts of testing in Rust. -- [Unsafe Operations](unsafe.md) +- [Unsafe Operations](unsafe.md) - Learn about entering a block of unsafe operations. -- [Compatibility](compatibility.md) +- [Compatibility](compatibility.md) - Handling Rust's evolution and potential compatibility issues. - [Meta](meta.md) - Documentation, Benchmarking. - [rust]: https://www.rust-lang.org/ [install]: https://www.rust-lang.org/tools/install [std]: https://doc.rust-lang.org/std/ diff --git a/src-en/macros.md b/src-en/macros.md index ffeb923..169d2e4 100644 --- a/src-en/macros.md +++ b/src-en/macros.md @@ -1,4 +1,4 @@ -# macro_rules! +# `macro_rules!` Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name @@ -16,12 +16,12 @@ macro_rules! say_hello { // `()` indicates that the macro takes no argument. () => { // The macro will expand into the contents of this block. - println!("Hello!"); + println!("Hello!") }; } fn main() { - // This call will expand into `println!("Hello");` + // This call will expand into `println!("Hello!")` say_hello!() } ``` @@ -37,4 +37,4 @@ So why are macros useful? 3. Variadic interfaces. Sometimes you want to define an interface that takes a variable number of arguments. An example is `println!` which could take any - number of arguments, depending on the format string!. (More on this later) + number of arguments, depending on the format string. (More on this later) diff --git a/src-en/macros/dsl.md b/src-en/macros/dsl.md index fb0c235..d83885a 100644 --- a/src-en/macros/dsl.md +++ b/src-en/macros/dsl.md @@ -10,12 +10,12 @@ an expression and have the output printed to console. ```rust,editable macro_rules! calculate { - (eval $e:expr) => {{ + (eval $e:expr) => { { - let val: usize = $e; // Force types to be integers + let val: usize = $e; // Force types to be unsigned integers println!("{} = {}", stringify!{$e}, val); } - }}; + }; } fn main() { diff --git a/src-en/macros/overload.md b/src-en/macros/overload.md index 84d341a..090af6f 100644 --- a/src-en/macros/overload.md +++ b/src-en/macros/overload.md @@ -1,6 +1,6 @@ # Overload -Macros can be overloaded to accept different combinations of arguments. +Macros can be overloaded to accept different combinations of arguments. In that regard, `macro_rules!` can work similarly to a match block: ```rust,editable @@ -28,4 +28,4 @@ fn main() { test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32); test!(true; or false); } -``` \ No newline at end of file +``` diff --git a/src-en/macros/repeat.md b/src-en/macros/repeat.md index 04c3b7d..3b8a2b6 100644 --- a/src-en/macros/repeat.md +++ b/src-en/macros/repeat.md @@ -21,8 +21,8 @@ macro_rules! find_min { } fn main() { - println!("{}", find_min!(1u32)); - println!("{}", find_min!(1u32 + 2, 2u32)); - println!("{}", find_min!(5u32, 2u32 * 3, 4u32)); + println!("{}", find_min!(1)); + println!("{}", find_min!(1 + 2, 2)); + println!("{}", find_min!(5, 2 * 3, 4)); } ``` diff --git a/src-en/macros/variadics.md b/src-en/macros/variadics.md index e0cb1c1..0ba36e6 100644 --- a/src-en/macros/variadics.md +++ b/src-en/macros/variadics.md @@ -9,12 +9,12 @@ We can extend our `calculate!` macro from the previous section to be variadic: ```rust,editable macro_rules! calculate { // The pattern for a single `eval` - (eval $e:expr) => {{ + (eval $e:expr) => { { let val: usize = $e; // Force types to be integers println!("{} = {}", stringify!{$e}, val); } - }}; + }; // Decompose multiple `eval`s recursively (eval $e:expr, $(eval $es:expr),+) => {{ diff --git a/src-en/meta.md b/src-en/meta.md index a743452..77a42ad 100644 --- a/src-en/meta.md +++ b/src-en/meta.md @@ -1,12 +1,12 @@ # Meta -Some topics aren't exactly relevant to how you program but provide you +Some topics aren't exactly relevant to how you program runs but provide you tooling or infrastructure support which just makes things better for everyone. These topics include: - [Documentation][doc]: Generate library documentation for users via the included `rustdoc`. -- [Playpen][playpen]: Integrate the Rust Playpen(also known as the Rust Playground) in your documentation. +- [Playground][playground]: Integrate the Rust Playground in your documentation. [doc]: meta/doc.md -[playpen]: meta/playpen.md +[playground]: meta/playground.md diff --git a/src-en/meta/doc.md b/src-en/meta/doc.md index 63e0b41..6b6f9d0 100644 --- a/src-en/meta/doc.md +++ b/src-en/meta/doc.md @@ -1,8 +1,10 @@ # Documentation -Use `cargo doc` to build documentation in `target/doc`. +Use `cargo doc` to build documentation in `target/doc`, `cargo doc --open` +will automatically open it in your web browser. -Use `cargo test` to run all tests (including documentation tests), and `cargo test --doc` to only run documentation tests. +Use `cargo test` to run all tests (including documentation tests), and `cargo +test --doc` to only run documentation tests. These commands will appropriately invoke `rustdoc` (and `rustc`) as required. @@ -22,11 +24,7 @@ pub struct Person { } impl Person { - /// Returns a person with the name given them - /// - /// # Arguments - /// - /// * `name` - A string slice that holds the name of the person + /// Creates a person with the given name. /// /// # Examples /// @@ -44,8 +42,8 @@ impl Person { /// Gives a friendly hello! /// - /// Says "Hello, [name]" to the `Person` it is called on. - pub fn hello(& self) { + /// Says "Hello, [name](Person::name)" to the `Person` it is called on. + pub fn hello(&self) { println!("Hello, {}!", self.name); } } @@ -67,7 +65,8 @@ $ rustdoc --test --extern doc="libdoc.rlib" doc.rs ## Doc attributes -Below are a few examples of the most common `#[doc]` attributes used with `rustdoc`. +Below are a few examples of the most common `#[doc]` attributes used with +`rustdoc`. ### `inline` @@ -78,7 +77,7 @@ Used to inline docs, instead of linking out to separate page. pub use bar::Bar; /// bar docs -mod bar { +pub mod bar { /// the docs for Bar pub struct Bar; } @@ -104,7 +103,8 @@ Using this tells `rustdoc` not to include this in documentation: pub use self::async_await::*; ``` -For documentation, `rustdoc` is widely used by the community. It's what is used to generate the [std library docs](https://doc.rust-lang.org/std/). +For documentation, `rustdoc` is widely used by the community. It's what is used +to generate the [std library docs](https://doc.rust-lang.org/std/). ### See also: diff --git a/src-en/meta/playground.md b/src-en/meta/playground.md new file mode 100644 index 0000000..8bcbc4d --- /dev/null +++ b/src-en/meta/playground.md @@ -0,0 +1,60 @@ +# Playground + +The [Rust Playground](https://play.rust-lang.org/) is a way to experiment with +Rust code through a web interface. + +## Using it with `mdbook` + +In [`mdbook`][mdbook], you can make code examples playable and editable. + +```rust,editable +fn main() { + println!("Hello World!"); +} +``` + +This allows the reader to both run your code sample, but also modify and tweak +it. The key here is the adding of the word `editable` to your codefence block +separated by a comma. + +````markdown +```rust,editable +//...place your code here +``` +```` + +Additionally, you can add `ignore` if you want `mdbook` to skip your code when +it builds and tests. + +````markdown +```rust,editable,ignore +//...place your code here +``` +```` + +## Using it with docs + +You may have noticed in some of the [official Rust docs][official-rust-docs] a +button that says "Run", which opens the code sample up in a new tab in Rust +Playground. This feature is enabled if you use the `#[doc]` attribute called +[`html_playground_url`][html-playground-url]. + +```text +#![doc(html_playground_url = "https://play.rust-lang.org/")] +//! ``` +//! println!("Hello World"); +//! ``` +``` + +### See also: + +- [The Rust Playground][rust-playground] +- [The Rust Playground On Github][rust-playground-github] +- [The rustdoc Book][rustdoc-book] + +[rust-playground]: https://play.rust-lang.org/ +[rust-playground-github]: https://github.com/integer32llc/rust-playground/ +[mdbook]: https://github.com/rust-lang/mdBook +[official-rust-docs]: https://doc.rust-lang.org/core/ +[rustdoc-book]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html +[html-playground-url]: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#html_playground_url diff --git a/src-en/meta/playpen.md b/src-en/meta/playpen.md deleted file mode 100644 index a125f13..0000000 --- a/src-en/meta/playpen.md +++ /dev/null @@ -1,46 +0,0 @@ -# Playpen - -The [Rust Playpen](https://github.com/rust-lang/rust-playpen) is a way to experiment with Rust code through a web interface. This project is now commonly referred to as [Rust Playground](https://play.rust-lang.org/). - -## Using it with `mdbook` - -In [`mdbook`][mdbook], you can make code examples playable and editable. - -```rust,editable -fn main() { - println!("Hello World!"); -} -``` - -This allows the reader to both run your code sample, but also modify and tweak it. The key here is the adding the word `editable` to your codefence block separated by a comma. - -````markdown -```rust,editable -//...place your code here -``` -```` - -Additionally, you can add `ignore` if you want `mdbook` to skip your code when it builds and tests. - -````markdown -```rust,editable,ignore -//...place your code here -``` -```` - -## Using it with docs - -You may have noticed in some of the [official Rust docs][official-rust-docs] a button that says "Run", which opens the code sample up in a new tab in Rust Playground. This feature is enabled if you use the #[doc] attribute called [`html_playground_url`][html-playground-url]. - -### See also: - -- [The Rust Playground][rust-playground] -- [The next-gen playpen][next-gen-playpen] -- [The rustdoc Book][rustdoc-book] - -[rust-playground]: https://play.rust-lang.org/ -[next-gen-playpen]: https://github.com/integer32llc/rust-playground/ -[mdbook]: https://github.com/rust-lang/mdBook -[official-rust-docs]: https://doc.rust-lang.org/core/ -[rustdoc-book]: https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html -[html-playground-url]: https://doc.rust-lang.org/rustdoc/the-doc-attribute.html#html_playground_url diff --git a/src-en/mod.md b/src-en/mod.md index f9a1183..5066007 100644 --- a/src-en/mod.md +++ b/src-en/mod.md @@ -5,4 +5,4 @@ code in logical units (modules), and manage visibility (public/private) between them. A module is a collection of items: functions, structs, traits, `impl` blocks, -and even other modules. \ No newline at end of file +and even other modules. diff --git a/src-en/mod/split.md b/src-en/mod/split.md index 634e7ef..2c78ee2 100644 --- a/src-en/mod/split.md +++ b/src-en/mod/split.md @@ -6,17 +6,17 @@ Modules can be mapped to a file/directory hierarchy. Let's break down the ```shell $ tree . . -|-- my -| |-- inaccessible.rs -| |-- mod.rs -| `-- nested.rs -`-- split.rs +├── my +│   ├── inaccessible.rs +│   └── nested.rs +├── my.rs +└── split.rs ``` In `split.rs`: ```rust,ignore -// This declaration will look for a file named `my.rs` or `my/mod.rs` and will +// This declaration will look for a file named `my.rs` and will // insert its contents inside a module named `my` under this scope mod my; @@ -36,7 +36,7 @@ fn main() { ``` -In `my/mod.rs`: +In `my.rs`: ```rust,ignore // Similarly `mod inaccessible` and `mod nested` will locate the `nested.rs` diff --git a/src-en/mod/struct_visibility.md b/src-en/mod/struct_visibility.md index e735941..118ce1e 100644 --- a/src-en/mod/struct_visibility.md +++ b/src-en/mod/struct_visibility.md @@ -1,8 +1,8 @@ # Struct visibility -Structs have an extra level of visibility with their fields. The visibility -defaults to private, and can be overridden with the `pub` modifier. This -visibility only matters when a struct is accessed from outside the module +Structs have an extra level of visibility with their fields. The visibility +defaults to private, and can be overridden with the `pub` modifier. This +visibility only matters when a struct is accessed from outside the module where it is defined, and has the goal of hiding information (encapsulation). ```rust,editable @@ -13,7 +13,6 @@ mod my { } // A public struct with a private field of generic type `T` - #[allow(dead_code)] pub struct ClosedBox { contents: T, } @@ -56,4 +55,4 @@ fn main() { [generics][generics] and [methods][methods] [generics]: ../generics.md -[methods]: ../fn/methods.md \ No newline at end of file +[methods]: ../fn/methods.md diff --git a/src-en/primitives.md b/src-en/primitives.md index 6b579e5..cdbee02 100644 --- a/src-en/primitives.md +++ b/src-en/primitives.md @@ -2,28 +2,27 @@ Rust provides access to a wide variety of `primitives`. A sample includes: - ### Scalar Types -* signed integers: `i8`, `i16`, `i32`, `i64`, `i128` and `isize` (pointer size) -* unsigned integers: `u8`, `u16`, `u32`, `u64`, `u128` and `usize` (pointer +* Signed integers: `i8`, `i16`, `i32`, `i64`, `i128` and `isize` (pointer size) +* Unsigned integers: `u8`, `u16`, `u32`, `u64`, `u128` and `usize` (pointer size) -* floating point: `f32`, `f64` +* Floating point: `f32`, `f64` * `char` Unicode scalar values like `'a'`, `'α'` and `'∞'` (4 bytes each) * `bool` either `true` or `false` -* and the unit type `()`, whose only possible value is an empty tuple: `()` +* The unit type `()`, whose only possible value is an empty tuple: `()` -Despite the value of a unit type being a tuple, it is not considered a -compound type because it does not contain multiple values. +Despite the value of a unit type being a tuple, it is not considered a compound +type because it does not contain multiple values. ### Compound Types -* arrays like `[1, 2, 3]` -* tuples like `(1, true)` +* Arrays like `[1, 2, 3]` +* Tuples like `(1, true)` -Variables can always be *type annotated*. Numbers may additionally be -annotated via a *suffix* or *by default*. Integers default to `i32` and -floats to `f64`. Note that Rust can also infer types from context. +Variables can always be *type annotated*. Numbers may additionally be annotated +via a *suffix* or *by default*. Integers default to `i32` and floats to `f64`. +Note that Rust can also infer types from context. ```rust,editable,ignore,mdbook-runnable fn main() { @@ -36,26 +35,36 @@ fn main() { // Or a default will be used. let default_float = 3.0; // `f64` let default_integer = 7; // `i32` - - // A type can also be inferred from context - let mut inferred_type = 12; // Type i64 is inferred from another line + + // A type can also be inferred from context. + let mut inferred_type = 12; // Type i64 is inferred from another line. inferred_type = 4294967296i64; - + // A mutable variable's value can be changed. let mut mutable = 12; // Mutable `i32` mutable = 21; - + // Error! The type of a variable can't be changed. mutable = true; - + // Variables can be overwritten with shadowing. let mutable = true; + + /* Compound types - Array and Tuple */ + + // Array signature consists of Type T and length as [T; length]. + let my_array: [i32; 5] = [1, 2, 3, 4, 5]; + + // Tuple is a collection of values of different types + // and is constructed using parentheses (). + let my_tuple = (5u32, 1u8, true, -5.04f32); } ``` ### See also: -[the `std` library][std], [`mut`][mut], [`inference`][inference], and [`shadowing`][shadowing] +[the `std` library][std], [`mut`][mut], [`inference`][inference], and +[`shadowing`][shadowing] [std]: https://doc.rust-lang.org/std/ [mut]: variable_bindings/mut.md diff --git a/src-en/primitives/array.md b/src-en/primitives/array.md index 50ea52f..e1c303a 100644 --- a/src-en/primitives/array.md +++ b/src-en/primitives/array.md @@ -5,50 +5,67 @@ memory. Arrays are created using brackets `[]`, and their length, which is known at compile time, is part of their type signature `[T; length]`. Slices are similar to arrays, but their length is not known at compile time. -Instead, a slice is a two-word object, the first word is a pointer to the data, -and the second word is the length of the slice. The word size is the same as -usize, determined by the processor architecture eg 64 bits on an x86-64. -Slices can be used to borrow a section of an array, and have the type signature -`&[T]`. +Instead, a slice is a two-word object; the first word is a pointer to the data, +the second word is the length of the slice. The word size is the same as usize, +determined by the processor architecture, e.g. 64 bits on an x86-64. Slices can +be used to borrow a section of an array and have the type signature `&[T]`. ```rust,editable,ignore,mdbook-runnable use std::mem; -// This function borrows a slice +// This function borrows a slice. fn analyze_slice(slice: &[i32]) { - println!("first element of the slice: {}", slice[0]); - println!("the slice has {} elements", slice.len()); + println!("First element of the slice: {}", slice[0]); + println!("The slice has {} elements", slice.len()); } fn main() { - // Fixed-size array (type signature is superfluous) + // Fixed-size array (type signature is superfluous). let xs: [i32; 5] = [1, 2, 3, 4, 5]; - // All elements can be initialized to the same value + // All elements can be initialized to the same value. let ys: [i32; 500] = [0; 500]; - // Indexing starts at 0 - println!("first element of the array: {}", xs[0]); - println!("second element of the array: {}", xs[1]); + // Indexing starts at 0. + println!("First element of the array: {}", xs[0]); + println!("Second element of the array: {}", xs[1]); - // `len` returns the count of elements in the array - println!("number of elements in array: {}", xs.len()); + // `len` returns the count of elements in the array. + println!("Number of elements in array: {}", xs.len()); - // Arrays are stack allocated - println!("array occupies {} bytes", mem::size_of_val(&xs)); + // Arrays are stack allocated. + println!("Array occupies {} bytes", mem::size_of_val(&xs)); - // Arrays can be automatically borrowed as slices - println!("borrow the whole array as a slice"); + // Arrays can be automatically borrowed as slices. + println!("Borrow the whole array as a slice."); analyze_slice(&xs); - // Slices can point to a section of an array - // They are of the form [starting_index..ending_index] - // starting_index is the first position in the slice - // ending_index is one more than the last position in the slice - println!("borrow a section of the array as a slice"); + // Slices can point to a section of an array. + // They are of the form [starting_index..ending_index]. + // `starting_index` is the first position in the slice. + // `ending_index` is one more than the last position in the slice. + println!("Borrow a section of the array as a slice."); analyze_slice(&ys[1 .. 4]); - // Out of bound indexing causes compile error - println!("{}", xs[5]); + // Example of empty slice `&[]`: + let empty_array: [u32; 0] = []; + assert_eq!(&empty_array, &[]); + assert_eq!(&empty_array, &[][..]); // Same but more verbose + + // Arrays can be safely accessed using `.get`, which returns an + // `Option`. This can be matched as shown below, or used with + // `.expect()` if you would like the program to exit with a nice + // message instead of happily continue. + for i in 0..xs.len() + 1 { // Oops, one element too far! + match xs.get(i) { + Some(xval) => println!("{}: {}", i, xval), + None => println!("Slow down! {} is too far!", i), + } + } + + // Out of bound indexing on array with constant value causes compile time error. + //println!("{}", xs[5]); + // Out of bound indexing on slice causes runtime error. + //println!("{}", xs[..][5]); } ``` diff --git a/src-en/primitives/literals.md b/src-en/primitives/literals.md index 4927e6a..dfe4e3a 100644 --- a/src-en/primitives/literals.md +++ b/src-en/primitives/literals.md @@ -9,12 +9,15 @@ notation using these prefixes respectively: `0x`, `0o` or `0b`. Underscores can be inserted in numeric literals to improve readability, e.g. `1_000` is the same as `1000`, and `0.000_001` is the same as `0.000001`. +Rust also supports scientific [E-notation][enote], e.g. `1e6`, `7.6e-4`. The +associated type is `f64`. + We need to tell the compiler the type of the literals we use. For now, we'll use the `u32` suffix to indicate that the literal is an unsigned 32-bit integer, and the `i32` suffix to indicate that it's a signed 32-bit integer. -The operators available and their precedence [in Rust][rust op-prec] are similar to other -[C-like languages][op-prec]. +The operators available and their precedence [in Rust][rust op-prec] are similar +to other [C-like languages][op-prec]. ```rust,editable fn main() { @@ -25,6 +28,9 @@ fn main() { println!("1 - 2 = {}", 1i32 - 2); // TODO ^ Try changing `1i32` to `1u32` to see why the type is important + // Scientific notation + println!("1e4 is {}, -2.5e-3 is {}", 1e4, -2.5e-3); + // Short-circuiting boolean logic println!("true AND false is {}", true && false); println!("true OR false is {}", true || false); @@ -42,5 +48,6 @@ fn main() { } ``` +[enote]: https://en.wikipedia.org/wiki/Scientific_notation#E_notation [rust op-prec]: https://doc.rust-lang.org/reference/expressions.html#expression-precedence [op-prec]: https://en.wikipedia.org/wiki/Operator_precedence#Programming_languages diff --git a/src-en/primitives/tuples.md b/src-en/primitives/tuples.md index 7e29ef2..75c265e 100644 --- a/src-en/primitives/tuples.md +++ b/src-en/primitives/tuples.md @@ -6,12 +6,12 @@ using parentheses `()`, and each tuple itself is a value with type signature use tuples to return multiple values, as tuples can hold any number of values. ```rust,editable -// Tuples can be used as function arguments and as return values +// Tuples can be used as function arguments and as return values. fn reverse(pair: (i32, bool)) -> (bool, i32) { - // `let` can be used to bind the members of a tuple to variables - let (integer, boolean) = pair; + // `let` can be used to bind the members of a tuple to variables. + let (int_param, bool_param) = pair; - (boolean, integer) + (bool_param, int_param) } // The following struct is for the activity. @@ -19,38 +19,38 @@ fn reverse(pair: (i32, bool)) -> (bool, i32) { struct Matrix(f32, f32, f32, f32); fn main() { - // A tuple with a bunch of different types + // A tuple with a bunch of different types. let long_tuple = (1u8, 2u16, 3u32, 4u64, -1i8, -2i16, -3i32, -4i64, 0.1f32, 0.2f64, 'a', true); - // Values can be extracted from the tuple using tuple indexing - println!("long tuple first value: {}", long_tuple.0); - println!("long tuple second value: {}", long_tuple.1); + // Values can be extracted from the tuple using tuple indexing. + println!("Long tuple first value: {}", long_tuple.0); + println!("Long tuple second value: {}", long_tuple.1); - // Tuples can be tuple members + // Tuples can be tuple members. let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16); - // Tuples are printable + // Tuples are printable. println!("tuple of tuples: {:?}", tuple_of_tuples); - - // But long Tuples cannot be printed - // let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); - // println!("too long tuple: {:?}", too_long_tuple); + + // But long Tuples (more than 12 elements) cannot be printed. + //let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); + //println!("Too long tuple: {:?}", too_long_tuple); // TODO ^ Uncomment the above 2 lines to see the compiler error let pair = (1, true); - println!("pair is {:?}", pair); + println!("Pair is {:?}", pair); - println!("the reversed pair is {:?}", reverse(pair)); + println!("The reversed pair is {:?}", reverse(pair)); // To create one element tuples, the comma is required to tell them apart - // from a literal surrounded by parentheses - println!("one element tuple: {:?}", (5u32,)); - println!("just an integer: {:?}", (5u32)); + // from a literal surrounded by parentheses. + println!("One element tuple: {:?}", (5u32,)); + println!("Just an integer: {:?}", (5u32)); - //tuples can be destructured to create bindings + // Tuples can be destructured to create bindings. let tuple = (1, "hello", 4.5, true); let (a, b, c, d) = tuple; @@ -58,40 +58,39 @@ fn main() { let matrix = Matrix(1.1, 1.2, 2.1, 2.2); println!("{:?}", matrix); - } ``` ### Activity - 1. *Recap*: Add the `fmt::Display` trait to the Matrix `struct` in the above example, - so that if you switch from printing the debug format `{:?}` to the display - format `{}`, you see the following output: - - ```text - ( 1.1 1.2 ) - ( 2.1 2.2 ) - ``` - - You may want to refer back to the example for [print display][print_display]. - 2. Add a `transpose` function using the `reverse` function as a template, which - accepts a matrix as an argument, and returns a matrix in which two elements - have been swapped. For example: - - ```rust,ignore - println!("Matrix:\n{}", matrix); - println!("Transpose:\n{}", transpose(matrix)); - ``` - - results in the output: - - ```text - Matrix: - ( 1.1 1.2 ) - ( 2.1 2.2 ) - Transpose: - ( 1.1 2.1 ) - ( 1.2 2.2 ) - ``` +1. *Recap*: Add the `fmt::Display` trait to the `Matrix` struct in the above + example, so that if you switch from printing the debug format `{:?}` to the + display format `{}`, you see the following output: + + ```text + ( 1.1 1.2 ) + ( 2.1 2.2 ) + ``` + + You may want to refer back to the example for [print display][print_display]. +2. Add a `transpose` function using the `reverse` function as a template, which + accepts a matrix as an argument, and returns a matrix in which two elements + have been swapped. For example: + + ```rust,ignore + println!("Matrix:\n{}", matrix); + println!("Transpose:\n{}", transpose(matrix)); + ``` + + Results in the output: + + ```text + Matrix: + ( 1.1 1.2 ) + ( 2.1 2.2 ) + Transpose: + ( 1.1 2.1 ) + ( 1.2 2.2 ) + ``` [print_display]: ../hello/print/print_display.md diff --git a/src-en/scope.md b/src-en/scope.md index ac70132..47bf5a1 100644 --- a/src-en/scope.md +++ b/src-en/scope.md @@ -1,5 +1,5 @@ # Scoping rules Scopes play an important part in ownership, borrowing, and lifetimes. -That is, they indicate to the compiler when borrows are valid, when -resources can be freed, and when variables are created or destroyed. \ No newline at end of file +That is, they indicate to the compiler when borrows are valid, when +resources can be freed, and when variables are created or destroyed. diff --git a/src-en/scope/borrow.md b/src-en/scope/borrow.md index 72c70e7..c80a4dd 100644 --- a/src-en/scope/borrow.md +++ b/src-en/scope/borrow.md @@ -4,7 +4,7 @@ Most of the time, we'd like to access data without taking ownership over it. To accomplish this, Rust uses a *borrowing* mechanism. Instead of passing objects by value (`T`), objects can be passed by reference (`&T`). -The compiler statically guarantees (via its borrow checker) that references +The compiler statically guarantees (via its borrow checker) that references *always* point to valid objects. That is, while references to an object exist, the object cannot be destroyed. @@ -20,7 +20,9 @@ fn borrow_i32(borrowed_i32: &i32) { } fn main() { - // Create a boxed i32, and a stacked i32 + // Create a boxed i32 in the heap, and a i32 on the stack + // Remember: numbers can have arbitrary underscores added for readability + // 5_i32 is the same as 5i32 let boxed_i32 = Box::new(5_i32); let stacked_i32 = 6_i32; @@ -43,7 +45,7 @@ fn main() { // `_ref_to_i32` goes out of scope and is no longer borrowed. } - // `boxed_i32` can now give up ownership to `eat_box` and be destroyed + // `boxed_i32` can now give up ownership to `eat_box_i32` and be destroyed eat_box_i32(boxed_i32); } ``` diff --git a/src-en/scope/borrow/mut.md b/src-en/scope/borrow/mut.md index 62119bb..f0bd108 100644 --- a/src-en/scope/borrow/mut.md +++ b/src-en/scope/borrow/mut.md @@ -1,8 +1,8 @@ # Mutability -Mutable data can be mutably borrowed using `&mut T`. This is called +Mutable data can be mutably borrowed using `&mut T`. This is called a *mutable reference* and gives read/write access to the borrower. -In contrast, `&T` borrows the data via an immutable reference, and +In contrast, `&T` borrows the data via an immutable reference, and the borrower can read the data but not modify it: ```rust,editable,ignore,mdbook-runnable @@ -54,6 +54,7 @@ fn main() { ``` ### See also: + [`static`][static] [static]: ../lifetime/static_lifetime.md diff --git a/src-en/scope/borrow/ref.md b/src-en/scope/borrow/ref.md index 3c035f6..e17d679 100644 --- a/src-en/scope/borrow/ref.md +++ b/src-en/scope/borrow/ref.md @@ -1,7 +1,7 @@ # The ref pattern When doing pattern matching or destructuring via the `let` binding, the `ref` -keyword can be used to take references to the fields of a struct/tuple. The +keyword can be used to take references to the fields of a struct/tuple. The example below shows a few instances where this can be useful: ```rust,editable @@ -54,4 +54,4 @@ fn main() { println!("tuple is {:?}", mutable_tuple); } -``` \ No newline at end of file +``` diff --git a/src-en/scope/lifetime.md b/src-en/scope/lifetime.md index 33ffcae..01c4bf4 100644 --- a/src-en/scope/lifetime.md +++ b/src-en/scope/lifetime.md @@ -26,7 +26,7 @@ fn main() { let borrow1 = &i; // `borrow1` lifetime starts. ──┐│ // ││ println!("borrow1: {}", borrow1); // ││ - } // `borrow1 ends. ──────────────────────────────────┘│ + } // `borrow1` ends. ─────────────────────────────────┘│ // │ // │ { // │ diff --git a/src-en/scope/lifetime/explicit.md b/src-en/scope/lifetime/explicit.md index ebd7b69..61ef387 100644 --- a/src-en/scope/lifetime/explicit.md +++ b/src-en/scope/lifetime/explicit.md @@ -2,18 +2,18 @@ The borrow checker uses explicit lifetime annotations to determine how long references should be valid. In cases where lifetimes are not -elided[^1], Rust requires explicit annotations to determine what the -lifetime of a reference should be. The syntax for explicitly annotating -a lifetime uses an apostrophe character as follows: +elided[^1], Rust requires explicit annotations to determine what the +lifetime of a reference should be. The syntax for explicitly annotating +a lifetime uses an apostrophe character as follows: ```rust,ignore foo<'a> // `foo` has a lifetime parameter `'a` ``` -Similar to [closures][anonymity], using lifetimes requires generics. -Additionally, this lifetime syntax indicates that the lifetime of `foo` -may not exceed that of `'a`. Explicit annotation of a type has the form +Similar to [closures][anonymity], using lifetimes requires generics. +Additionally, this lifetime syntax indicates that the lifetime of `foo` +may not exceed that of `'a`. Explicit annotation of a type has the form `&'a T` where `'a` has already been introduced. In cases with multiple lifetimes, the syntax is similar: @@ -40,10 +40,10 @@ fn failed_borrow<'a>() { let _x = 12; // ERROR: `_x` does not live long enough - //let y: &'a i32 = &_x; + let _y: &'a i32 = &_x; // Attempting to use the lifetime `'a` as an explicit type annotation // inside the function will fail because the lifetime of `&_x` is shorter - // than that of `y`. A short lifetime cannot be coerced into a longer one. + // than that of `_y`. A short lifetime cannot be coerced into a longer one. } fn main() { diff --git a/src-en/scope/lifetime/fn.md b/src-en/scope/lifetime/fn.md index 6c5c8af..c863f09 100644 --- a/src-en/scope/lifetime/fn.md +++ b/src-en/scope/lifetime/fn.md @@ -1,6 +1,6 @@ # Functions -Ignoring [elision], function signatures with lifetimes have a few constraints: +Ignoring [elision], function signatures with lifetimes have a few constraints: * any reference *must* have an annotated lifetime. * any reference being returned *must* have the same lifetime as an input or @@ -57,7 +57,8 @@ fn main() { ### See also: -[functions][fn] +[Functions][fn] + +[fn]: ../../fn.md [elision]: elision.md -[fn]: fn.md diff --git a/src-en/scope/lifetime/lifetime_bounds.md b/src-en/scope/lifetime/lifetime_bounds.md index 3d635bd..a5114e1 100644 --- a/src-en/scope/lifetime/lifetime_bounds.md +++ b/src-en/scope/lifetime/lifetime_bounds.md @@ -1,7 +1,7 @@ # Bounds Just like generic types can be bounded, lifetimes (themselves generic) -use bounds as well. The `:` character has a slightly different meaning here, +use bounds as well. The `:` character has a slightly different meaning here, but `+` is the same. Note how the following read: 1. `T: 'a`: *All* references in `T` must outlive lifetime `'a`. @@ -16,7 +16,7 @@ use std::fmt::Debug; // Trait to bound with. #[derive(Debug)] struct Ref<'a, T: 'a>(&'a T); // `Ref` contains a reference to a generic type `T` that has -// an unknown lifetime `'a`. `T` is bounded such that any +// some lifetime `'a` unknown by `Ref`. `T` is bounded such that any // *references* in `T` must outlive `'a`. Additionally, the lifetime // of `Ref` may not exceed `'a`. @@ -45,7 +45,7 @@ fn main() { ### See also: -[generics][generics], [bounds in generics][bounds], and +[generics][generics], [bounds in generics][bounds], and [multiple bounds in generics][multibounds] [generics]: ../../generics.md diff --git a/src-en/scope/lifetime/lifetime_coercion.md b/src-en/scope/lifetime/lifetime_coercion.md index 96090f4..6ebff8f 100644 --- a/src-en/scope/lifetime/lifetime_coercion.md +++ b/src-en/scope/lifetime/lifetime_coercion.md @@ -1,6 +1,6 @@ # Coercion -A longer lifetime can be coerced into a shorter one +A longer lifetime can be coerced into a shorter one so that it works inside a scope it normally wouldn't work in. This comes in the form of inferred coercion by the Rust compiler, and also in the form of declaring a lifetime difference: @@ -28,4 +28,4 @@ fn main() { println!("{} is the first", choose_first(&first, &second)); }; } -``` \ No newline at end of file +``` diff --git a/src-en/scope/lifetime/static_lifetime.md b/src-en/scope/lifetime/static_lifetime.md index 7a1bddf..637add3 100644 --- a/src-en/scope/lifetime/static_lifetime.md +++ b/src-en/scope/lifetime/static_lifetime.md @@ -17,10 +17,10 @@ confusion when learning Rust. Here are some examples for each situation: ## Reference lifetime As a reference lifetime `'static` indicates that the data pointed to by -the reference lives for the entire lifetime of the running program. +the reference lives for the remaining lifetime of the running program. It can still be coerced to a shorter lifetime. -There are two ways to make a variable with `'static` lifetime, and both +There are two common ways to make a variable with `'static` lifetime, and both are stored in the read-only memory of the binary: * Make a constant with the `static` declaration. @@ -62,6 +62,31 @@ fn main() { } ``` +Since `'static` references only need to be valid for the _remainder_ of +a program's life, they can be created while the program is executed. Just to +demonstrate, the below example uses +[`Box::leak`](https://doc.rust-lang.org/std/boxed/struct.Box.html#method.leak) +to dynamically create `'static` references. In that case it definitely doesn't +live for the entire duration, but only from the leaking point onward. + +```rust,editable,compile_fail +extern crate rand; +use rand::Fill; + +fn random_vec() -> &'static [usize; 100] { + let mut rng = rand::thread_rng(); + let mut boxed = Box::new([0; 100]); + boxed.try_fill(&mut rng).unwrap(); + Box::leak(boxed) +} + +fn main() { + let first: &'static [usize; 100] = random_vec(); + let second: &'static [usize; 100] = random_vec(); + assert_ne!(first, second) +} +``` + ## Trait bound As a trait bound, it means the type does not contain any non-static @@ -75,23 +100,23 @@ does not: ```rust,editable,compile_fail use std::fmt::Debug; -fn print_it( input: impl Debug + 'static ) -{ +fn print_it( input: impl Debug + 'static ) { println!( "'static value passed in is: {:?}", input ); } -fn use_it() -{ +fn main() { // i is owned and contains no references, thus it's 'static: let i = 5; print_it(i); // oops, &i only has the lifetime defined by the scope of - // use_it(), so it's not 'static: + // main(), so it's not 'static: print_it(&i); } ``` + The compiler will tell you: + ```ignore error[E0597]: `i` does not live long enough --> src/lib.rs:15:15 diff --git a/src-en/scope/lifetime/struct.md b/src-en/scope/lifetime/struct.md index aca0bfe..f793a0d 100644 --- a/src-en/scope/lifetime/struct.md +++ b/src-en/scope/lifetime/struct.md @@ -42,5 +42,4 @@ fn main() { [`struct`s][structs] - [structs]: ../../custom_types/structs.md diff --git a/src-en/scope/lifetime/trait.md b/src-en/scope/lifetime/trait.md index 0f56f5b..6527df0 100644 --- a/src-en/scope/lifetime/trait.md +++ b/src-en/scope/lifetime/trait.md @@ -6,9 +6,9 @@ Note that `impl` may have annotation of lifetimes too. ```rust,editable // A struct with annotation of lifetimes. #[derive(Debug)] - struct Borrowed<'a> { - x: &'a i32, - } +struct Borrowed<'a> { + x: &'a i32, +} // Annotate lifetimes to impl. impl<'a> Default for Borrowed<'a> { @@ -29,5 +29,4 @@ fn main() { [`trait`s][trait] - [trait]: ../../trait.md diff --git a/src-en/scope/move.md b/src-en/scope/move.md index 0433e8a..b20bc40 100644 --- a/src-en/scope/move.md +++ b/src-en/scope/move.md @@ -1,12 +1,12 @@ # Ownership and moves -Because variables are in charge of freeing their own resources, -**resources can only have one owner**. This also prevents resources -from being freed more than once. Note that not all variables own +Because variables are in charge of freeing their own resources, +**resources can only have one owner**. This prevents resources +from being freed more than once. Note that not all variables own resources (e.g. [references]). When doing assignments (`let x = y`) or passing function arguments by value -(`foo(x)`), the *ownership* of the resources is transferred. In Rust-speak, +(`foo(x)`), the *ownership* of the resources is transferred. In Rust-speak, this is known as a *move*. After moving resources, the previous owner can no longer be used. This avoids diff --git a/src-en/scope/move/mut.md b/src-en/scope/move/mut.md index 3dddc4a..3ad32a9 100644 --- a/src-en/scope/move/mut.md +++ b/src-en/scope/move/mut.md @@ -21,4 +21,4 @@ fn main() { println!("mutable_box now contains {}", mutable_box); } -``` \ No newline at end of file +``` diff --git a/src-en/scope/move/partial_move.md b/src-en/scope/move/partial_move.md index 7afa7e5..8b1dab0 100644 --- a/src-en/scope/move/partial_move.md +++ b/src-en/scope/move/partial_move.md @@ -1,23 +1,35 @@ # Partial moves -Pattern bindings can have `by-move` and `by-reference` bindings at -the same time which is used in [destructuring]. Using these pattern -will result in partial move for the variable, which means that part -of the variable is moved while other parts stayed. In this case, the -parent variable cannot be used afterwards as a whole. However, parts -of it that are referenced and not moved can be used. +Within the [destructuring] of a single variable, both `by-move` and +`by-reference` pattern bindings can be used at the same time. Doing +this will result in a _partial move_ of the variable, which means +that parts of the variable will be moved while other parts stay. In +such a case, the parent variable cannot be used afterwards as a +whole, however the parts that are only referenced (and not moved) +can still be used. Note that types that implement the +[`Drop` trait][droptrait] cannot be partially moved from, because +its `drop` method would use it afterwards as a whole. + ```rust,editable fn main() { #[derive(Debug)] struct Person { name: String, - age: u8, + age: Box, } + // Error! cannot move out of a type which implements the `Drop` trait + //impl Drop for Person { + // fn drop(&mut self) { + // println!("Dropping the person struct {:?}", self) + // } + //} + // TODO ^ Try uncommenting these lines + let person = Person { name: String::from("Alice"), - age: 20, + age: Box::new(20), }; // `name` is moved out of person, but `age` is referenced @@ -34,7 +46,17 @@ fn main() { println!("The person's age from person struct is {}", person.age); } ``` + +(In this example, we store the `age` variable on the heap to +illustrate the partial move: deleting `ref` in the above code would +give an error as the ownership of `person.age` would be moved to the +variable `age`. If `Person.age` were stored on the stack, `ref` would +not be required as the definition of `age` would copy the data from +`person.age` without moving it.) + ### See also: + [destructuring][destructuring] -[destructuring]: ../../flow_control/match/destructuring.md \ No newline at end of file +[droptrait]: ../../trait/drop.md +[destructuring]: ../../flow_control/match/destructuring.md diff --git a/src-en/scope/raii.md b/src-en/scope/raii.md index 7b6bca6..6d94b07 100644 --- a/src-en/scope/raii.md +++ b/src-en/scope/raii.md @@ -41,6 +41,8 @@ fn main() { Of course, we can double check for memory errors using [`valgrind`][valgrind]: + + ```shell $ rustc raii.rs && valgrind ./raii ==26873== Memcheck, a memory error detector @@ -58,6 +60,7 @@ $ rustc raii.rs && valgrind ./raii ==26873== For counts of detected and suppressed errors, rerun with: -v ==26873== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) ``` + No leaks here! diff --git a/src-en/std/arc.md b/src-en/std/arc.md index 566bd55..f4c1ce8 100644 --- a/src-en/std/arc.md +++ b/src-en/std/arc.md @@ -1,27 +1,33 @@ # Arc -When shared ownership between threads is needed, `Arc`(Atomic Reference Counted) can be used. This struct, via the `Clone` implementation can create a reference pointer for the location of a value in the memory heap while increasing the reference counter. As it shares ownership between threads, when the last reference pointer to a value is out of scope, the variable is dropped. +When shared ownership between threads is needed, `Arc`(Atomically Reference +Counted) can be used. This struct, via the `Clone` implementation can create +a reference pointer for the location of a value in the memory heap while +increasing the reference counter. As it shares ownership between threads, when +the last reference pointer to a value is out of scope, the variable is dropped. ```rust,editable - -fn main() { +use std::time::Duration; use std::sync::Arc; use std::thread; -// This variable declaration is where it's value is specified. -let apple = Arc::new("the same apple"); +fn main() { + // This variable declaration is where its value is specified. + let apple = Arc::new("the same apple"); + + for _ in 0..10 { + // Here there is no value specification as it is a pointer to a + // reference in the memory heap. + let apple = Arc::clone(&apple); -for _ in 0..10 { - // Here there is no value specification as it is a pointer to a reference - // in the memory heap. - let apple = Arc::clone(&apple); + thread::spawn(move || { + // As Arc was used, threads can be spawned using the value allocated + // in the Arc variable pointer's location. + println!("{:?}", apple); + }); + } - thread::spawn(move || { - // As Arc was used, threads can be spawned using the value allocated - // in the Arc variable pointer's location. - println!("{:?}", apple); - }); -} + // Make sure all Arc instances are printed from spawned threads. + thread::sleep(Duration::from_secs(1)); } - ``` diff --git a/src-en/std/box.md b/src-en/std/box.md index ebc8ff4..bcb5dfd 100644 --- a/src-en/std/box.md +++ b/src-en/std/box.md @@ -6,7 +6,7 @@ heap allocated value of type `T`. When a box goes out of scope, its destructor is called, the inner object is destroyed, and the memory on the heap is freed. Boxed values can be dereferenced using the `*` operator; this removes one layer -of indirection. +of indirection. ```rust,editable use std::mem; @@ -74,4 +74,4 @@ fn main() { println!("Unboxed point occupies {} bytes on the stack", mem::size_of_val(&unboxed_point)); } -``` \ No newline at end of file +``` diff --git a/src-en/std/hash.md b/src-en/std/hash.md index 94faacb..6ed0f8c 100644 --- a/src-en/std/hash.md +++ b/src-en/std/hash.md @@ -1,14 +1,14 @@ # HashMap -Where vectors store values by an integer index, `HashMap`s store values by key. -`HashMap` keys can be booleans, integers, strings, -or any other type that implements the `Eq` and `Hash` traits. +Where vectors store values by an integer index, `HashMap`s store values by key. +`HashMap` keys can be booleans, integers, strings, +or any other type that implements the `Eq` and `Hash` traits. More on this in the next section. -Like vectors, `HashMap`s are growable, but HashMaps can also shrink themselves -when they have excess space. -You can create a HashMap with a certain starting capacity using -`HashMap::with_capacity(uint)`, or use `HashMap::new()` to get a HashMap +Like vectors, `HashMap`s are growable, but HashMaps can also shrink themselves +when they have excess space. +You can create a HashMap with a certain starting capacity using +`HashMap::with_capacity(uint)`, or use `HashMap::new()` to get a HashMap with a default initial capacity (recommended). ```rust,editable @@ -57,8 +57,8 @@ fn main() { } ``` -For more information on how hashing and hash maps -(sometimes called hash tables) work, have a look at +For more information on how hashing and hash maps +(sometimes called hash tables) work, have a look at [Hash Table Wikipedia][wiki-hash] [wiki-hash]: https://en.wikipedia.org/wiki/Hash_table diff --git a/src-en/std/hash/alt_key_types.md b/src-en/std/hash/alt_key_types.md index ab94819..8c23335 100644 --- a/src-en/std/hash/alt_key_types.md +++ b/src-en/std/hash/alt_key_types.md @@ -1,9 +1,9 @@ # Alternate/custom key types -Any type that implements the `Eq` and `Hash` traits can be a key in `HashMap`. +Any type that implements the `Eq` and `Hash` traits can be a key in `HashMap`. This includes: -* `bool` (though not very useful since there is only two possible keys) +* `bool` (though not very useful since there are only two possible keys) * `int`, `uint`, and all variations thereof * `String` and `&str` (protip: you can have a `HashMap` keyed by `String` and call `.get()` with an `&str`) @@ -12,18 +12,18 @@ Note that `f32` and `f64` do *not* implement `Hash`, likely because [floating-point precision errors][floating] would make using them as hashmap keys horribly error-prone. -All collection classes implement `Eq` and `Hash` -if their contained type also respectively implements `Eq` and `Hash`. +All collection classes implement `Eq` and `Hash` +if their contained type also respectively implements `Eq` and `Hash`. For example, `Vec` will implement `Hash` if `T` implements `Hash`. -You can easily implement `Eq` and `Hash` for a custom type with just one line: +You can easily implement `Eq` and `Hash` for a custom type with just one line: `#[derive(PartialEq, Eq, Hash)]` -The compiler will do the rest. If you want more control over the details, -you can implement `Eq` and/or `Hash` yourself. -This guide will not cover the specifics of implementing `Hash`. +The compiler will do the rest. If you want more control over the details, +you can implement `Eq` and/or `Hash` yourself. +This guide will not cover the specifics of implementing `Hash`. -To play around with using a `struct` in `HashMap`, +To play around with using a `struct` in `HashMap`, let's try making a very simple user logon system: ```rust,editable diff --git a/src-en/std/hash/hashset.md b/src-en/std/hash/hashset.md index 63d5af8..23ac67d 100644 --- a/src-en/std/hash/hashset.md +++ b/src-en/std/hash/hashset.md @@ -5,19 +5,19 @@ Consider a `HashSet` as a `HashMap` where we just care about the keys ( "What's the point of that?" you ask. "I could just store the keys in a `Vec`." -A `HashSet`'s unique feature is that -it is guaranteed to not have duplicate elements. -That's the contract that any set collection fulfills. +A `HashSet`'s unique feature is that +it is guaranteed to not have duplicate elements. +That's the contract that any set collection fulfills. `HashSet` is just one implementation. (see also: [`BTreeSet`][treeset]) -If you insert a value that is already present in the `HashSet`, -(i.e. the new value is equal to the existing and they both have the same hash), +If you insert a value that is already present in the `HashSet`, +(i.e. the new value is equal to the existing and they both have the same hash), then the new value will replace the old. -This is great for when you never want more than one of something, +This is great for when you never want more than one of something, or when you want to know if you've already got something. -But sets can do more than that. +But sets can do more than that. Sets have 4 primary operations (all of the following calls return an iterator): @@ -27,7 +27,7 @@ Sets have 4 primary operations (all of the following calls return an iterator): * `intersection`: get all the elements that are only in *both* sets. -* `symmetric_difference`: +* `symmetric_difference`: get all the elements that are in one set or the other, but *not* both. Try all of these in the following example: diff --git a/src-en/std/panic.md b/src-en/std/panic.md index b220004..d08d1f4 100644 --- a/src-en/std/panic.md +++ b/src-en/std/panic.md @@ -34,6 +34,8 @@ fn main() { Let's check that `panic!` doesn't leak memory. + + ```shell $ rustc panic.rs && valgrind ./panic ==4401== Memcheck, a memory error detector @@ -52,3 +54,4 @@ thread '
' panicked at 'division by zero', panic.rs:5 ==4401== For counts of detected and suppressed errors, rerun with: -v ==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ``` + diff --git a/src-en/std/rc.md b/src-en/std/rc.md index a9691b2..658145b 100644 --- a/src-en/std/rc.md +++ b/src-en/std/rc.md @@ -1,10 +1,16 @@ # `Rc` -When multiple ownership is needed, `Rc`(Reference Counting) can be used. `Rc` keeps track of the number of the references which means the number of owners of the value wrapped inside an `Rc`. +When multiple ownership is needed, `Rc`(Reference Counting) can be used. `Rc` +keeps track of the number of the references which means the number of owners of +the value wrapped inside an `Rc`. -Reference count of an `Rc` increases by 1 whenever an `Rc` is cloned, and decreases by 1 whenever one cloned `Rc` is dropped out of the scope. When an `Rc`'s reference count becomes zero, which means there are no owners remained, both the `Rc` and the value are all dropped. +Reference count of an `Rc` increases by 1 whenever an `Rc` is cloned, and +decreases by 1 whenever one cloned `Rc` is dropped out of the scope. When an +`Rc`'s reference count becomes zero (which means there are no remaining owners), +both the `Rc` and the value are all dropped. -Cloning an `Rc` never performs a deep copy. Cloning creates just another pointer to the wrapped value, and increments the count. +Cloning an `Rc` never performs a deep copy. Cloning creates just another pointer +to the wrapped value, and increments the count. ```rust,editable use std::rc::Rc; diff --git a/src-en/std/result.md b/src-en/std/result.md index b812b9d..50898de 100644 --- a/src-en/std/result.md +++ b/src-en/std/result.md @@ -2,7 +2,7 @@ We've seen that the `Option` enum can be used as a return value from functions that may fail, where `None` can be returned to indicate failure. However, -sometimes it is important to express *why* an operation failed. To do this we +sometimes it is important to express *why* an operation failed. To do this we have the `Result` enum. The `Result` enum has two variants: diff --git a/src-en/std/result/question_mark.md b/src-en/std/result/question_mark.md index c51cc63..61d938a 100644 --- a/src-en/std/result/question_mark.md +++ b/src-en/std/result/question_mark.md @@ -2,8 +2,8 @@ Chaining results using match can get pretty untidy; luckily, the `?` operator can be used to make things pretty again. `?` is used at the end of an expression -returning a `Result`, and is equivalent to a match expression, where the -`Err(err)` branch expands to an early `Err(From::from(err))`, and the `Ok(ok)` +returning a `Result`, and is equivalent to a match expression, where the +`Err(err)` branch expands to an early `return Err(From::from(err))`, and the `Ok(ok)` branch expands to an `ok` expression. ```rust,editable,ignore,mdbook-runnable @@ -54,7 +54,7 @@ mod checked { pub fn op(x: f64, y: f64) { match op_(x, y) { - Err(why) => panic!(match why { + Err(why) => panic!("{}", match why { MathError::NonPositiveLogarithm => "logarithm of non-positive number", MathError::DivisionByZero diff --git a/src-en/std/str.md b/src-en/std/str.md index ba14b59..8b2d744 100644 --- a/src-en/std/str.md +++ b/src-en/std/str.md @@ -1,6 +1,6 @@ # Strings -There are two types of strings in Rust: `String` and `&str`. +The two most used string types in Rust are `String` and `&str`. A `String` is stored as a vector of bytes (`Vec`), but guaranteed to always be a valid UTF-8 sequence. `String` is heap allocated, growable and not @@ -69,7 +69,7 @@ This way you can add any character to your string, even unprintable ones and ones that you don't know how to type. If you want a literal backslash, escape it with another one: `\\` -String or character literal delimiters occuring within a literal must be escaped: `"\""`, `'\''`. +String or character literal delimiters occurring within a literal must be escaped: `"\""`, `'\''`. ```rust,editable fn main() { @@ -106,7 +106,7 @@ fn main() { println!("{}", quotes); // If you need "# in your string, just use more #s in the delimiter. - // There is no limit for the number of #s you can use. + // You can use up to 255 #s. let longer_delimiter = r###"A string with "# in it. And even "##!"###; println!("{}", longer_delimiter); } diff --git a/src-en/std/vec.md b/src-en/std/vec.md index f5cb1ca..e8cf244 100644 --- a/src-en/std/vec.md +++ b/src-en/std/vec.md @@ -2,13 +2,14 @@ Vectors are re-sizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time. A vector is represented using -3 parameters: +3 parameters: + - pointer to the data - length -- capacity +- capacity -The capacity indicates how much memory is reserved for the vector. The vector -can grow as long as the length is smaller than the capacity. When this threshold +The capacity indicates how much memory is reserved for the vector. The vector +can grow as long as the length is smaller than the capacity. When this threshold needs to be surpassed, the vector is reallocated with a larger capacity. ```rust,editable,ignore,mdbook-runnable diff --git a/src-en/std_misc/arg.md b/src-en/std_misc/arg.md index b5dd89c..1cf8cf7 100644 --- a/src-en/std_misc/arg.md +++ b/src-en/std_misc/arg.md @@ -30,8 +30,7 @@ I got 3 arguments: ["1", "2", "3"]. ## Crates Alternatively, there are numerous crates that can provide extra functionality -when creating command-line applications. The [Rust Cookbook] exhibits best -practices on how to use one of the more popular command line argument crates, -`clap`. +when creating command-line applications. One of the more popular command line +argument crates being [`clap`]. -[Rust Cookbook]: https://rust-lang-nursery.github.io/rust-cookbook/cli/arguments.html +[`clap`]: https://rust-cli.github.io/book/tutorial/cli-args.html#parsing-cli-arguments-with-clap diff --git a/src-en/std_misc/arg/matching.md b/src-en/std_misc/arg/matching.md index 4cb68ea..4a96fbc 100644 --- a/src-en/std_misc/arg/matching.md +++ b/src-en/std_misc/arg/matching.md @@ -70,6 +70,9 @@ fn main() { } ``` +If you named your program `match_args.rs` and compile it like this `rustc +match_args.rs`, you can execute it as follows: + ```shell $ ./match_args Rust This is not the answer. diff --git a/src-en/std_misc/ffi.md b/src-en/std_misc/ffi.md index 71977e6..2728736 100644 --- a/src-en/std_misc/ffi.md +++ b/src-en/std_misc/ffi.md @@ -8,6 +8,16 @@ attribute containing the name of the foreign library. use std::fmt; // this extern block links to the libm library +#[cfg(target_family = "windows")] +#[link(name = "msvcrt")] +extern { + // this is a foreign function + // that computes the square root of a single precision complex number + fn csqrtf(z: Complex) -> Complex; + + fn ccosf(z: Complex) -> Complex; +} +#[cfg(target_family = "unix")] #[link(name = "m")] extern { // this is a foreign function diff --git a/src-en/std_misc/file.md b/src-en/std_misc/file.md index 60e28cf..f0538a7 100644 --- a/src-en/std_misc/file.md +++ b/src-en/std_misc/file.md @@ -8,4 +8,4 @@ return the `io::Result` type, which is an alias for `Result`. This makes the failure of all I/O operations *explicit*. Thanks to this, the programmer can see all the failure paths, and is encouraged to handle them in -a proactive manner. \ No newline at end of file +a proactive manner. diff --git a/src-en/std_misc/file/create.md b/src-en/std_misc/file/create.md index 16eba89..4b96e02 100644 --- a/src-en/std_misc/file/create.md +++ b/src-en/std_misc/file/create.md @@ -1,6 +1,6 @@ # `create` -The `create` static method opens a file in write-only mode. If the file +The `create` function opens a file in write-only mode. If the file already existed, the old content is destroyed. Otherwise, a new file is created. @@ -41,6 +41,7 @@ Here's the expected successful output: ```shell $ rustc create.rs && ./create successfully wrote to lorem_ipsum.txt + $ cat lorem_ipsum.txt Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, @@ -53,6 +54,6 @@ proident, sunt in culpa qui officia deserunt mollit anim id est laborum. (As in the previous example, you are encouraged to test this example under failure conditions.) -There is [`OpenOptions`] struct that can be used to configure how a file is opened. +The [`OpenOptions`] struct can be used to configure how a file is opened. [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html diff --git a/src-en/std_misc/file/open.md b/src-en/std_misc/file/open.md index 16fdb2c..77a5470 100644 --- a/src-en/std_misc/file/open.md +++ b/src-en/std_misc/file/open.md @@ -1,6 +1,6 @@ # `open` -The `open` static method can be used to open a file in read-only mode. +The `open` function can be used to open a file in read-only mode. A `File` owns a resource, the file descriptor and takes care of closing the file when it is `drop`ed. diff --git a/src-en/std_misc/file/read_lines.md b/src-en/std_misc/file/read_lines.md index 6792b17..837bac3 100644 --- a/src-en/std_misc/file/read_lines.md +++ b/src-en/std_misc/file/read_lines.md @@ -1,10 +1,51 @@ # `read_lines` -The method `lines()` returns an iterator over the lines -of a file. +## A naive approach -`File::open` expects a generic, `AsRef`. That's what -`read_lines()` expects as input. +This might be a reasonable first attempt for a beginner's first +implementation for reading lines from a file. + +```rust,norun +use std::fs::read_to_string; + +fn read_lines(filename: &str) -> Vec { + let mut result = Vec::new(); + + for line in read_to_string(filename).unwrap().lines() { + result.push(line.to_string()) + } + + result +} +``` + +Since the method `lines()` returns an iterator over the lines in the file, +we can also perform a map inline and collect the results, yielding a more +concise and fluent expression. + +```rust,norun +use std::fs::read_to_string; + +fn read_lines(filename: &str) -> Vec { + read_to_string(filename) + .unwrap() // panic on possible file-reading errors + .lines() // split the string into an iterator of string slices + .map(String::from) // make each slice into a string + .collect() // gather them together into a vector +} +``` + +Note that in both examples above, we must convert the `&str` reference +returned from `lines()` to the owned type `String`, using `.to_string()` +and `String::from` respectively. + +## A more efficient approach + +Here we pass ownership of the open `File` to a `BufReader` struct. `BufReader` uses an internal +buffer to reduce intermediate allocations. + +We also update `read_lines` to return an iterator instead of allocating new +`String` objects in memory for each line. ```rust,no_run use std::fs::File; @@ -12,18 +53,16 @@ use std::io::{self, BufRead}; use std::path::Path; fn main() { - // File hosts must exist in current path before this produces output - if let Ok(lines) = read_lines("./hosts") { + // File hosts.txt must exist in the current path + if let Ok(lines) = read_lines("./hosts.txt") { // Consumes the iterator, returns an (Optional) String - for line in lines { - if let Ok(ip) = line { - println!("{}", ip); - } + for line in lines.map_while(Result::ok) { + println!("{}", line); } } } -// The output is wrapped in a Result to allow matching on errors +// The output is wrapped in a Result to allow matching on errors. // Returns an Iterator to the Reader of the lines of the file. fn read_lines

(filename: P) -> io::Result>> where P: AsRef, { @@ -33,12 +72,16 @@ where P: AsRef, { ``` Running this program simply prints the lines individually. + ```shell -$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts +$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt $ rustc read_lines.rs && ./read_lines 127.0.0.1 192.168.0.1 ``` -This process is more efficient than creating a `String` in memory -especially working with larger files. \ No newline at end of file +(Note that since `File::open` expects a generic `AsRef` as argument, we define our +generic `read_lines()` method with the same generic constraint, using the `where` keyword.) + +This process is more efficient than creating a `String` in memory with all of the file's +contents. This can especially cause performance issues when working with larger files. diff --git a/src-en/std_misc/fs.md b/src-en/std_misc/fs.md index 33612d7..ba2d4b4 100644 --- a/src-en/std_misc/fs.md +++ b/src-en/std_misc/fs.md @@ -7,7 +7,10 @@ use std::fs; use std::fs::{File, OpenOptions}; use std::io; use std::io::prelude::*; +#[cfg(target_family = "unix")] use std::os::unix; +#[cfg(target_family = "windows")] +use std::os::windows; use std::path::Path; // A simple implementation of `% cat path` @@ -62,9 +65,14 @@ fn main() { println!("`ln -s ../b.txt a/c/b.txt`"); // Create a symbolic link, returns `io::Result<()>` - if cfg!(target_family = "unix") { + #[cfg(target_family = "unix")] { unix::fs::symlink("../b.txt", "a/c/b.txt").unwrap_or_else(|why| { - println!("! {:?}", why.kind()); + println!("! {:?}", why.kind()); + }); + } + #[cfg(target_family = "windows")] { + windows::fs::symlink_file("../b.txt", "a/c/b.txt").unwrap_or_else(|why| { + println!("! {:?}", why.to_string()); }); } diff --git a/src-en/std_misc/path.md b/src-en/std_misc/path.md index eb83df8..378df6e 100644 --- a/src-en/std_misc/path.md +++ b/src-en/std_misc/path.md @@ -8,9 +8,15 @@ platform-specific `Path` variant. A `Path` can be created from an `OsStr`, and provides several methods to get information from the file/directory the path points to. +A `Path` is immutable. The owned version of `Path` is `PathBuf`. The relation +between `Path` and `PathBuf` is similar to that of `str` and `String`: +a `PathBuf` can be mutated in-place, and can be dereferenced to a `Path`. + Note that a `Path` is *not* internally represented as an UTF-8 string, but -instead is stored as a vector of bytes (`Vec`). Therefore, converting a -`Path` to a `&str` is *not* free and may fail (an `Option` is returned). +instead is stored as an `OsString`. Therefore, converting a `Path` to a `&str` +is *not* free and may fail (an `Option` is returned). However, a `Path` can be +freely converted to an `OsString` or `&OsStr` using `into_os_string` and +`as_os_str`, respectively. ```rust,editable use std::path::Path; @@ -19,14 +25,21 @@ fn main() { // Create a `Path` from an `&'static str` let path = Path::new("."); - // The `display` method returns a `Show`able structure + // The `display` method returns a `Display`able structure let _display = path.display(); // `join` merges a path with a byte container using the OS specific - // separator, and returns the new path - let new_path = path.join("a").join("b"); + // separator, and returns a `PathBuf` + let mut new_path = path.join("a").join("b"); + + // `push` extends the `PathBuf` with a `&Path` + new_path.push("c"); + new_path.push("myfile.tar.gz"); + + // `set_file_name` updates the file name of the `PathBuf` + new_path.set_file_name("package.tgz"); - // Convert the path into a string slice + // Convert the `PathBuf` into a string slice match new_path.to_str() { None => panic!("new path is not a valid UTF-8 sequence"), Some(s) => println!("new path is {}", s), diff --git a/src-en/std_misc/process/pipe.md b/src-en/std_misc/process/pipe.md index fb0be0e..6403596 100644 --- a/src-en/std_misc/process/pipe.md +++ b/src-en/std_misc/process/pipe.md @@ -1,6 +1,6 @@ # Pipes -The `std::Child` struct represents a running child process, and exposes the +The `std::process::Child` struct represents a child process, and exposes the `stdin`, `stdout` and `stderr` handles for interaction with the underlying process via pipes. @@ -9,11 +9,18 @@ use std::io::prelude::*; use std::process::{Command, Stdio}; static PANGRAM: &'static str = -"the quick brown fox jumped over the lazy dog\n"; +"the quick brown fox jumps over the lazy dog\n"; fn main() { // Spawn the `wc` command - let process = match Command::new("wc") + let mut cmd = if cfg!(target_family = "windows") { + let mut cmd = Command::new("powershell"); + cmd.arg("-Command").arg("$input | Measure-Object -Line -Word -Character"); + cmd + } else { + Command::new("wc") + }; + let process = match cmd .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() { diff --git a/src-en/std_misc/threads/testcase_mapreduce.md b/src-en/std_misc/threads/testcase_mapreduce.md index 9efc34a..0de796d 100644 --- a/src-en/std_misc/threads/testcase_mapreduce.md +++ b/src-en/std_misc/threads/testcase_mapreduce.md @@ -1,14 +1,14 @@ # Testcase: map-reduce -Rust makes it very easy to parallelise data processing, without many of the headaches traditionally associated with such an attempt. +Rust makes it very easy to parallelize data processing, without many of the headaches traditionally associated with such an attempt. The standard library provides great threading primitives out of the box. These, combined with Rust's concept of Ownership and aliasing rules, automatically prevent data races. The aliasing rules (one writable reference XOR many readable references) automatically prevent -you from manipulating state that is visible to other threads. (Where synchronisation is needed, -there are synchronisation +you from manipulating state that is visible to other threads. (Where synchronization is needed, +there are synchronization primitives like `Mutex`es or `Channel`s.) In this example, we will calculate the sum of all digits in a block of numbers. @@ -17,9 +17,11 @@ its tiny block of digits, and subsequently we will sum the intermediate sums pro thread. Note that, although we're passing references across thread boundaries, Rust understands that we're -only passing read-only references, and that thus no unsafety or data races can occur. Because -we're `move`-ing the data segments into the thread, Rust will also ensure the data is kept alive -until the threads exit, so no dangling pointers occur. +only passing read-only references, and that thus no unsafety or data races can occur. Also because +the references we're passing have `'static` lifetimes, Rust understands that our data won't be +destroyed while these threads are still running. (When you need to share non-`static` data between +threads, you can use a smart pointer like `Arc` to keep the data alive and avoid non-`static` +lifetimes.) ```rust,editable use std::thread; @@ -28,7 +30,7 @@ use std::thread; fn main() { // This is our data to process. - // We will calculate the sum of all digits via a threaded map-reduce algorithm. + // We will calculate the sum of all digits via a threaded map-reduce algorithm. // Each whitespace separated chunk will be handled in a different thread. // // TODO: see what happens to the output if you insert spaces! @@ -103,21 +105,13 @@ fn main() { * Collect our intermediate results, and combine them into a final result ************************************************************************/ - // collect each thread's intermediate results into a new Vec - let mut intermediate_sums = vec![]; - for child in children { - // collect each child thread's return-value - let intermediate_sum = child.join().unwrap(); - intermediate_sums.push(intermediate_sum); - } - - // combine all intermediate sums into a single final sum. + // combine each thread's intermediate results into a single final sum. // // we use the "turbofish" ::<> to provide sum() with a type hint. // // TODO: try without the turbofish, by instead explicitly // specifying the type of final_result - let final_result = intermediate_sums.iter().sum::(); + let final_result = children.into_iter().map(|c| c.join().unwrap()).sum::(); println!("Final sum result: {}", final_result); } @@ -126,12 +120,14 @@ fn main() { ``` ### Assignments + It is not wise to let our number of threads depend on user inputted data. What if the user decides to insert a lot of spaces? Do we _really_ want to spawn 2,000 threads? Modify the program so that the data is always chunked into a limited number of chunks, defined by a static constant at the beginning of the program. ### See also: + * [Threads][thread] * [vectors][vectors] and [iterators][iterators] * [closures][closures], [move][move] semantics and [`move` closures][move_closure] @@ -147,6 +143,6 @@ defined by a static constant at the beginning of the program. [closures]: ../../fn/closures.md [move]: ../../scope/move.md [move_closure]: https://doc.rust-lang.org/book/ch13-01-closures.html#closures-can-capture-their-environment -[turbofish]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect +[turbofish]: https://doc.rust-lang.org/book/appendix-02-operators.html?highlight=turbofish [unwrap]: ../../error/option_unwrap.md [enumerate]: https://doc.rust-lang.org/book/loops.html#enumerate diff --git a/src-en/testing/dev_dependencies.md b/src-en/testing/dev_dependencies.md index 3d4ccde..9d4c3c6 100644 --- a/src-en/testing/dev_dependencies.md +++ b/src-en/testing/dev_dependencies.md @@ -5,23 +5,18 @@ or benchmarks) only. Such dependencies are added to `Cargo.toml` in the `[dev-dependencies]` section. These dependencies are not propagated to other packages which depend on this package. -One such example is using a crate that extends standard `assert!` macros. +One such example is [`pretty_assertions`](https://docs.rs/pretty_assertions/1.0.0/pretty_assertions/index.html), which extends standard `assert_eq!` and `assert_ne!` macros, to provide colorful diff. File `Cargo.toml`: ```toml # standard crate data is left out [dev-dependencies] -pretty_assertions = "0.4.0" +pretty_assertions = "1" ``` File `src/lib.rs`: ```rust,ignore -// externing crate for test-only use -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; - pub fn add(a: i32, b: i32) -> i32 { a + b } @@ -29,6 +24,7 @@ pub fn add(a: i32, b: i32) -> i32 { #[cfg(test)] mod tests { use super::*; + use pretty_assertions::assert_eq; // crate for test-only use. Cannot be used in non-test code. #[test] fn test_add() { @@ -38,6 +34,7 @@ mod tests { ``` ## See Also + [Cargo][cargo] docs on specifying dependencies. [cargo]: http://doc.crates.io/specifying-dependencies.html diff --git a/src-en/testing/doc_testing.md b/src-en/testing/doc_testing.md index 642cbe8..4015ddc 100644 --- a/src-en/testing/doc_testing.md +++ b/src-en/testing/doc_testing.md @@ -1,9 +1,10 @@ # Documentation testing The primary way of documenting a Rust project is through annotating the source -code. Documentation comments are written in [markdown] and support code -blocks in them. Rust takes care about correctness, so these code blocks are -compiled and used as tests. +code. Documentation comments are written in +[CommonMark Markdown specification][commonmark] and support code blocks in them. +Rust takes care about correctness, so these code blocks are compiled and used +as documentation tests. ```rust,ignore /// First line is a short summary describing function. @@ -48,7 +49,8 @@ pub fn div(a: i32, b: i32) -> i32 { } ``` -Tests can be run with `cargo test`: +Code blocks in documentation are automatically tested +when running the regular `cargo test` command: ```shell $ cargo test @@ -73,16 +75,16 @@ the functionality, which is one of the most important [guidelines][question-instead-of-unwrap]. It allows using examples from docs as complete code snippets. But using `?` makes compilation fail since `main` returns `unit`. The ability to hide some source lines from documentation comes -to the rescue: one may write `fn try_main() -> Result<(), ErrorType>`, hide it and -`unwrap` it in hidden `main`. Sounds complicated? Here's an example: +to the rescue: one may write `fn try_main() -> Result<(), ErrorType>`, hide it +and `unwrap` it in hidden `main`. Sounds complicated? Here's an example: ```rust,ignore /// Using hidden `try_main` in doc tests. /// /// ``` -/// # // hidden lines start with `#` symbol, but they're still compileable! +/// # // hidden lines start with `#` symbol, but they're still compilable! /// # fn try_main() -> Result<(), String> { // line that wraps the body shown in doc -/// let res = try::try_div(10, 2)?; +/// let res = doccomments::try_div(10, 2)?; /// # Ok(()) // returning from try_main /// # } /// # fn main() { // starting main that'll unwrap() @@ -105,6 +107,6 @@ pub fn try_div(a: i32, b: i32) -> Result { * [API Guidelines][doc-nursery] on documentation guidelines [doc-nursery]: https://rust-lang-nursery.github.io/api-guidelines/documentation.html -[markdown]: https://daringfireball.net/projects/markdown/ +[commonmark]: https://commonmark.org/ [RFC505]: https://github.com/rust-lang/rfcs/blob/master/text/0505-api-comment-conventions.md [question-instead-of-unwrap]: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#examples-use--not-try-not-unwrap-c-question-mark diff --git a/src-en/testing/integration_testing.md b/src-en/testing/integration_testing.md index a4345ae..60406e3 100644 --- a/src-en/testing/integration_testing.md +++ b/src-en/testing/integration_testing.md @@ -47,11 +47,11 @@ running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -Each Rust source file in `tests` directory is compiled as a separate crate. One -way of sharing some code between integration tests is making module with public +Each Rust source file in the `tests` directory is compiled as a separate crate. In +order to share some code between integration tests we can make a module with public functions, importing and using it within tests. -File `tests/common.rs`: +File `tests/common/mod.rs`: ```rust,ignore pub fn setup() { @@ -74,8 +74,9 @@ fn test_add() { } ``` -Modules with common code follow the ordinary [modules][mod] rules, so it's ok to -create common module as `tests/common/mod.rs`. +Creating the module as `tests/common.rs` also works, but is not recommended +because the test runner will treat the file as a test crate and try to run tests +inside it. [unit]: unit_testing.md [mod]: ../mod.md diff --git a/src-en/testing/unit_testing.md b/src-en/testing/unit_testing.md index cd87706..f476e5e 100644 --- a/src-en/testing/unit_testing.md +++ b/src-en/testing/unit_testing.md @@ -71,6 +71,7 @@ test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out ``` ## Tests and `?` + None of the previous unit test examples had a return type. But in Rust 2018, your unit tests can return `Result<()>`, which lets you use `?` in them! This can make them much more concise. @@ -199,7 +200,7 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Tests can be marked with the `#[ignore]` attribute to exclude some tests. Or to run them with command `cargo test -- --ignored` -```rust +```rust,ignore pub fn add(a: i32, b: i32) -> i32 { a + b } diff --git a/src-en/trait.md b/src-en/trait.md index 7878d47..3c297f5 100644 --- a/src-en/trait.md +++ b/src-en/trait.md @@ -4,18 +4,18 @@ A `trait` is a collection of methods defined for an unknown type: `Self`. They can access other methods declared in the same trait. Traits can be implemented for any data type. In the example below, -we define `Animal`, a group of methods. The `Animal` `trait` is -then implemented for the `Sheep` data type, allowing the use of +we define `Animal`, a group of methods. The `Animal` `trait` is +then implemented for the `Sheep` data type, allowing the use of methods from `Animal` with a `Sheep`. ```rust,editable struct Sheep { naked: bool, name: &'static str } trait Animal { - // Static method signature; `Self` refers to the implementor type. + // Associated function signature; `Self` refers to the implementor type. fn new(name: &'static str) -> Self; - // Instance method signatures; these will return a string. + // Method signatures; these will return a string. fn name(&self) -> &'static str; fn noise(&self) -> &'static str; @@ -77,4 +77,4 @@ fn main() { dolly.shear(); dolly.talk(); } -``` \ No newline at end of file +``` diff --git a/src-en/trait/clone.md b/src-en/trait/clone.md index 5d6747a..8e04f5a 100644 --- a/src-en/trait/clone.md +++ b/src-en/trait/clone.md @@ -40,11 +40,11 @@ fn main() { // Clone `moved_pair` into `cloned_pair` (resources are included) let cloned_pair = moved_pair.clone(); - // Drop the original pair using std::mem::drop + // Drop the moved original pair using std::mem::drop drop(moved_pair); // Error! `moved_pair` has been dropped - //println!("copy: {:?}", moved_pair); + //println!("moved and dropped: {:?}", moved_pair); // TODO ^ Try uncommenting this line // The result from .clone() can still be used! diff --git a/src-en/trait/derive.md b/src-en/trait/derive.md index 4769efe..39c958f 100644 --- a/src-en/trait/derive.md +++ b/src-en/trait/derive.md @@ -5,6 +5,7 @@ the `#[derive]` [attribute][attribute]. These traits can still be manually implemented if a more complex behavior is required. The following is a list of derivable traits: + * Comparison traits: [`Eq`][eq], [`PartialEq`][partial-eq], [`Ord`][ord], [`PartialOrd`][partial-ord]. * [`Clone`][clone], to create `T` from `&T` via a copy. @@ -12,7 +13,7 @@ The following is a list of derivable traits: * [`Hash`][hash], to compute a hash from `&T`. * [`Default`][default], to create an empty instance of a data type. * [`Debug`][debug], to format a value using the `{:?}` formatter. - + ```rust,editable // `Centimeters`, a tuple struct that can be compared #[derive(PartialEq, PartialOrd)] @@ -62,6 +63,7 @@ fn main() { ``` ### See also: + [`derive`][derive] [attribute]: ../attribute.md diff --git a/src-en/trait/disambiguating.md b/src-en/trait/disambiguating.md index c893e9d..b056095 100644 --- a/src-en/trait/disambiguating.md +++ b/src-en/trait/disambiguating.md @@ -1,9 +1,11 @@ # Disambiguating overlapping traits -A type can implement many different traits. What if two traits both require the same name? For example, many traits might have a method named `get()`. They might even have different return types! +A type can implement many different traits. What if two traits both require +the same name for a function? For example, many traits might have a method +named `get()`. They might even have different return types! -Good news: because each trait implementation gets its own `impl` block, it's -clear which trait's `get` method you're implementing. +Good news: because each trait implementation gets its own `impl` block, it's +clear which trait's `get` method you're implementing. What about when it comes time to _call_ those methods? To disambiguate between them, we have to use Fully Qualified Syntax. @@ -38,12 +40,12 @@ impl AgeWidget for Form { } fn main() { - let form = Form{ + let form = Form { username: "rustacean".to_owned(), age: 28, }; - // If you uncomment this line, you'll get an error saying + // If you uncomment this line, you'll get an error saying // "multiple `get` found". Because, after all, there are multiple methods // named `get`. // println!("{}", form.get()); diff --git a/src-en/trait/drop.md b/src-en/trait/drop.md index 7aeaca7..a3e481e 100644 --- a/src-en/trait/drop.md +++ b/src-en/trait/drop.md @@ -1,6 +1,6 @@ # Drop -The [`Drop`][Drop] trait only has one method: `drop`, which is called automatically +The [`Drop`][Drop] trait only has one method: `drop`, which is called automatically when an object goes out of scope. The main use of the `Drop` trait is to free the resources that the implementor instance owns. @@ -54,4 +54,56 @@ fn main() { } ``` -[Drop]: https://doc.rust-lang.org/std/ops/trait.Drop.html \ No newline at end of file +For a more practical example, here's how the `Drop` trait can be used to automatically +clean up temporary files when they're no longer needed: + +```rust,editable +use std::fs::File; +use std::path::PathBuf; + +struct TempFile { + file: File, + path: PathBuf, +} + +impl TempFile { + fn new(path: PathBuf) -> std::io::Result { + // Note: File::create() will overwrite existing files + let file = File::create(&path)?; + + Ok(Self { file, path }) + } +} + +// When TempFile is dropped: +// 1. First, the File will be automatically closed (Drop for File) +// 2. Then our drop implementation will remove the file +impl Drop for TempFile { + fn drop(&mut self) { + // Note: File is already closed at this point + if let Err(e) = std::fs::remove_file(&self.path) { + eprintln!("Failed to remove temporary file: {}", e); + } + println!("> Dropped temporary file: {:?}", self.path); + } +} + +fn main() -> std::io::Result<()> { + // Create a new scope to demonstrate drop behavior + { + let temp = TempFile::new("test.txt".into())?; + println!("Temporary file created"); + // File will be automatically cleaned up when temp goes out of scope + } + println!("End of scope - file should be cleaned up"); + + // We can also manually drop if needed + let temp2 = TempFile::new("another_test.txt".into())?; + drop(temp2); // Explicitly drop the file + println!("Manually dropped file"); + + Ok(()) +} +``` + +[Drop]: https://doc.rust-lang.org/std/ops/trait.Drop.html diff --git a/src-en/trait/dyn.md b/src-en/trait/dyn.md index 5b2f8c2..e5cd224 100644 --- a/src-en/trait/dyn.md +++ b/src-en/trait/dyn.md @@ -1,10 +1,18 @@ # Returning Traits with `dyn` -The Rust compiler needs to know how much space every function's return type requires. This means all your functions have to return a concrete type. Unlike other languages, if you have a trait like `Animal`, you can't write a function that returns `Animal`, because its different implementations will need different amounts of memory. +The Rust compiler needs to know how much space every function's return type requires. This means all +your functions have to return a concrete type. Unlike other languages, if you have a trait like +`Animal`, you can't write a function that returns `Animal`, because its different implementations +will need different amounts of memory. -However, there's an easy workaround. Instead of returning a trait object directly, our functions return a `Box` which _contains_ some `Animal`. A `box` is just a reference to some memory in the heap. Because a reference has a statically-known size, and the compiler can guarantee it points to a heap-allocated `Animal`, we can return a trait from our function! +However, there's an easy workaround. Instead of returning a trait object directly, our functions +return a `Box` which _contains_ some `Animal`. A `box` is just a reference to some memory in the +heap. Because a reference has a statically-known size, and the compiler can guarantee it points to a +heap-allocated `Animal`, we can return a trait from our function! -Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the `dyn` keyword, e.g. `Box`. +Rust tries to be as explicit as possible whenever it allocates memory on the heap. So if your +function returns a pointer-to-trait-on-heap in this way, you need to write the return type with the +`dyn` keyword, e.g. `Box`. ```rust,editable struct Sheep {} @@ -44,4 +52,4 @@ fn main() { println!("You've randomly chosen an animal, and it says {}", animal.noise()); } -``` \ No newline at end of file +``` diff --git a/src-en/trait/impl_trait.md b/src-en/trait/impl_trait.md index 7b87bd2..9b6618e 100644 --- a/src-en/trait/impl_trait.md +++ b/src-en/trait/impl_trait.md @@ -1,5 +1,55 @@ # `impl Trait` +`impl Trait` can be used in two locations: + +1. as an argument type +2. as a return type + +## As an argument type + +If your function is generic over a trait but you don't mind the specific type, you can simplify the function declaration using `impl Trait` as the type of the argument. + +For example, consider the following code: + +```rust,editable +fn parse_csv_document(src: R) -> std::io::Result>> { + src.lines() + .map(|line| { + // For each line in the source + line.map(|line| { + // If the line was read successfully, process it, if not, return the error + line.split(',') // Split the line separated by commas + .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace + .collect() // Collect all strings in a row into a Vec + }) + }) + .collect() // Collect all lines into a Vec> +} +``` + +`parse_csv_document` is generic, allowing it to take any type which implements BufRead, such as `BufReader` or `[u8]`, +but it's not important what type `R` is, and `R` is only used to declare the type of `src`, so the function can also be written as: + +```rust,editable +fn parse_csv_document(src: impl std::io::BufRead) -> std::io::Result>> { + src.lines() + .map(|line| { + // For each line in the source + line.map(|line| { + // If the line was read successfully, process it, if not, return the error + line.split(',') // Split the line separated by commas + .map(|entry| String::from(entry.trim())) // Remove leading and trailing whitespace + .collect() // Collect all strings in a row into a Vec + }) + }) + .collect() // Collect all lines into a Vec> +} +``` + +Note that using `impl Trait` as an argument type means that you cannot explicitly state what form of the function you use, i.e. `parse_csv_document::(std::io::empty())` will not work with the second example. + +## As a return type + If your function returns a type that implements `MyTrait`, you can write its return type as `-> impl MyTrait`. This can help simplify your type signatures quite a lot! @@ -68,4 +118,10 @@ fn double_positives<'a>(numbers: &'a Vec) -> impl Iterator + 'a .filter(|x| x > &&0) .map(|x| x * 2) } + +fn main() { + let singles = vec![-3, -2, 2, 3]; + let doubles = double_positives(&singles); + assert_eq!(doubles.collect::>(), vec![4, 6]); +} ``` diff --git a/src-en/trait/iter.md b/src-en/trait/iter.md index 9c95409..611c18b 100644 --- a/src-en/trait/iter.md +++ b/src-en/trait/iter.md @@ -1,12 +1,13 @@ # Iterators -The [`Iterator`][iter] trait is used to implement iterators over collections such as arrays. +The [`Iterator`][iter] trait is used to implement iterators over collections +such as arrays. -The trait requires only a method to be defined for the `next` element, -which may be manually defined in an `impl` block or automatically +The trait requires only a method to be defined for the `next` element, +which may be manually defined in an `impl` block or automatically defined (as in arrays and ranges). -As a point of convenience for common situations, the `for` construct +As a point of convenience for common situations, the `for` construct turns some collections into iterators using the [`.into_iter()`][intoiter] method. ```rust,editable @@ -16,23 +17,27 @@ struct Fibonacci { } // Implement `Iterator` for `Fibonacci`. -// The `Iterator` trait only requires a method to be defined for the `next` element. +// The `Iterator` trait only requires a method to be defined for the `next` element, +// and an `associated type` to declare the return type of the iterator. impl Iterator for Fibonacci { + // We can refer to this type using Self::Item type Item = u32; - + // Here, we define the sequence using `.curr` and `.next`. // The return type is `Option`: // * When the `Iterator` is finished, `None` is returned. // * Otherwise, the next value is wrapped in `Some` and returned. - fn next(&mut self) -> Option { - let new_next = self.curr + self.next; + // We use Self::Item in the return type, so we can change + // the type without having to update the function signatures. + fn next(&mut self) -> Option { + let current = self.curr; self.curr = self.next; - self.next = new_next; + self.next = current + self.next; // Since there's no endpoint to a Fibonacci sequence, the `Iterator` // will never return `None`, and `Some` is always returned. - Some(self.curr) + Some(current) } } diff --git a/src-en/types.md b/src-en/types.md index 1d3b50a..24bad81 100644 --- a/src-en/types.md +++ b/src-en/types.md @@ -2,6 +2,7 @@ Rust provides several mechanisms to change or define the type of primitive and user defined types. The following sections cover: + * [Casting] between primitive types * Specifying the desired type of [literals] * Using [type inference] diff --git a/src-en/types/alias.md b/src-en/types/alias.md index 5fda947..4aad7be 100644 --- a/src-en/types/alias.md +++ b/src-en/types/alias.md @@ -5,19 +5,15 @@ must have `UpperCamelCase` names, or the compiler will raise a warning. The exception to this rule are the primitive types: `usize`, `f32`, etc. ```rust,editable -// `NanoSecond` is a new name for `u64`. +// `NanoSecond`, `Inch`, and `U64` are new names for `u64`. type NanoSecond = u64; type Inch = u64; - -// Use an attribute to silence warning. -#[allow(non_camel_case_types)] -type u64_t = u64; -// TODO ^ Try removing the attribute +type U64 = u64; fn main() { - // `NanoSecond` = `Inch` = `u64_t` = `u64`. - let nanoseconds: NanoSecond = 5 as u64_t; - let inches: Inch = 2 as u64_t; + // `NanoSecond` = `Inch` = `U64` = `u64`. + let nanoseconds: NanoSecond = 5 as u64; + let inches: Inch = 2 as U64; // Note that type aliases *don't* provide any extra type safety, because // aliases are *not* new types @@ -28,8 +24,8 @@ fn main() { } ``` -The main use of aliases is to reduce boilerplate; for example the `IoResult` type -is an alias for the `Result` type. +The main use of aliases is to reduce boilerplate; for example the `io::Result` type +is an alias for the `Result` type. ### See also: diff --git a/src-en/types/cast.md b/src-en/types/cast.md index 2ca18c1..7e944d0 100644 --- a/src-en/types/cast.md +++ b/src-en/types/cast.md @@ -22,7 +22,8 @@ fn main() { let integer = decimal as u8; let character = integer as char; - // Error! There are limitations in conversion rules. A float cannot be directly converted to a char. + // Error! There are limitations in conversion rules. + // A float cannot be directly converted to a char. let character = decimal as char; // FIXME ^ Comment out this line @@ -51,33 +52,38 @@ fn main() { // Unless it already fits, of course. println!(" 128 as a i16 is: {}", 128 as i16); - // 128 as u8 -> 128, whose two's complement in eight bits is: + + // In boundary case 128 value in 8-bit two's complement representation is -128 println!(" 128 as a i8 is : {}", 128 as i8); // repeating the example above // 1000 as u8 -> 232 println!("1000 as a u8 is : {}", 1000 as u8); - // and the two's complement of 232 is -24 + // and the value of 232 in 8-bit two's complement representation is -24 println!(" 232 as a i8 is : {}", 232 as i8); - - // Since Rust 1.45, the `as` keyword performs a *saturating cast* when casting from float to int. - // If the floating point value exceeds the upper bound or is less than the lower bound, the returned value will be equal to the bound crossed. - - // 300.0 is 255 - println!("300.0 is {}", 300.0_f32 as u8); + + // Since Rust 1.45, the `as` keyword performs a *saturating cast* + // when casting from float to int. If the floating point value exceeds + // the upper bound or is less than the lower bound, the returned value + // will be equal to the bound crossed. + + // 300.0 as u8 is 255 + println!(" 300.0 as u8 is : {}", 300.0_f32 as u8); // -100.0 as u8 is 0 - println!("-100.0 as u8 is {}", -100.0_f32 as u8); + println!("-100.0 as u8 is : {}", -100.0_f32 as u8); // nan as u8 is 0 - println!("nan as u8 is {}", f32::NAN as u8); - - // This behavior incures a small runtime cost and can be avoided with unsafe methods, however the results might overflow and return **unsound values**. Use these methods wisely: + println!(" nan as u8 is : {}", f32::NAN as u8); + + // This behavior incurs a small runtime cost and can be avoided + // with unsafe methods, however the results might overflow and + // return **unsound values**. Use these methods wisely: unsafe { - // 300.0 is 44 - println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); + // 300.0 as u8 is 44 + println!(" 300.0 as u8 is : {}", 300.0_f32.to_int_unchecked::()); // -100.0 as u8 is 156 - println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); + println!("-100.0 as u8 is : {}", (-100.0_f32).to_int_unchecked::()); // nan as u8 is 0 - println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); + println!(" nan as u8 is : {}", f32::NAN.to_int_unchecked::()); } } ``` diff --git a/src-en/types/inference.md b/src-en/types/inference.md index 5d10301..8822650 100644 --- a/src-en/types/inference.md +++ b/src-en/types/inference.md @@ -2,7 +2,7 @@ The type inference engine is pretty smart. It does more than looking at the type of the value expression -during an initialization. It also looks at how the variable is used afterwards +during an initialization. It also looks at how the variable is used afterwards to infer its type. Here's an advanced example of type inference: ```rust,editable diff --git a/src-en/types/literals.md b/src-en/types/literals.md index fd26b9d..275b2f5 100644 --- a/src-en/types/literals.md +++ b/src-en/types/literals.md @@ -1,6 +1,6 @@ # Literals -Numeric literals can be type annotated by adding the type as a suffix. As an example, +Numeric literals can be type annotated by adding the type as a suffix. As an example, to specify that the literal `42` should have the type `i32`, write `42i32`. The type of unsuffixed numeric literals will depend on how they are used. If no diff --git a/src-en/unsafe.md b/src-en/unsafe.md index 1eeec6b..fe83672 100644 --- a/src-en/unsafe.md +++ b/src-en/unsafe.md @@ -8,11 +8,12 @@ things that unsafe is used for: * dereferencing raw pointers * calling functions or methods which are `unsafe` (including calling a function - over FFI, see [a previous chapter](std_misc/ffi.md) of the book) + over FFI, see [a previous chapter](std_misc/ffi.md) of the book) * accessing or modifying static mutable variables * implementing unsafe traits ### Raw Pointers + Raw pointers `*` and references `&T` function similarly, but references are always safe because they are guaranteed to point to valid data due to the borrow checker. Dereferencing a raw pointer can only be done through an unsafe @@ -29,6 +30,7 @@ fn main() { ``` ### Calling Unsafe Functions + Some functions can be declared as `unsafe`, meaning it is the programmer's responsibility to ensure correctness instead of the compiler's. One example of this is [`std::slice::from_raw_parts`] which will create a slice given a @@ -51,11 +53,10 @@ fn main() { } ``` -For `slice::from_raw_parts`, one of the assumptions which *must* be upheld is +For `slice::from_raw_parts`, one of the assumptions which *must* be upheld is that the pointer passed in points to valid memory and that the memory pointed to -is of the correct type. If these invariants aren't upheld then the program's +is of the correct type. If these invariants aren't upheld then the program's behaviour is undefined and there is no knowing what will happen. - [unsafe]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html [`std::slice::from_raw_parts`]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html diff --git a/src-en/unsafe/asm.md b/src-en/unsafe/asm.md new file mode 100644 index 0000000..3c78436 --- /dev/null +++ b/src-en/unsafe/asm.md @@ -0,0 +1,489 @@ +# Inline assembly + +Rust provides support for inline assembly via the `asm!` macro. +It can be used to embed handwritten assembly in the assembly output generated by the compiler. +Generally this should not be necessary, but might be where the required performance or timing +cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality. + +> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported. + +Inline assembly is currently supported on the following architectures: + +- x86 and x86-64 +- ARM +- AArch64 +- RISC-V + +## Basic usage + +Let us start with the simplest possible example: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +unsafe { + asm!("nop"); +} +# } +``` + +This will insert a NOP (no operation) instruction into the assembly generated by the compiler. +Note that all `asm!` invocations have to be inside an `unsafe` block, as they could insert +arbitrary instructions and break various invariants. The instructions to be inserted are listed +in the first argument of the `asm!` macro as a string literal. + +## Inputs and outputs + +Now inserting an instruction that does nothing is rather boring. Let us do something that +actually acts on data: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let x: u64; +unsafe { + asm!("mov {}, 5", out(reg) x); +} +assert_eq!(x, 5); +# } +``` + +This will write the value `5` into the `u64` variable `x`. +You can see that the string literal we use to specify instructions is actually a template string. +It is governed by the same rules as Rust [format strings][format-syntax]. +The arguments that are inserted into the template however look a bit different than you may +be familiar with. First we need to specify if the variable is an input or an output of the +inline assembly. In this case it is an output. We declared this by writing `out`. +We also need to specify in what kind of register the assembly expects the variable. +In this case we put it in an arbitrary general purpose register by specifying `reg`. +The compiler will choose an appropriate register to insert into +the template and will read the variable from there after the inline assembly finishes executing. + +[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax + +Let us see another example that also uses an input: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let i: u64 = 3; +let o: u64; +unsafe { + asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, + ); +} +assert_eq!(o, 8); +# } +``` + +This will add `5` to the input in variable `i` and write the result to variable `o`. +The particular way this assembly does this is first copying the value from `i` to the output, +and then adding `5` to it. + +The example shows a few things: + +First, we can see that `asm!` allows multiple template string arguments; each +one is treated as a separate line of assembly code, as if they were all joined +together with newlines between them. This makes it easy to format assembly +code. + +Second, we can see that inputs are declared by writing `in` instead of `out`. + +Third, we can see that we can specify an argument number, or name as in any format string. +For inline assembly templates this is particularly useful as arguments are often used more than once. +For more complex inline assembly using this facility is generally recommended, as it improves +readability, and allows reordering instructions without changing the argument order. + +We can further refine the above example to avoid the `mov` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut x: u64 = 3; +unsafe { + asm!("add {0}, 5", inout(reg) x); +} +assert_eq!(x, 8); +# } +``` + +We can see that `inout` is used to specify an argument that is both input and output. +This is different from specifying an input and output separately in that it is guaranteed to assign both to the same register. + +It is also possible to specify different variables for the input and output parts of an `inout` operand: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let x: u64 = 3; +let y: u64; +unsafe { + asm!("add {0}, 5", inout(reg) x => y); +} +assert_eq!(y, 8); +# } +``` + +## Late output operands + +The Rust compiler is conservative with its allocation of operands. It is assumed that an `out` +can be written at any time, and can therefore not share its location with any other argument. +However, to guarantee optimal performance it is important to use as few registers as possible, +so they won't have to be saved and reloaded around the inline assembly block. +To achieve this Rust provides a `lateout` specifier. This can be used on any output that is +written only after all inputs have been consumed. There is also an `inlateout` variant of this +specifier. + +Here is an example where `inlateout` *cannot* be used in `release` mode or other optimized cases: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +let c: u64 = 4; +unsafe { + asm!( + "add {0}, {1}", + "add {0}, {2}", + inout(reg) a, + in(reg) b, + in(reg) c, + ); +} +assert_eq!(a, 12); +# } +``` + +In unoptimized cases (e.g. `Debug` mode), replacing `inout(reg) a` with `inlateout(reg) a` in the +above example can continue to give the expected result. However, with `release` mode or other +optimized cases, using `inlateout(reg) a` can instead lead to the final value `a = 16`, causing the +assertion to fail. + +This is because in optimized cases, the compiler is free to allocate the same register for inputs +`b` and `c` since it knows that they have the same value. Furthermore, when `inlateout` is used, `a` +and `c` could be allocated to the same register, in which case the first `add` instruction would +overwrite the initial load from variable `c`. This is in contrast to how using `inout(reg) a` +ensures a separate register is allocated for `a`. + +However, the following example can use `inlateout` since the output is only modified after all input +registers have been read: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +unsafe { + asm!("add {0}, {1}", inlateout(reg) a, in(reg) b); +} +assert_eq!(a, 8); +# } +``` + +As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register. + +## Explicit register operands + +Some instructions require that the operands be in a specific register. +Therefore, Rust inline assembly provides some more specific constraint specifiers. +While `reg` is generally available on any architecture, explicit registers are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi` among others can be addressed by their name. + +```rust,no_run +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let cmd = 0xd1; +unsafe { + asm!("out 0x64, eax", in("eax") cmd); +} +# } +``` + +In this example we call the `out` instruction to output the content of the `cmd` variable to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand we had to use the `eax` constraint specifier. + +> **Note**: unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types. + +Consider this example which uses the x86 `mul` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +fn mul(a: u64, b: u64) -> u128 { + let lo: u64; + let hi: u64; + + unsafe { + asm!( + // The x86 mul instruction takes rax as an implicit input and writes + // the 128-bit result of the multiplication to rax:rdx. + "mul {}", + in(reg) a, + inlateout("rax") b => lo, + lateout("rdx") hi + ); + } + + ((hi as u128) << 64) + lo as u128 +} +# } +``` + +This uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result. +The only explicit operand is a register, that we fill from the variable `a`. +The second operand is implicit, and must be the `rax` register, which we fill from the variable `b`. +The lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`. +The higher 64 bits are stored in `rdx` from which we fill the variable `hi`. + +## Clobbered registers + +In many cases inline assembly will modify state that is not needed as an output. +Usually this is either because we have to use a scratch register in the assembly or because instructions modify state that we don't need to further examine. +This state is generally referred to as being "clobbered". +We need to tell the compiler about this since it may need to save and restore this state around the inline assembly block. + +```rust +use std::arch::asm; + +# #[cfg(target_arch = "x86_64")] +fn main() { + // three entries of four bytes each + let mut name_buf = [0_u8; 12]; + // String is stored as ascii in ebx, edx, ecx in order + // Because ebx is reserved, the asm needs to preserve the value of it. + // So we push and pop it around the main asm. + // 64 bit mode on 64 bit processors does not allow pushing/popping of + // 32 bit registers (like ebx), so we have to use the extended rbx register instead. + + unsafe { + asm!( + "push rbx", + "cpuid", + "mov [rdi], ebx", + "mov [rdi + 4], edx", + "mov [rdi + 8], ecx", + "pop rbx", + // We use a pointer to an array for storing the values to simplify + // the Rust code at the cost of a couple more asm instructions + // This is more explicit with how the asm works however, as opposed + // to explicit register outputs such as `out("ecx") val` + // The *pointer itself* is only an input even though it's written behind + in("rdi") name_buf.as_mut_ptr(), + // select cpuid 0, also specify eax as clobbered + inout("eax") 0 => _, + // cpuid clobbers these registers too + out("ecx") _, + out("edx") _, + ); + } + + let name = core::str::from_utf8(&name_buf).unwrap(); + println!("CPU Manufacturer ID: {}", name); +} + +# #[cfg(not(target_arch = "x86_64"))] +# fn main() {} +``` + +In the example above we use the `cpuid` instruction to read the CPU manufacturer ID. +This instruction writes to `eax` with the maximum supported `cpuid` argument and `ebx`, `edx`, and `ecx` with the CPU manufacturer ID as ASCII bytes in that order. + +Even though `eax` is never read we still need to tell the compiler that the register has been modified so that the compiler can save any values that were in these registers before the asm. This is done by declaring it as an output but with `_` instead of a variable name, which indicates that the output value is to be discarded. + +This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an input or output **except** if the compiler uses it to fulfill a general register class (e.g. `in(reg)`). This makes `reg` operands dangerous when using reserved registers as we could unknowingly corrupt our input or output because they share the same register. + +To work around this we use `rdi` to store the pointer to the output array, save `ebx` via `push`, read from `ebx` inside the asm block into the array and then restore `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`. + +This can also be used with a general register class to obtain a scratch register for use inside the asm code: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +// Multiply x by 6 using shifts and adds +let mut x: u64 = 4; +unsafe { + asm!( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); +} +assert_eq!(x, 4 * 6); +# } +``` + +## Symbol operands and ABI clobbers + +By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`] argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered. Multiple `clobber_abi` arguments may be provided and all clobbers from all specified ABIs will be inserted. + +[`clobber_abi`]: https://doc.rust-lang.org/stable/reference/inline-assembly.html#abi-clobbers + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +extern "C" fn foo(arg: i32) -> i32 { + println!("arg = {}", arg); + arg * 2 +} + +fn call_foo(arg: i32) -> i32 { + unsafe { + let result; + asm!( + "call {}", + // Function pointer to call + in(reg) foo, + // 1st argument in rdi + in("rdi") arg, + // Return value in rax + out("rax") result, + // Mark all registers which are not preserved by the "C" calling + // convention as clobbered. + clobber_abi("C"), + ); + result + } +} +# } +``` + +## Register template modifiers + +In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register). + +By default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc). + +This default can be overridden by using modifiers on the template string operands, just like you would with format strings: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut x: u16 = 0xab; + +unsafe { + asm!("mov {0:h}, {0:l}", inout(reg_abcd) x); +} + +assert_eq!(x, 0xabab); +# } +``` + +In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 registers (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently. + +Let us assume that the register allocator has chosen to allocate `x` in the `ax` register. +The `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte. + +If you use a smaller data type (e.g. `u16`) with an operand and forget to use template modifiers, the compiler will emit a warning and suggest the correct modifier to use. + +## Memory address operands + +Sometimes assembly instructions require operands passed via memory addresses/memory locations. +You have to manually use the memory address syntax specified by the target architecture. +For example, on x86/x86_64 using Intel assembly syntax, you should wrap inputs/outputs in `[]` to indicate they are memory operands: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +fn load_fpu_control_word(control: u16) { + unsafe { + asm!("fldcw [{}]", in(reg) &control, options(nostack)); + } +} +# } +``` + +## Labels + +Any reuse of a named label, local or otherwise, can result in an assembler or linker error or may cause other strange behavior. Reuse of a named label can happen in a variety of ways including: + +- explicitly: using a label more than once in one `asm!` block, or multiple times across blocks. +- implicitly via inlining: the compiler is allowed to instantiate multiple copies of an `asm!` block, for example when the function containing it is inlined in multiple places. +- implicitly via LTO: LTO can cause code from *other crates* to be placed in the same codegen unit, and so could bring in arbitrary labels. + +As a consequence, you should only use GNU assembler **numeric** [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. + +Moreover, on x86 when using the default Intel syntax, due to [an LLVM bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. Using `options(att_syntax)` will avoid any ambiguity, but that affects the syntax of the *entire* `asm!` block. (See [Options](#options), below, for more on `options`.) + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a = 0; +unsafe { + asm!( + "mov {0}, 10", + "2:", + "sub {0}, 1", + "cmp {0}, 3", + "jle 2f", + "jmp 2b", + "2:", + "add {0}, 2", + out(reg) a + ); +} +assert_eq!(a, 5); +# } +``` + +This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`. + +This example shows a few things: + +- First, that the same number can be used as a label multiple times in the same inline block. +- Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes “b” (“backward”) or ”f” (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction. + +[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels +[an LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + +## Options {#options} + +By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However, in many cases it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. + +Let's take our previous example of an `add` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +unsafe { + asm!( + "add {0}, {1}", + inlateout(reg) a, in(reg) b, + options(pure, nomem, nostack), + ); +} +assert_eq!(a, 8); +# } +``` + +Options can be provided as an optional final argument to the `asm!` macro. We specified three options here: + +- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely. +- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global). +- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments. + +These allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed. + +See the [reference](https://doc.rust-lang.org/stable/reference/inline-assembly.html) for the full list of available options and their effects. diff --git a/src-en/variable_bindings.md b/src-en/variable_bindings.md index b280ed5..4e23c11 100644 --- a/src-en/variable_bindings.md +++ b/src-en/variable_bindings.md @@ -26,5 +26,6 @@ fn main() { let noisy_unused_variable = 2u32; // FIXME ^ Prefix with an underscore to suppress the warning + // Please note that warnings may not be shown in a browser } -``` \ No newline at end of file +``` diff --git a/src-en/variable_bindings/declare.md b/src-en/variable_bindings/declare.md index e9dee6a..9d2382a 100644 --- a/src-en/variable_bindings/declare.md +++ b/src-en/variable_bindings/declare.md @@ -1,8 +1,10 @@ # Declare first -It's possible to declare variable bindings first, and initialize them later. -However, this form is seldom used, as it may lead to the use of uninitialized -variables. +It is possible to declare variable bindings first and initialize them later, but all variable bindings must be initialized before they are used: the compiler forbids use of uninitialized variable bindings, as it would lead to undefined behavior. + +It is not common to declare a variable binding and initialize it later in the function. +It is more difficult for a reader to find the initialization when initialization is separated from declaration. +It is common to declare and initialize a variable binding near where the variable will be used. ```rust,editable,ignore,mdbook-runnable fn main() { @@ -30,5 +32,4 @@ fn main() { } ``` -The compiler forbids use of uninitialized variables, as this would lead to -undefined behavior. + diff --git a/src-en/variable_bindings/freeze.md b/src-en/variable_bindings/freeze.md index b399570..d99b1bd 100644 --- a/src-en/variable_bindings/freeze.md +++ b/src-en/variable_bindings/freeze.md @@ -1,6 +1,6 @@ # Freezing -When data is bound by the same name immutably, it also *freezes*. *Frozen* data can't be +When data is bound by the same name immutably, it also *freezes*. *Frozen* data can't be modified until the immutable binding goes out of scope: ```rust,editable,ignore,mdbook-runnable diff --git a/src-en/variable_bindings/mut.md b/src-en/variable_bindings/mut.md index 0925132..c75f034 100644 --- a/src-en/variable_bindings/mut.md +++ b/src-en/variable_bindings/mut.md @@ -15,9 +15,8 @@ fn main() { println!("After mutation: {}", mutable_binding); - // Error! + // Error! Cannot assign a new value to an immutable variable _immutable_binding += 1; - // FIXME ^ Comment out this line } ``` diff --git a/src-en/variable_bindings/scope.md b/src-en/variable_bindings/scope.md index 9e3c79e..0ac6691 100644 --- a/src-en/variable_bindings/scope.md +++ b/src-en/variable_bindings/scope.md @@ -1,7 +1,8 @@ # Scope and Shadowing Variable bindings have a scope, and are constrained to live in a *block*. A -block is a collection of statements enclosed by braces `{}`. +block is a collection of statements enclosed by braces `{}`. + ```rust,editable,ignore,mdbook-runnable fn main() { // This binding lives in the main function @@ -23,7 +24,9 @@ fn main() { println!("outer long: {}", long_lived_binding); } ``` + Also, [variable shadowing][variable-shadow] is allowed. + ```rust,editable,ignore,mdbook-runnable fn main() { let shadowed_binding = 1; @@ -43,4 +46,5 @@ fn main() { println!("shadowed in outer block: {}", shadowed_binding); } ``` + [variable-shadow]: https://en.wikipedia.org/wiki/Variable_shadowing From 6d14df997f5b9e8493aa7dc6bfb907da5ffa5c1a Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sun, 6 Apr 2025 14:14:09 +0900 Subject: [PATCH 02/11] =?UTF-8?q?=EC=A0=9C2=EC=9E=A5=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=9E=90=EB=A3=8C=ED=98=95=EA=B9=8C=EC=A7=80=20=EB=B2=88?= =?UTF-8?q?=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추가된 예제 반영 - 한글 변수 사용 가능성 반영 - 일부 역주 추가 gh-pages.yml 패치: - Patch deprecations * ubuntu-18.04 not served anymore * peaceiris/actions-gh-pages removed - Be more modern outputs * using actions/upload-artifact instead of deploying on branch * using actions/deploy-pages to directly publish builts @origin hash:c9bc71c25a5e9ebf87311815c7f924feea86fd00 hash:c9f73d3babb3c7974f5673991d939afab826c034 --- .github/workflows/gh-pages.yml | 44 +++++++++++++++++++++++++--------- src/hello/comment.md | 3 ++- src/hello/print.md | 40 ++++++++++++++++++++++++++----- src/hello/print/fmt.md | 6 ++++- src/index.md | 16 ++++++------- src/primitives.md | 9 +++++++ src/primitives/array.md | 19 ++++++++++++++- src/primitives/literals.md | 7 ++++++ 8 files changed, 116 insertions(+), 28 deletions(-) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index ff35582..59655eb 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -6,22 +6,44 @@ on: - main workflow_dispatch: +permissions: + contents: read + pages: write + id-token: write + jobs: - deploy: - runs-on: ubuntu-18.04 + build: + #runs-on: ubuntu-18.04 # deprecated + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v2 - name: Setup mdBook - uses: peaceiris/actions-mdbook@v1 + uses: peaceiris/actions-mdbook@v2 with: - mdbook-version: '0.4.6' - # mdbook-version: 'latest' + # mdbook-version: '0.4.6' + mdbook-version: 'latest' - - run: mdbook build + - name: Build Rust by Example + run: mdbook build - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 + - name: Upload artifact + #uses: peaceiris/actions-gh-pages@v3 + #with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + # publish_dir: ./book + uses: actions/upload-pages-artifact@v3 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./book + path: ./book + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/src/hello/comment.md b/src/hello/comment.md index ba15244..7e6cc5a 100644 --- a/src/hello/comment.md +++ b/src/hello/comment.md @@ -1,6 +1,7 @@ # 코멘트 -모든 프로그램에는 코멘트가 필요합니다. 러스트는 이를 위해 몇가지 문법을 제공합니다. +모든 프로그램에는 코멘트가 필요합니다. +러스트는 이를 위해 몇가지 문법을 제공합니다. * *일반 코멘트*를 사용하면 컴파일러가 안쪽의 내용을 무시해줍니다. : * `// 해당 줄의 끝까지 코멘트가 됩니다.` diff --git a/src/hello/print.md b/src/hello/print.md index e78df38..2b2f543 100644 --- a/src/hello/print.md +++ b/src/hello/print.md @@ -1,6 +1,7 @@ # 형식을 지정하는 출력 -러스트에서 출력 관련 기능은 [`std::fmt`][fmt]에 정의된 몇개의 [`macro`][macros]로 처리합니다. +러스트에서 출력 관련 기능은 [`std::fmt`][fmt]에 정의된 몇 개의 +[`macro`][macros]로 처리합니다. * `format!`: 형식 지정 문자열을 [`String`][string] 에 출력합니다. * `print!`: `format!` 과 동일하지만, 출력을 콘솔 (io::stdout) 에 합니다. @@ -36,13 +37,22 @@ fn main() { // `:` 의 뒤에 특별한 형식을 지정할 수도 있습니다. println!("{:b}명의 사람들 중 {}명 만이 이진법을 알고, 나머지 반은 모른다", 2, 1); + println!("10진수: {}", 69420); // 69420 + println!(" 2진수 (binary): {:b}", 69420); // 10000111100101100 + println!(" 8진수 (octal): {:o}", 69420); // 207454 + println!("16진수 (hexadecimal): {:x}", 69420); // 10f2c // 폭을 지정해서 오른편 정렬을 할 수도 있습니다. 다음 코드의 출력은 // " 1" 이 됩니다. 즉, 공백문자 5개가 나온 후에 "1"이 출력됩니다. println!("{number:>width$}", number=1, width=6); + //println!("{number:>5}", number=1); // 공백대신 숫자 0을 넣을 수도 있습니다. 다음 코드는 "000001"을 출력합니다. - println!("{number:>0width$}", number=1, width=6); + println!("{number:0>width$}", number=1, width=6); + //println!("{number:0>5}", number=1); // 00001 + // 반대쪽에 넣을 수도 있습니다. 다음 코드는 "100000"을 출력합니다. + println!("{number:0폭$}", 숫자=1, 폭=6); @@ -52,14 +62,28 @@ fn main() { println!("내 이름은 {0}요. {1} {0}.", "본드"); // FIXME ^ 위 코드에서 빠진 인자 "제임스"를 추가해주세요. + // fmt::Display를 정의한 타입만 `{}`를 이용해 출력할 수 있습니다. + // 사용자 정의 타입은 기본적으로 정의하고 있지 않고요. + // 한개의 `i32` 를 가지고 있는 `Structure`라는 구조체를 생성해봅니다. #[allow(dead_code)] struct Structure(i32); - // 하지만, 이런 사용자 정의 자료형을 출력하려면 추가 작업이 필요합니다. - // 다음 코드는 동작하지 않을겁니다. + // `Structure`가 fmt::Display를 가지고 있지 않기 때문에, 이 코드는 컴파일조차 + // 되지 않습니다. println!("이 구조체 {}는 출력되지 않을겁니다.", Structure(3)); // FIXME ^ 위 코드를 코멘트로 막아주세요. + + // 러스트 1.58부터, 근처의 변수로부터 매개변수를 가져올 수 있습니다. + // 여기에서는 위에서처럼 " 1", 1 앞에 4개의 공백을 출력합니다. + let number: f64 = 1.0; + let width: usize = 5; + println!("{number:>width$}"); + + // 역주) 역시, 한글도 잘 됩니다. 그리고 사실 변수명도 한글로 잘 됩니다. + let 숫자: f64 = 1.0; + let 폭: usize = 6; + println!("{숫자:->폭$}"); // -----1 } ``` @@ -75,17 +99,19 @@ fn main() { 만약 `fmt::Display` 트레잇을 구현해주면 자동으로 [`ToString`] 트레잇이 구현되고, 해당 자료형을 [`String`][string]으로 [`변환(convert)`][convert] 할 수 있게됩니다. +중간의 `#[allow(dead_code)]`는 바로 다음에 오는 모듈에만 적용되는 [attribute]입니다. + ### Activities * 위 코드에서 두 개의 이슈(FIXME 라고 된 부분들)를 수정하고 오류없이 실행되게 해보세요. * `println!` 매크로의 소수점 표시 기능을 이용해서 `원주율의 근사치는 3.142이다.` 를 출력해보세요. - 위한 파이값은 `let pi = 3.141592` 라고 정의해주세요. (힌트: [`std::fmt`][fmt] + 이를 위한 파이값은 `let pi = 3.141592` 라고 정의해주세요. (힌트: [`std::fmt`][fmt] 문서에서 소수점 표시(Precision) 항목을 참고하세요.) ### 참고: [`std::fmt`][fmt], [`매크로(macros)`][macros], [`구조체(struct)`][structs], -[`트레잇(traits)`][traits] +[`트레잇(traits)`][traits], [`미사용 코드(dead_code)`][dead_code] [fmt]: https://doc.rust-lang.org/std/fmt/ [macros]: ../macros.md @@ -94,3 +120,5 @@ fn main() { [traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html [convert]: ../conversion/string.md +[attribute]: ../attribute.md +[dead_code]: ../attribute/unused.md diff --git a/src/hello/print/fmt.md b/src/hello/print/fmt.md index f3ee607..c88198e 100644 --- a/src/hello/print/fmt.md +++ b/src/hello/print/fmt.md @@ -76,13 +76,17 @@ RGB (0, 0, 0) 0x000000 ``` 다음을 참고하시면 구현할 수 있습니다. : + * RGB 색공간에서 색상을 계산하는 공식은 `RGB = (R*65536)+(G*256)+B`입니다. + 여기서 R은 빨강, G는 초록, B는 파란색 성분입니다. 더 자세히는 + [RGB 색 포맷과 계산][rgb_color]에서 확인하세요. * [각각의 색상을 한번 이상 표시하기][named_parameters], - * `:02` 로 [0을 붙여서 2글자로 출력하기][fmt_width]. + * `:0>2` 로 [0을 붙여서 2글자로 출력하기][fmt_width]. ### 참고: [`std::fmt`][fmt] +[rgb_color]: https://www.rapidtables.com/web/cololr/RGB_Color.html#rgb-format [named_parameters]: https://doc.rust-lang.org/std/fmt/#named-parameters [deadbeef]: https://en.wikipedia.org/wiki/Deadbeef#Magic_debug_values [fmt]: https://doc.rust-lang.org/std/fmt/ diff --git a/src/index.md b/src/index.md index 88fb02f..0e807cf 100644 --- a/src/index.md +++ b/src/index.md @@ -1,6 +1,6 @@ # 예제로 배우는 러스트 (Rust by Example) 한국어판 -러스트는 안전성과 속도 그리고, 병렬 처리에 초점을 맞춘 최신 시스템 프로그래밍 언어 +[러스트][rust]는 안전성과 속도 그리고, 병렬 처리에 초점을 맞춘 최신 시스템 프로그래밍 언어 입니다. 러스트는 이를 위해 가비지 컬렉션 기술을 사용하지 않고 메모리 안전성을 지원합니다. 이 문서는 실행 가능한 예제들로 러스트의 여러가지 개념과 표준 라이브러리를 소개합니다. @@ -19,13 +19,13 @@ - [변수 바인딩](variable_bindings.md) - mutable bindings, scope, shadowing. -- [자료형](types.md) - Learn about changing and defining types. +- [자료형](types.md) - 타입을 변경하고 정의하는 방법에 대해 배워 봅니다. -- [형변환](conversion.md) +- [형변환](conversion.md) - String, integer, float와 같은 서로 다른 타입으로 변환해 봅니다. -- [표현식](expression.md) +- [표현식](expression.md) - 표현식과 이를 이용하는 방법에 대해 배워 봅니다. -- [제어문](flow_control.md) - `if`/`else`, `for`, and others. +- [제어문](flow_control.md) - `if`/`else`, `for`, 그리고 여러 다른 것들. - [함수](fn.md) - Learn about Methods, Closures and High Order Functions. @@ -43,7 +43,7 @@ - [Traits](trait.md) - A trait is a collection of methods defined for an unknown type: `Self` -- [Macros](macros.md) +- [Macros](macros.md) - Macros are a way of writing code that writes other code, which is known as metaprogramming. - [Error handling](error.md) - Learn Rust way of handling failures. @@ -53,9 +53,9 @@ - [Testing](testing.md) - All sorts of testing in Rust. -- [Unsafe Operations](unsafe.md) +- [Unsafe Operations](unsafe.md) - "안전하지 않은" 동작 블록에 진입하는 방법을 알아봅니다. -- [Compatibility](compatibility.md) +- [Compatibility](compatibility.md) - 러스트의 발전과 가능한 호환성 문제를 다룹니다. - [Meta](meta.md) - Documentation, Benchmarking. diff --git a/src/primitives.md b/src/primitives.md index a1d27f6..438bf8d 100644 --- a/src/primitives.md +++ b/src/primitives.md @@ -49,6 +49,15 @@ fn main() { // 변수는 덮어 쓰여질 수 있습니다. 이것을 셰도우잉(shadowing)이라고 합니다. let mutable = true; + + /* 복합 자료형 - 배열과 튜플 */ + + // 배열의 시그니처는 타입 T와 길이 length를 이용해 [T; length]로 정의됩니다. + let my_array: [i32; 5] = [1, 2, 3, 4, 5]; + + // 튜플은 서로 다른 타입을 갖는 값의 모음이며, + // () 안에 들어 있습니다. + let my_tuple = (5u32, 1u8, true, -5.04f32); // 타입: (u32, u8, bool, f32) } ``` diff --git a/src/primitives/array.md b/src/primitives/array.md index 07484b4..f7a866c 100644 --- a/src/primitives/array.md +++ b/src/primitives/array.md @@ -45,7 +45,24 @@ fn main() { println!("배열의 일부를 슬라이스로 빌립니다"); analyze_slice(&ys[1 .. 4]); - // 아래 문장에서는 index out of bounds 오류가 발생합니다. + // 빈 슬라이스 `&[]`의 예제입니다. + let empty_array: [u32; 0] = []; // 역주) 타입이 없으면 컴파일되지 않습니다 + assert_eq!(&empty_array, &[]); // 역주) 이 매크로는 같으면 계속하고, 다르면 패닉을 리턴합니다. + assert_eq!(&empty_array, &[][..]); + + // 배열은 `.get`으로 안전하게 접근할 수 있으며, `Option`을 리턴합니다. + // 아래와 같이 match로 접근해 처리할 수도 있고, + for i in 0..xs.len() + 1 { // 변수 하나만큼 멀리 가버렸어요! + match xs.get(i) { + Some(값) => println!("{}: {}", i, 값), + None => println!("천천히요! {}는 너무 멀어요!", i), + } + } + // 프로그램을 멈추게 하고 싶으면 `.expect()`를 활용할 수도 있습니다. + + // 아래 문장에서는 index out of bounds 컴파일-시간 오류가 발생합니다. println!("{}", xs[5]); + // 범위 바깥을 인덱싱하려고 하면 index out of bounds 실행-시간 오류가 발생합니다. + println!("{}", xs[..][5]); } ``` diff --git a/src/primitives/literals.md b/src/primitives/literals.md index fe519b7..b35cdf0 100644 --- a/src/primitives/literals.md +++ b/src/primitives/literals.md @@ -9,6 +9,9 @@ 숫자 자료형은 가독성을 높이기 위해 밑줄을 넣을 수 있습니다. 예를 들면, `1_000` 은 `1000`과 같고, `0.000_001` 는 `0.000001`과 같습니다. +[E-노테이션][enote] 방식으로 값을 지정할 수도 있습니다. `ie6`, `7.6e-4`처럼요. +이들은 `f64` 타입이 됩니다. + 변수를 정의할 때는 컴파일러에게 자료형을 알려주어야 합니다. 여기서는 `u32` 로 부호없는 32비트 정수형임을 알리고, `i32`로 부호있는 32비트 정수임을 표시했습니다. @@ -24,6 +27,9 @@ fn main() { println!("1 - 2 = {}", 1i32 - 2); // TODO ^ 1i32 를 1u32 로 바꾸고 어째서 자료형이 중요한지 확인해보세요. + // 과학적 노테이션 + println!("1e4는 {}, -2.5e-3은 {}입니다", 1e4, -2.5e-3); + // 불리언 로직 println!("true AND false is {}", true && false); println!("true OR false is {}", true || false); @@ -41,5 +47,6 @@ fn main() { } ``` +[enote]: https://en.wikipedia.org/wiki/Scientific_notation#E_notation [rust op-prec]: https://doc.rust-lang.org/reference/expressions.html#expression-precedence [op-prec]: https://en.wikipedia.org/wiki/Operator_precedence#Programming_languages From eba9f361804f48fdc3438f96a142e1626c2fbd03 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sun, 6 Apr 2025 13:28:25 +0900 Subject: [PATCH 03/11] custom types: structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구조체에 대한 예시 수정 - attribute에 대한 번역 추가 - 원본 식별자(`r#`)에 대한 번역 추가 참고사항 - 코드 블럭 안쪽은 아직 번역해야 할 게 많습니다. 이 부분은 텍스트 부분의 번역이 끝나면 다시 돌면서 진행할 예정입니다. --- src/attribute.md | 54 ++++++++++++++----- src/attribute/unused.md | 10 ++-- src/compatibility/raw_identifiers.md | 26 +++++---- src/custom_types/structs.md | 38 +++++++------ src/flow_control/match/destructuring.md | 12 ++--- .../match/destructuring/destructure_enum.md | 8 +-- .../destructuring/destructure_pointers.md | 13 +++-- src/scope/borrow/ref.md | 8 +-- 8 files changed, 97 insertions(+), 72 deletions(-) diff --git a/src/attribute.md b/src/attribute.md index 13b0a60..0f77cab 100644 --- a/src/attribute.md +++ b/src/attribute.md @@ -1,29 +1,53 @@ # Attributes -An attribute is metadata applied to some module, crate or item. This metadata -can be used to/for: +Attribute는 모듈, 크레이트 또는 아이템에 붙이는 메타데이터로, +다음과 같은 상황에 사용할 수 있습니다: -* [conditional compilation of code][cfg] -* [set crate name, version and type (binary or library)][crate] -* disable [lints][lint] (warnings) -* enable compiler features (macros, glob imports, etc.) -* link to a foreign library -* mark functions as unit tests -* mark functions that will be part of a benchmark +* [코드의 조건부 컴파일][cfg] +* [크레이트의 이름, 버전과 종류 지정 (바이너리나 라이브러리)][crate] +* [린팅][lint] (경고) 비활성화 +* 컴파일러 기능 활성화 (매크로, 글로벌 임포트 등) +* 외부 라이브러리에 링크 +* 함수를 유닛의 테스트로 표시 +* 함수를 벤치마크의 일부로 표시 +* [Attribute화 매크로][macros] -When attributes apply to a whole crate, their syntax is `#![crate_attribute]`, -and when they apply to a module or item, the syntax is `#[item_attribute]` -(notice the missing bang `!`). +Attribute는 `#[outer_attribute]`나 `#![inner_attribute]`로 표기하며, +각각에 따라 적용하는 지점이 다릅니다. -Attributes can take arguments with different syntaxes: +* `#[outer_attribute]`는 이어지는 [항목][item]에 바로 적용됩니다. + 예를 들어 함수, 모듈 선언, 상수, 구조체, enum 등이 있습니다. + 구조체 `Rectangle`에 attribute `#[derive(debug)]`가 적용되는 예를 들어보겠습니다: + + ```rust + #[derive(Debug)] + struct Rectangle { + width: u32, + height: u32, + } + ``` + +* `#![inner_attribute]`는 이를 포함하는 [아이템][item] (보통 모듈 또는 크레이트) 전체에 적용됩니다. + 즉, 이는 배치된 모든 스코프에 적용하는 것으로 해석됩니다. + `main.rs`에 배치함으로서 `#![allow(unused_variables)]`가 크레이트 전체에 적용되는 예를 들어보겠습니다: + + ```rust + #![allow(unused_variables)] + + fn main() { + let x = 3; // This would normally warn about an unused variable. + } + ``` + +Attribute는 여러 방식으로 인수를 받을 수 있습니다: * `#[attribute = "value"]` * `#[attribute(key = "value")]` * `#[attribute(value)]` -Attributes can have multiple values and can be separated over multiple lines, too: +인수를 여러 개 받을 수도, 여러 줄에 걸쳐 받을 수도 있습니다: ```rust,ignore #[attribute(value, value2)] @@ -35,4 +59,6 @@ Attributes can have multiple values and can be separated over multiple lines, to [cfg]: attribute/cfg.md [crate]: attribute/crate.md +[item]: https://doc.rust-lang.org/stable/reference/items.html [lint]: https://en.wikipedia.org/wiki/Lint_%28software%29 +[macros]: https://doc.rust-lang.org/book/ch19-06-macros.html#attribute-like-macros diff --git a/src/attribute/unused.md b/src/attribute/unused.md index 1eab374..d278141 100644 --- a/src/attribute/unused.md +++ b/src/attribute/unused.md @@ -1,8 +1,7 @@ # `dead_code` -The compiler provides a `dead_code` -[*lint*][lint] that will warn -about unused functions. An *attribute* can be used to disable the lint. +러스트 컴파일러는 `dead_code` [*lint*][lint]를 통해 사용되지 않은 +함수에 대해 경고를 표시합니다. *Attribute*를 이용해 이를 비활성화할 수 있습니다. ```rust,editable fn used_function() {} @@ -19,8 +18,7 @@ fn main() { } ``` -Note that in real programs, you should eliminate dead code. In these examples -we'll allow dead code in some places because of the interactive nature of the -examples. +실제 프로그램에서는 이러한 "죽은 코드"를 없애야 합니다. 이 예시들에서는 +예시들의 대화형 특성에 의해 여러 "죽은 코드"를 허용하고 있습니다. [lint]: https://en.wikipedia.org/wiki/Lint_%28software%29 diff --git a/src/compatibility/raw_identifiers.md b/src/compatibility/raw_identifiers.md index eb42cf6..863198f 100644 --- a/src/compatibility/raw_identifiers.md +++ b/src/compatibility/raw_identifiers.md @@ -1,17 +1,15 @@ -# Raw identifiers +# 원본 식별자 -Rust, like many programming languages, has the concept of "keywords". -These identifiers mean something to the language, and so you cannot use them in -places like variable names, function names, and other places. -Raw identifiers let you use keywords where they would not normally be allowed. -This is particularly useful when Rust introduces new keywords, and a library -using an older edition of Rust has a variable or function with the same name -as a keyword introduced in a newer edition. +러스트에는 다른 프로그래밍 언어와 같이 "키워드"라는 컨셉이 존재합니다. +이 식별자는 언어 수준에서 특정한 의미를 가지고, 이에 따라 변수명이나 함수명 +등등에 보통은 사용할 수 없습니다. +원본 식별자(`r#`)를 이용하면 보통이라면 이용할 수 없는 키워드를 식별자로 이용할 수 있게 해줍니다. +이 방식은 러스트에서 새로운 키워드를 발표함에 따라 이전 에디션의 러스트에서 사용한 변수/함수 식별자가 +최신 에디션에서 키워드로 지정되었고 이를 활용해야 할 때 유용합니다. -For example, consider a crate `foo` compiled with the 2015 edition of Rust that -exports a function named `try`. This keyword is reserved for a new feature in -the 2018 edition, so without raw identifiers, we would have no way to name the -function. +예를 들어, 크레이트 `foo`가 러스트 2015 에디션에서 컴파일되었고 `try`라는 이름의 +함수를 노출한다고 해봅시다. 이 키워드는 2018 에디션에서 키워드로 지정되었기 때문에, +원본 식별자 문법 없이는 그 함수를 더이상 활용할 수 없게 됩니다. ```rust,ignore extern crate foo; @@ -21,7 +19,7 @@ fn main() { } ``` -You'll get this error: +이를 컴파일하면 이런 오류가 발생합니다: ```text error: expected identifier, found keyword `try` @@ -31,7 +29,7 @@ error: expected identifier, found keyword `try` | ^^^ expected identifier, found keyword ``` -You can write this with a raw identifier: +이를 해결하기 위해 원본 식별자를 이용해 작성하면 이렇게 됩니다: ```rust,ignore extern crate foo; diff --git a/src/custom_types/structs.md b/src/custom_types/structs.md index d68cecf..1d84fda 100644 --- a/src/custom_types/structs.md +++ b/src/custom_types/structs.md @@ -7,26 +7,28 @@ * 유닛 구조체. 필드가 없는 제너릭에 유용한 구조체입니다. ```rust,editable +// 미사용 코드에 대한 오류를 숨기기 위해 +#![allow(dead_code)] + #[derive(Debug)] struct Person { name: String, age: u8, } -// A unit struct +// 유닛 구조체 struct Unit; -// A tuple struct +// 튜플 구조체 struct Pair(i32, f32); -// A struct with two fields +// 2개의 필드를 가진 구조체 struct Point { x: f32, y: f32, } -// Structs can be reused as fields of another struct -#[allow(dead_code)] +// 구조체는 다른 구조체의 필드로 이용될 수 있습니다 struct Rectangle { // A rectangle can be specified by where the top left and bottom right // corners are in space. @@ -45,21 +47,22 @@ fn main() { // Instantiate a `Point` - let point: Point = Point { x: 10.3, y: 0.4 }; + let point: Point = Point { x: 5.2, y: 0.4 }; + let another_point: Point = Point { x: 10.3, y: 0.2 }; // Access the fields of the point println!("point coordinates: ({}, {})", point.x, point.y); // Make a new point by using struct update syntax to use the fields of our // other one - let bottom_right = Point { x: 5.2, ..point }; + let bottom_right = Point { x: 10.3, ..another_point }; // `bottom_right.y` will be the same as `point.y` because we used that field // from `point` println!("second point: ({}, {})", bottom_right.x, bottom_right.y); // Destructure the point using a `let` binding - let Point { x: top_edge, y: left_edge } = point; + let Point { x: left_edge, y: top_edge } = point; let _rectangle = Rectangle { // struct instantiation is an expression too @@ -83,18 +86,19 @@ fn main() { } ``` -### Activity +### 실습 -1. Add a function `rect_area` which calculates the area of a rectangle (try - using nested destructuring). -2. Add a function `square` which takes a `Point` and a `f32` as arguments, and - returns a `Rectangle` with its lower left corner on the point, and a width and - height corresponding to the `f32`. +1. 직사각형의 넓이를 계산하는 `rect_area` 함수를 추가해보세요. + (아이템 중첩 해체를 활용해보세요) +2. `Point`와 `f32`를 인수로 받아 왼쪽 바닥의 점을 `Point`에서, + 폭과 높이를 `f32`에서 계산해 `Rectangle`을 리턴하는 + `square` 함수를 추가해보세요. -### See also +### 참고 -[`attributes`][attributes], and [destructuring][destructuring] +[`attributes`][attributes], [raw identifiers][raw_identifiers]와 [destructuring][destructuring] [attributes]: ../attribute.md -[c_struct]: https://ko.wikipedia.org/wiki/Struct +[c_struct]: https://ko.wikipedia.org/wiki/Struct_(C_programming_language) [destructuring]: ../flow_control/match/destructuring.md +[raw_identifiers]: ../compatibility/raw_identifiers.md diff --git a/src/flow_control/match/destructuring.md b/src/flow_control/match/destructuring.md index 5e82e6c..bbe8a53 100644 --- a/src/flow_control/match/destructuring.md +++ b/src/flow_control/match/destructuring.md @@ -1,11 +1,11 @@ -# Destructuring +# 아이템 해체 -A `match` block can destructure items in a variety of ways. +`match` 블록을 이용해 아이템을 여러 방법으로 해체할 수 있습니다. -* [Destructuring Tuples][tuple] -* [Destructuring Enums][enum] -* [Destructuring Pointers][refs] -* [Destructuring Structures][struct] +* [튜플 해체][tuple] +* [열거형 해체][enum] +* [포인터 해체][refs] +* [구조체 해체][struct] [enum]: destructuring/destructure_enum.md diff --git a/src/flow_control/match/destructuring/destructure_enum.md b/src/flow_control/match/destructuring/destructure_enum.md index 60d8969..1b7d57c 100644 --- a/src/flow_control/match/destructuring/destructure_enum.md +++ b/src/flow_control/match/destructuring/destructure_enum.md @@ -1,6 +1,6 @@ -# enums +# 열거형 -An `enum` is destructured similarly: +`열거형`은 이런 식으로 해체할 수 있습니다: ```rust,editable // `allow` required to silence warnings because only @@ -45,9 +45,9 @@ fn main() { } ``` -### See also: +### 참고 -[`#[allow(...)]`][allow], [color models][color_models] and [`enum`][enum] +[`#[allow(...)]`][allow], [색상 모델][color_models] and [`enum`][enum] [allow]: ../../../attribute/unused.md [color_models]: https://en.wikipedia.org/wiki/Color_model diff --git a/src/flow_control/match/destructuring/destructure_pointers.md b/src/flow_control/match/destructuring/destructure_pointers.md index 43ca3bd..d554598 100644 --- a/src/flow_control/match/destructuring/destructure_pointers.md +++ b/src/flow_control/match/destructuring/destructure_pointers.md @@ -1,11 +1,10 @@ -# pointers/ref +# 포인터/레퍼런스 -For pointers, a distinction needs to be made between destructuring -and dereferencing as they are different concepts which are used -differently from a language like `C`. +`C`언어 같은 언어들에서와 그 컨셉이 다르기 때문에, +포인터에서는 해체 과정에 비구조화와 비참조화 과정이 필요합니다. - * Dereferencing uses `*` - * Destructuring uses `&`, `ref`, and `ref mut` + * 비참조화는 `*`를 사용합니다 + * 비구조화는 `&`, `ref`와/또는 `ref mut`를 사용합니다 ```rust,editable fn main() { @@ -60,6 +59,6 @@ fn main() { } ``` -### See also: +### 참고 [The ref pattern](../../../scope/borrow/ref.md) diff --git a/src/scope/borrow/ref.md b/src/scope/borrow/ref.md index 3c035f6..ac58a8e 100644 --- a/src/scope/borrow/ref.md +++ b/src/scope/borrow/ref.md @@ -1,8 +1,8 @@ -# The ref pattern +# ref 패턴 -When doing pattern matching or destructuring via the `let` binding, the `ref` -keyword can be used to take references to the fields of a struct/tuple. The -example below shows a few instances where this can be useful: +`let` 바인딩을 통해 패턴 매칭이나 구조 해체를 할 때, +`ref` 키워드를 이용해 구조체나 튜플의 필드에 대한 참조를 가져올 수 있습니다. +아래의 예시는 이가 유용할 수 있는 몇몇 상황을 들었습니다: ```rust,editable #[derive(Clone, Copy)] From 95074ac4496a68f8b3ae79d8d56d295ccd502fe5 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sun, 6 Apr 2025 13:32:35 +0900 Subject: [PATCH 04/11] =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=ED=95=B4=EC=B2=B4?= =?UTF-8?q?=20=ED=8C=8C=ED=8A=B8=20=EB=B2=88=EC=97=AD=20(=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=ED=83=80=EC=9E=85=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일부 커스텀 타입 문서의 원문 최신화도 진행했습니다. 이들은 다음 커밋에 번역이 진행될 예정입니다. --- src/custom_types/constants.md | 2 +- src/custom_types/enum.md | 6 +-- src/custom_types/enum/enum_use.md | 40 ++++++++-------- src/custom_types/enum/testcase_linked_list.md | 5 +- .../match/destructing/destructure_slice.md | 48 +++++++++++++++++++ src/flow_control/match/destructuring.md | 2 + 6 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 src/flow_control/match/destructing/destructure_slice.md diff --git a/src/custom_types/constants.md b/src/custom_types/constants.md index 8878ba8..c060db7 100644 --- a/src/custom_types/constants.md +++ b/src/custom_types/constants.md @@ -4,7 +4,7 @@ Rust has two different types of constants which can be declared in any scope including global. Both require explicit type annotation: * `const`: An unchangeable value (the common case). -* `static`: A possibly `mut`able variable with [`'static`][static] lifetime. +* `static`: A possibly mutable variable with [`'static`][static] lifetime. The static lifetime is inferred and does not have to be specified. Accessing or modifying a mutable static variable is [`unsafe`][unsafe]. diff --git a/src/custom_types/enum.md b/src/custom_types/enum.md index e861df9..b7fb7e4 100644 --- a/src/custom_types/enum.md +++ b/src/custom_types/enum.md @@ -1,7 +1,7 @@ # Enums The `enum` keyword allows the creation of a type which may be one of a few -different variants. Any variant which is valid as a `struct` is also valid as +different variants. Any variant which is valid as a `struct` is also valid in an `enum`. ```rust,editable @@ -10,7 +10,7 @@ an `enum`. // `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`. // Each is different and independent. enum WebEvent { - // An `enum` may either be `unit-like`, + // An `enum` variant may either be `unit-like`, PageLoad, PageUnload, // like tuple structs, @@ -26,7 +26,7 @@ fn inspect(event: WebEvent) { match event { WebEvent::PageLoad => println!("page loaded"), WebEvent::PageUnload => println!("page unloaded"), - // Destructure `c` from inside the `enum`. + // Destructure `c` from inside the `enum` variant. WebEvent::KeyPress(c) => println!("pressed '{}'.", c), WebEvent::Paste(s) => println!("pasted \"{}\".", s), // Destructure `Click` into `x` and `y`. diff --git a/src/custom_types/enum/enum_use.md b/src/custom_types/enum/enum_use.md index cf75c67..80a86e1 100644 --- a/src/custom_types/enum/enum_use.md +++ b/src/custom_types/enum/enum_use.md @@ -6,45 +6,45 @@ The `use` declaration can be used so manual scoping isn't needed: // An attribute to hide warnings for unused code. #![allow(dead_code)] -enum Status { - Rich, - Poor, +enum Stage { + Beginner, + Advanced, } -enum Work { - Civilian, - Soldier, +enum Role { + Student, + Teacher, } fn main() { // Explicitly `use` each name so they are available without // manual scoping. - use crate::Status::{Poor, Rich}; - // Automatically `use` each name inside `Work`. - use crate::Work::*; + use crate::Stage::{Beginner, Advanced}; + // Automatically `use` each name inside `Role`. + use crate::Role::*; - // Equivalent to `Status::Poor`. - let status = Poor; - // Equivalent to `Work::Civilian`. - let work = Civilian; + // Equivalent to `Stage::Beginner`. + let stage = Beginner; + // Equivalent to `Role::Student`. + let role = Student; - match status { + match stage { // Note the lack of scoping because of the explicit `use` above. - Rich => println!("The rich have lots of money!"), - Poor => println!("The poor have no money..."), + Beginner => println!("Beginners are starting their learning journey!"), + Advanced => println!("Advanced learners are mastering their subjects..."), } - match work { + match role { // Note again the lack of scoping. - Civilian => println!("Civilians work!"), - Soldier => println!("Soldiers fight!"), + Student => println!("Students are acquiring knowledge!"), + Teacher => println!("Teachers are spreading knowledge!"), } } ``` ### See also: -[`match`][match] and [`use`][use] +[`match`][match] and [`use`][use] [use]: ../../mod/use.md [match]: ../../flow_control/match.md diff --git a/src/custom_types/enum/testcase_linked_list.md b/src/custom_types/enum/testcase_linked_list.md index 84855d1..cd4e3e6 100644 --- a/src/custom_types/enum/testcase_linked_list.md +++ b/src/custom_types/enum/testcase_linked_list.md @@ -1,6 +1,6 @@ # Testcase: linked-list -A common use for `enums` is to create a linked-list: +A common way to implement a linked-list is via `enums`: ```rust,editable use crate::List::*; @@ -32,6 +32,9 @@ impl List { // depends on the variant of `self` // `self` has type `&List`, and `*self` has type `List`, matching on a // concrete type `T` is preferred over a match on a reference `&T` + // after Rust 2018 you can use self here and tail (with no ref) below as well, + // rust will infer &s and ref tail. + // See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html match *self { // Can't take ownership of the tail, because `self` is borrowed; // instead take a reference to the tail diff --git a/src/flow_control/match/destructing/destructure_slice.md b/src/flow_control/match/destructing/destructure_slice.md new file mode 100644 index 0000000..93b7e42 --- /dev/null +++ b/src/flow_control/match/destructing/destructure_slice.md @@ -0,0 +1,48 @@ +# arrays/slices + +Like tuples, arrays and slices can be destructured this way: + +```rust,editable +fn main() { + // Try changing the values in the array, or make it a slice! + let array = [1, -2, 6]; + + match array { + // Binds the second and the third elements to the respective variables + [0, second, third] => + println!("array[0] = 0, array[1] = {}, array[2] = {}", second, third), + + // Single values can be ignored with _ + [1, _, third] => println!( + "array[0] = 1, array[2] = {} and array[1] was ignored", + third + ), + + // You can also bind some and ignore the rest + [-1, second, ..] => println!( + "array[0] = -1, array[1] = {} and all the other ones were ignored", + second + ), + // The code below would not compile + // [-1, second] => ... + + // Or store them in another array/slice (the type depends on + // that of the value that is being matched against) + [3, second, tail @ ..] => println!( + "array[0] = 3, array[1] = {} and the other elements were {:?}", + second, tail + ), + + // Combining these patterns, we can, for example, bind the first and + // last values, and store the rest of them in a single array + [first, middle @ .., last] => println!( + "array[0] = {}, middle = {:?}, array[2] = {}", + first, middle, last + ), + } +} +``` + +### See also: + +[Arrays and Slices](../../../primitives/array.md) and [Binding](../binding.md) for `@` sigil diff --git a/src/flow_control/match/destructuring.md b/src/flow_control/match/destructuring.md index bbe8a53..5ddc7e6 100644 --- a/src/flow_control/match/destructuring.md +++ b/src/flow_control/match/destructuring.md @@ -3,6 +3,7 @@ `match` 블록을 이용해 아이템을 여러 방법으로 해체할 수 있습니다. * [튜플 해체][tuple] +* [배열과 슬라이스 해체][slice] * [열거형 해체][enum] * [포인터 해체][refs] * [구조체 해체][struct] @@ -12,3 +13,4 @@ [refs]: destructuring/destructure_pointers.md [struct]: destructuring/destructure_structures.md [tuple]: destructuring/destructure_tuple.md +[slice]: destructing/destructure_slice.md From 30faa533d2371c11a0c7afeb361fee0f82c38e67 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sun, 6 Apr 2025 15:42:11 +0900 Subject: [PATCH 05/11] =?UTF-8?q?SUMMARY=20=EC=99=84=EC=97=AD=20=EB=93=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 목차 부분인 SUMMARY.md 파일을 완역했습니다. 용어는 The Book을 일부 참조했습니다. * `std` 내부 트레잇의 경우 원문을 따랐습니다. - 대부분의 용어가 완역됨에 따라, 종전에 번역하지 않고 내버려둔 용어도 일부 번역을 진행했습니다. - 일부 dead link를 해결했습니다. - 번역 예정인 부분 중 새로 추가된 문서를 반영했습니다. 기존판에 대하여 추가된 부분은 다음과 같습니다: * `error/abort_unwind` * `error/option_unwrap/defaults` * `flow_control/let_else` * `flow_control/match/destructuring/destructure_slice` (파일 위치 재변경) * `unsafe/asm` - diverging에 안정화 관련 "한글" 문단을 추가했습니다. - FFI에 unsafe 관련 "영문" 문단을 추가했습니다. * 해당 문단은 FFI를 번역할 시점이 되면 알아서 번역하겠습니다. 제가 직접 썼는데 번역 못할 것도 없죠 뭐.. --- src/SUMMARY.md | 270 +++++----- src/attribute.md | 10 +- src/attribute/unused.md | 2 +- src/custom_types/structs.md | 2 +- src/error/abort_unwind.md | 58 +++ src/error/option_unwrap/defaults.md | 124 +++++ src/flow_control/let_else.md | 62 +++ src/flow_control/match/destructuring.md | 2 +- .../destructure_slice.md | 0 src/fn/diverging.md | 7 + src/hello/print.md | 2 +- src/hello/print/fmt.md | 2 +- .../print/print_display/testcase_list.md | 2 +- src/std_misc/ffi.md | 7 + src/unsafe/asm.md | 489 ++++++++++++++++++ 15 files changed, 895 insertions(+), 144 deletions(-) create mode 100644 src/error/abort_unwind.md create mode 100644 src/error/option_unwrap/defaults.md create mode 100644 src/flow_control/let_else.md rename src/flow_control/match/{destructing => destructuring}/destructure_slice.md (100%) create mode 100644 src/unsafe/asm.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 08a1d61..ac621e5 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,201 +19,205 @@ - [구조체](custom_types/structs.md) - [열거형](custom_types/enum.md) - [use](custom_types/enum/enum_use.md) - - [C-like](custom_types/enum/c_like.md) - - [Testcase: linked-list](custom_types/enum/testcase_linked_list.md) + - [C언어처럼 활용하기](custom_types/enum/c_like.md) + - [테스트 케이스: linked list](custom_types/enum/testcase_linked_list.md) - [상수](custom_types/constants.md) - [변수 바인딩](variable_bindings.md) - - [Mutability](variable_bindings/mut.md) - - [Scope and Shadowing](variable_bindings/scope.md) - - [Declare first](variable_bindings/declare.md) - - [Freezing](variable_bindings/freeze.md) + - [수정 가능성](variable_bindings/mut.md) + - [스코프와 섀도잉](variable_bindings/scope.md) + - [우선 선언하기](variable_bindings/declare.md) + - [프리징](variable_bindings/freeze.md) - [자료형](types.md) - - [Casting](types/cast.md) - - [Literals](types/literals.md) - - [Inference](types/inference.md) - - [Aliasing](types/alias.md) + - [캐스팅](types/cast.md) + - [리터럴](types/literals.md) + - [추적](types/inference.md) + - [별명 붙이기](types/alias.md) - [형변환](conversion.md) - - [`From` and `Into`](conversion/from_into.md) - - [`TryFrom` and `TryInto`](conversion/try_from_try_into.md) - - [To and from `String`s](conversion/string.md) + - [`From`과 `Into`](conversion/from_into.md) + - [`TryFrom`과 `TryInto`](conversion/try_from_try_into.md) + - [`string`으로, `string`에서](conversion/string.md) - [표현식](expression.md) - [제어문](flow_control.md) - [if/else](flow_control/if_else.md) - [loop](flow_control/loop.md) - - [Nesting and labels](flow_control/loop/nested.md) - - [Returning from loops](flow_control/loop/return.md) + - [중첩과 라벨링](flow_control/loop/nested.md) + - [반복문 안에서 리턴하기](flow_control/loop/return.md) - [while](flow_control/while.md) - - [for and range](flow_control/for.md) + - [for 반복문과 range](flow_control/for.md) - [match](flow_control/match.md) - - [Destructuring](flow_control/match/destructuring.md) - - [tuples](flow_control/match/destructuring/destructure_tuple.md) - - [enums](flow_control/match/destructuring/destructure_enum.md) - - [pointers/ref](flow_control/match/destructuring/destructure_pointers.md) - - [structs](flow_control/match/destructuring/destructure_structures.md) - - [Guards](flow_control/match/guard.md) - - [Binding](flow_control/match/binding.md) + - [아이템 해체](flow_control/match/destructuring.md) + - [튜플](flow_control/match/destructuring/destructure_tuple.md) + - [배열과 슬라이스](flow_control/match/destructuring/destructure_slice.md) + - [열거형](flow_control/match/destructuring/destructure_enum.md) + - [포인터와 참조형](flow_control/match/destructuring/destructure_pointers.md) + - [구조체](flow_control/match/destructuring/destructure_structures.md) + - [가드](flow_control/match/guard.md) + - [바인딩](flow_control/match/binding.md) - [if let](flow_control/if_let.md) + - [let-else](flow_control/let_else.md) - [while let](flow_control/while_let.md) - [함수](fn.md) - - [Methods](fn/methods.md) - - [Closures](fn/closures.md) - - [Capturing](fn/closures/capture.md) - - [As input parameters](fn/closures/input_parameters.md) - - [Type anonymity](fn/closures/anonymity.md) - - [Input functions](fn/closures/input_functions.md) - - [As output parameters](fn/closures/output_parameters.md) - - [Examples in `std`](fn/closures/closure_examples.md) + - [메서드](fn/methods.md) + - [클로저](fn/closures.md) + - [캡처링](fn/closures/capture.md) + - [입력 파라미터로서](fn/closures/input_parameters.md) + - [타입 익명성](fn/closures/anonymity.md) + - [파라미터에 함수 넣기](fn/closures/input_functions.md) + - [출력 파라미터로서](fn/closures/output_parameters.md) + - [`std`에서의 예시](fn/closures/closure_examples.md) - [Iterator::any](fn/closures/closure_examples/iter_any.md) - - [Searching through iterators](fn/closures/closure_examples/iter_find.md) - - [Higher Order Functions](fn/hof.md) - - [Diverging functions](fn/diverging.md) + - [Iterator에서 검색하기](fn/closures/closure_examples/iter_find.md) + - [상위 순서의 함수](fn/hof.md) + - [리턴하지 않는 함수](fn/diverging.md) - [모듈](mod.md) - - [Visibility](mod/visibility.md) - - [Struct visibility](mod/struct_visibility.md) - - [The `use` declaration](mod/use.md) - - [`super` and `self`](mod/super.md) - - [File hierarchy](mod/split.md) + - [접근성](mod/visibility.md) + - [구조체 접근성](mod/struct_visibility.md) + - [`use` 선언문](mod/use.md) + - [`super`와 `self`](mod/super.md) + - [파일 계층화](mod/split.md) - [크레이트(Crate)](crates.md) - - [Creating a Library](crates/lib.md) - - [Using a Library](crates/using_lib.md) + - [라이브러리 만들기](crates/lib.md) + - [라이브러리 활용하기](crates/using_lib.md) - [카고(Cargo)](cargo.md) - - [Dependencies](cargo/deps.md) - - [Conventions](cargo/conventions.md) - - [Tests](cargo/test.md) - - [Build Scripts](cargo/build_scripts.md) + - [의존성 관리](cargo/deps.md) + - [관례](cargo/conventions.md) + - [테스트 관리](cargo/test.md) + - [빌드 스크립트](cargo/build_scripts.md) -- [Attributes](attribute.md) +- [속성](attribute.md) - [`dead_code`](attribute/unused.md) - - [Crates](attribute/crate.md) + - [크레이트](attribute/crate.md) - [`cfg`](attribute/cfg.md) - - [Custom](attribute/cfg/custom.md) - -- [Generics](generics.md) - - [Functions](generics/gen_fn.md) - - [Implementation](generics/impl.md) - - [Traits](generics/gen_trait.md) - - [Bounds](generics/bounds.md) - - [Testcase: empty bounds](generics/bounds/testcase_empty.md) - - [Multiple bounds](generics/multi_bounds.md) - - [Where clauses](generics/where.md) - - [New Type Idiom](generics/new_types.md) - - [Associated items](generics/assoc_items.md) - - [The Problem](generics/assoc_items/the_problem.md) - - [Associated types](generics/assoc_items/types.md) - - [Phantom type parameters](generics/phantom.md) - - [Testcase: unit clarification](generics/phantom/testcase_units.md) - -- [Scoping rules](scope.md) + - [커스텀](attribute/cfg/custom.md) + +- [제네릭](generics.md) + - [함수](generics/gen_fn.md) + - [구현](generics/impl.md) + - [트레잇](generics/gen_trait.md) + - [바운딩](generics/bounds.md) + - [테스트 케이스: 빈 바운딩](generics/bounds/testcase_empty.md) + - [다중 바운딩](generics/multi_bounds.md) + - [where 제한자](generics/where.md) + - [newtype 관용구](generics/new_types.md) + - [연관된 아이템](generics/assoc_items.md) + - [문제점](generics/assoc_items/the_problem.md) + - [연관된 타입](generics/assoc_items/types.md) + - [팬텀 타입 파라미터](generics/phantom.md) + - [테스트 케이스: unit clarification](generics/phantom/testcase_units.md) + +- [스코프 규칙](scope.md) - [RAII](scope/raii.md) - - [Ownership and moves](scope/move.md) - - [Mutability](scope/move/mut.md) - - [Partial moves](scope/move/partial_move.md) - - [Borrowing](scope/borrow.md) - - [Mutability](scope/borrow/mut.md) - - [Aliasing](scope/borrow/alias.md) - - [The ref pattern](scope/borrow/ref.md) - - [Lifetimes](scope/lifetime.md) - - [Explicit annotation](scope/lifetime/explicit.md) - - [Functions](scope/lifetime/fn.md) - - [Methods](scope/lifetime/methods.md) - - [Structs](scope/lifetime/struct.md) - - [Traits](scope/lifetime/trait.md) - - [Bounds](scope/lifetime/lifetime_bounds.md) - - [Coercion](scope/lifetime/lifetime_coercion.md) + - [소유권과 그 이동](scope/move.md) + - [수정 가능성](scope/move/mut.md) + - [잠깐 옮기기](scope/move/partial_move.md) + - [빌리기](scope/borrow.md) + - [수정 가능성](scope/borrow/mut.md) + - [별명 달기](scope/borrow/alias.md) + - [ref 패턴](scope/borrow/ref.md) + - [수명](scope/lifetime.md) + - [명시하기](scope/lifetime/explicit.md) + - [함수](scope/lifetime/fn.md) + - [메서드](scope/lifetime/methods.md) + - [구조체](scope/lifetime/struct.md) + - [트레잇](scope/lifetime/trait.md) + - [바운딩](scope/lifetime/lifetime_bounds.md) + - [억제](scope/lifetime/lifetime_coercion.md) - [Static](scope/lifetime/static_lifetime.md) - - [Elision](scope/lifetime/elision.md) + - [생략하기](scope/lifetime/elision.md) -- [Traits](trait.md) +- [트레잇](trait.md) - [Derive](trait/derive.md) - - [Returning Traits with `dyn`](trait/dyn.md) - - [Operator Overloading](trait/ops.md) + - [`dyn` 키워드로 동적 리턴하기](trait/dyn.md) + - [연산자 오버로딩](trait/ops.md) - [Drop](trait/drop.md) - [Iterators](trait/iter.md) - [`impl Trait`](trait/impl_trait.md) - [Clone](trait/clone.md) - [Supertraits](trait/supertraits.md) - - [Disambiguating overlapping traits](trait/disambiguating.md) + - [중복 트레잇을 명확하게 호출하기](trait/disambiguating.md) - [macro_rules!](macros.md) - - [Syntax](macros/syntax.md) - - [Designators](macros/designators.md) - - [Overload](macros/overload.md) - - [Repeat](macros/repeat.md) - - [DRY (Don't Repeat Yourself)](macros/dry.md) - - [DSL (Domain Specific Languages)](macros/dsl.md) - - [Variadics](macros/variadics.md) - -- [Error handling](error.md) + - [문법](macros/syntax.md) + - [설계자](macros/designators.md) + - [오버로딩](macros/overload.md) + - [반복하기](macros/repeat.md) + - [DRY (반복하지 마세요)](macros/dry.md) + - [DSL (도메인-특정 언어)](macros/dsl.md) + - [길이 불특정 인터페이스](macros/variadics.md) + +- [오류 다루기](error.md) - [`panic`](error/panic.md) + - [`abort` & `unwind`](error/abort_unwind.md) - [`Option` & `unwrap`](error/option_unwrap.md) - - [Unpacking options with `?`](error/option_unwrap/question_mark.md) - - [Combinators: `map`](error/option_unwrap/map.md) - - [Combinators: `and_then`](error/option_unwrap/and_then.md) + - [option을 `?`로 해체하기](error/option_unwrap/question_mark.md) + - [조합자: `map`](error/option_unwrap/map.md) + - [조합자: `and_then`](error/option_unwrap/and_then.md) + - [기본 작업: `or`, `or_else`, `get_or_insert`, `get_or_insert_with`](error/option_unwrap/defaults.md) - [`Result`](error/result.md) - [`map` for `Result`](error/result/result_map.md) - - [aliases for `Result`](error/result/result_alias.md) - - [Early returns](error/result/early_returns.md) - - [Introducing `?`](error/result/enter_question_mark.md) - - [Multiple error types](error/multiple_error_types.md) - - [Pulling `Result`s out of `Option`s](error/multiple_error_types/option_result.md) - - [Defining an error type](error/multiple_error_types/define_error_type.md) - - [`Box`ing errors](error/multiple_error_types/boxing_errors.md) - - [Other uses of `?`](error/multiple_error_types/reenter_question_mark.md) - - [Wrapping errors](error/multiple_error_types/wrap_error.md) - - [Iterating over `Result`s](error/iter_result.md) - -- [Std library types](std.md) - - [Box, stack and heap](std/box.md) - - [Vectors](std/vec.md) - - [Strings](std/str.md) + - [`Result`의 별칭](error/result/result_alias.md) + - [빠르게 리턴하기](error/result/early_returns.md) + - [`?`를 소개합니다](error/result/enter_question_mark.md) + - [여러 오류 타입](error/multiple_error_types.md) + - [`Option`에서 `Result` 추출하기](error/multiple_error_types/option_result.md) + - [오류 타입 지정하기](error/multiple_error_types/define_error_type.md) + - [오류를 `box`에 넣기](error/multiple_error_types/boxing_errors.md) + - [`?`의 다른 용도](error/multiple_error_types/reenter_question_mark.md) + - [오류 감싸기](error/multiple_error_types/wrap_error.md) + - [`Results`로 반복하기](error/iter_result.md) + +- [std 라이브러리 타입](std.md) + - [Box, stack과 heap](std/box.md) + - [벡터](std/vec.md) + - [문자열](std/str.md) - [`Option`](std/option.md) - [`Result`](std/result.md) - [`?`](std/result/question_mark.md) - [`panic!`](std/panic.md) - [HashMap](std/hash.md) - - [Alternate/custom key types](std/hash/alt_key_types.md) + - [대체/커스텀 키 타입](std/hash/alt_key_types.md) - [HashSet](std/hash/hashset.md) - [`Rc`](std/rc.md) - [`Arc`](std/arc.md) -- [Std misc](std_misc.md) +- [std 라이브러리 잡동사니](std_misc.md) - [Threads](std_misc/threads.md) - - [Testcase: map-reduce](std_misc/threads/testcase_mapreduce.md) + - [테스트 케이스: map-reduce](std_misc/threads/testcase_mapreduce.md) - [Channels](std_misc/channels.md) - [Path](std_misc/path.md) - - [File I/O](std_misc/file.md) + - [파일 입출력](std_misc/file.md) - [`open`](std_misc/file/open.md) - [`create`](std_misc/file/create.md) - [`read lines`](std_misc/file/read_lines.md) - - [Child processes](std_misc/process.md) + - [하위 프로세스](std_misc/process.md) - [Pipes](std_misc/process/pipe.md) - [Wait](std_misc/process/wait.md) - - [Filesystem Operations](std_misc/fs.md) - - [Program arguments](std_misc/arg.md) - - [Argument parsing](std_misc/arg/matching.md) - - [Foreign Function Interface](std_misc/ffi.md) - -- [Testing](testing.md) - - [Unit testing](testing/unit_testing.md) - - [Documentation testing](testing/doc_testing.md) - - [Integration testing](testing/integration_testing.md) + - [파일 시스템 작업](std_misc/fs.md) + - [프로그램 변수](std_misc/arg.md) + - [변수 파싱하기](std_misc/arg/matching.md) + - [외부 함수 인터페이스](std_misc/ffi.md) + +- [테스트하기](testing.md) + - [유닛 단위 테스트](testing/unit_testing.md) + - [문서 테스트](testing/doc_testing.md) + - [복합 작업 테스트](testing/integration_testing.md) - [Dev-dependencies](testing/dev_dependencies.md) -- [Unsafe Operations](unsafe.md) +- [Unsafe한 작업](unsafe.md) + - [인라인 어셈블리](unsafe/asm.md) -- [Compatibility](compatibility.md) - - [Raw identifiers](compatibility/raw_identifiers.md) - -- [Meta](meta.md) - - [Documentation](meta/doc.md) - - [Playpen](meta/playpen.md) +- [호환성](compatibility.md) + - [원본 식별자](compatibility/raw_identifiers.md) +- [메타데이터](meta.md) + - [문서화하기](meta/doc.md) + - [플레이펜](meta/playpen.md) diff --git a/src/attribute.md b/src/attribute.md index 0f77cab..b289ea8 100644 --- a/src/attribute.md +++ b/src/attribute.md @@ -1,6 +1,6 @@ -# Attributes +# 속성 -Attribute는 모듈, 크레이트 또는 아이템에 붙이는 메타데이터로, +속성은 모듈, 크레이트 또는 아이템에 붙이는 메타데이터로, 다음과 같은 상황에 사용할 수 있습니다: @@ -12,9 +12,9 @@ Attribute는 모듈, 크레이트 또는 아이템에 붙이는 메타데이터 * 외부 라이브러리에 링크 * 함수를 유닛의 테스트로 표시 * 함수를 벤치마크의 일부로 표시 -* [Attribute화 매크로][macros] +* [속성화 매크로][macros] -Attribute는 `#[outer_attribute]`나 `#![inner_attribute]`로 표기하며, +속성은 `#[outer_attribute]`나 `#![inner_attribute]`와 같이 표기하며, 각각에 따라 적용하는 지점이 다릅니다. * `#[outer_attribute]`는 이어지는 [항목][item]에 바로 적용됩니다. @@ -41,7 +41,7 @@ Attribute는 `#[outer_attribute]`나 `#![inner_attribute]`로 표기하며, } ``` -Attribute는 여러 방식으로 인수를 받을 수 있습니다: +속성은 여러 방식으로 인수를 받을 수 있습니다: * `#[attribute = "value"]` * `#[attribute(key = "value")]` diff --git a/src/attribute/unused.md b/src/attribute/unused.md index d278141..9812bbd 100644 --- a/src/attribute/unused.md +++ b/src/attribute/unused.md @@ -1,7 +1,7 @@ # `dead_code` 러스트 컴파일러는 `dead_code` [*lint*][lint]를 통해 사용되지 않은 -함수에 대해 경고를 표시합니다. *Attribute*를 이용해 이를 비활성화할 수 있습니다. +함수에 대해 경고를 표시합니다. *속성*을 이용해 이를 비활성화할 수 있습니다. ```rust,editable fn used_function() {} diff --git a/src/custom_types/structs.md b/src/custom_types/structs.md index 1d84fda..cd351ef 100644 --- a/src/custom_types/structs.md +++ b/src/custom_types/structs.md @@ -96,7 +96,7 @@ fn main() { ### 참고 -[`attributes`][attributes], [raw identifiers][raw_identifiers]와 [destructuring][destructuring] +[`attributes`][attributes], [원본 식별자][raw_identifiers]와 [해체][destructuring] [attributes]: ../attribute.md [c_struct]: https://ko.wikipedia.org/wiki/Struct_(C_programming_language) diff --git a/src/error/abort_unwind.md b/src/error/abort_unwind.md new file mode 100644 index 0000000..e178566 --- /dev/null +++ b/src/error/abort_unwind.md @@ -0,0 +1,58 @@ +# `abort` and `unwind` + +The previous section illustrates the error handling mechanism `panic`. Different code paths can be conditionally compiled based on the panic setting. The current values available are `unwind` and `abort`. + +Building on the prior lemonade example, we explicitly use the panic strategy to exercise different lines of code. + +```rust,editable,mdbook-runnable +fn drink(beverage: &str) { + // You shouldn't drink too much sugary beverages. + if beverage == "lemonade" { + if cfg!(panic = "abort") { + println!("This is not your party. Run!!!!"); + } else { + println!("Spit it out!!!!"); + } + } else { + println!("Some refreshing {} is all I need.", beverage); + } +} + +fn main() { + drink("water"); + drink("lemonade"); +} +``` + +Here is another example focusing on rewriting `drink()` and explicitly use the `unwind` keyword. + +```rust,editable +#[cfg(panic = "unwind")] +fn ah() { + println!("Spit it out!!!!"); +} + +#[cfg(not(panic = "unwind"))] +fn ah() { + println!("This is not your party. Run!!!!"); +} + +fn drink(beverage: &str) { + if beverage == "lemonade" { + ah(); + } else { + println!("Some refreshing {} is all I need.", beverage); + } +} + +fn main() { + drink("water"); + drink("lemonade"); +} +``` + +The panic strategy can be set from the command line by using `abort` or `unwind`. + +```console +rustc lemonade.rs -C panic=abort +``` diff --git a/src/error/option_unwrap/defaults.md b/src/error/option_unwrap/defaults.md new file mode 100644 index 0000000..e749c14 --- /dev/null +++ b/src/error/option_unwrap/defaults.md @@ -0,0 +1,124 @@ +# Unpacking options and defaults + +There is more than one way to unpack an `Option` and fall back on a default if it is `None`. To choose the one that meets our needs, we need to consider the following: + +* do we need eager or lazy evaluation? +* do we need to keep the original empty value intact, or modify it in place? + +## `or()` is chainable, evaluates eagerly, keeps empty value intact + +`or()`is chainable and eagerly evaluates its argument, as is shown in the following example. Note that because `or`'s arguments are evaluated eagerly, the variable passed to `or` is moved. + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let apple = Some(Fruit::Apple); + let orange = Some(Fruit::Orange); + let no_fruit: Option = None; + + let first_available_fruit = no_fruit.or(orange).or(apple); + println!("first_available_fruit: {:?}", first_available_fruit); + // first_available_fruit: Some(Orange) + + // `or` moves its argument. + // In the example above, `or(orange)` returned a `Some`, so `or(apple)` was not invoked. + // But the variable named `apple` has been moved regardless, and cannot be used anymore. + // println!("Variable apple was moved, so this line won't compile: {:?}", apple); + // TODO: uncomment the line above to see the compiler error + } +``` + +## `or_else()` is chainable, evaluates lazily, keeps empty value intact + +Another alternative is to use `or_else`, which is also chainable, and evaluates lazily, as is shown in the following example: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let no_fruit: Option = None; + let get_kiwi_as_fallback = || { + println!("Providing kiwi as fallback"); + Some(Fruit::Kiwi) + }; + let get_lemon_as_fallback = || { + println!("Providing lemon as fallback"); + Some(Fruit::Lemon) + }; + + let first_available_fruit = no_fruit + .or_else(get_kiwi_as_fallback) + .or_else(get_lemon_as_fallback); + println!("first_available_fruit: {:?}", first_available_fruit); + // Providing kiwi as fallback + // first_available_fruit: Some(Kiwi) +} +``` + +## `get_or_insert()` evaluates eagerly, modifies empty value in place + +To make sure that an `Option` contains a value, we can use `get_or_insert` to modify it in place with a fallback value, as is shown in the following example. Note that `get_or_insert` eagerly evaluates its parameter, so variable `apple` is moved: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let mut my_fruit: Option = None; + let apple = Fruit::Apple; + let first_available_fruit = my_fruit.get_or_insert(apple); + println!("first_available_fruit is: {:?}", first_available_fruit); + println!("my_fruit is: {:?}", my_fruit); + // first_available_fruit is: Apple + // my_fruit is: Some(Apple) + //println!("Variable named `apple` is moved: {:?}", apple); + // TODO: uncomment the line above to see the compiler error +} +``` + +## `get_or_insert_with()` evaluates lazily, modifies empty value in place + +Instead of explicitly providing a value to fall back on, we can pass a closure to `get_or_insert_with`, as follows: + +```rust,editable +#[derive(Debug)] +enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } + +fn main() { + let mut my_fruit: Option = None; + let get_lemon_as_fallback = || { + println!("Providing lemon as fallback"); + Fruit::Lemon + }; + let first_available_fruit = my_fruit + .get_or_insert_with(get_lemon_as_fallback); + println!("first_available_fruit is: {:?}", first_available_fruit); + println!("my_fruit is: {:?}", my_fruit); + // Providing lemon as fallback + // first_available_fruit is: Lemon + // my_fruit is: Some(Lemon) + + // If the Option has a value, it is left unchanged, and the closure is not invoked + let mut my_apple = Some(Fruit::Apple); + let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback); + println!("should_be_apple is: {:?}", should_be_apple); + println!("my_apple is unchanged: {:?}", my_apple); + // The output is a follows. Note that the closure `get_lemon_as_fallback` is not invoked + // should_be_apple is: Apple + // my_apple is unchanged: Some(Apple) +} +``` + +### See also: + +[`closures`][closures], [`get_or_insert`][get_or_insert], [`get_or_insert_with`][get_or_insert_with], [`moved variables`][moved], [`or`][or], [`or_else`][or_else] + +[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html +[get_or_insert]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert +[get_or_insert_with]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert_with +[moved]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html +[or]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or +[or_else]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or_else diff --git a/src/flow_control/let_else.md b/src/flow_control/let_else.md new file mode 100644 index 0000000..2d07de7 --- /dev/null +++ b/src/flow_control/let_else.md @@ -0,0 +1,62 @@ +# let-else + +> 🛈 stable since: rust 1.65 +> +> 🛈 you can target specific edition by compiling like this +> `rustc --edition=2021 main.rs` + +With `let`-`else`, a refutable pattern can match and bind variables +in the surrounding scope like a normal `let`, or else diverge (e.g. `break`, +`return`, `panic!`) when the pattern doesn't match. + +```rust +use std::str::FromStr; + +fn get_count_item(s: &str) -> (u64, &str) { + let mut it = s.split(' '); + let (Some(count_str), Some(item)) = (it.next(), it.next()) else { + panic!("Can't segment count item pair: '{s}'"); + }; + let Ok(count) = u64::from_str(count_str) else { + panic!("Can't parse integer: '{count_str}'"); + }; + (count, item) +} + +fn main() { + assert_eq!(get_count_item("3 chairs"), (3, "chairs")); +} +``` + +The scope of name bindings is the main thing that makes this different from +`match` or `if let`-`else` expressions. You could previously approximate these +patterns with an unfortunate bit of repetition and an outer `let`: + +```rust +# use std::str::FromStr; +# +# fn get_count_item(s: &str) -> (u64, &str) { +# let mut it = s.split(' '); + let (count_str, item) = match (it.next(), it.next()) { + (Some(count_str), Some(item)) => (count_str, item), + _ => panic!("Can't segment count item pair: '{s}'"), + }; + let count = if let Ok(count) = u64::from_str(count_str) { + count + } else { + panic!("Can't parse integer: '{count_str}'"); + }; +# (count, item) +# } +# +# assert_eq!(get_count_item("3 chairs"), (3, "chairs")); +``` + +### See also: + +[option][option], [match][match], [if let][if_let] and the [let-else RFC][let_else_rfc]. + +[match]: ./match.md +[if_let]: ./if_let.md +[let_else_rfc]: https://rust-lang.github.io/rfcs/3137-let-else.html +[option]: ../std/option.md diff --git a/src/flow_control/match/destructuring.md b/src/flow_control/match/destructuring.md index 5ddc7e6..a3e350a 100644 --- a/src/flow_control/match/destructuring.md +++ b/src/flow_control/match/destructuring.md @@ -13,4 +13,4 @@ [refs]: destructuring/destructure_pointers.md [struct]: destructuring/destructure_structures.md [tuple]: destructuring/destructure_tuple.md -[slice]: destructing/destructure_slice.md +[slice]: destructuring/destructure_slice.md diff --git a/src/flow_control/match/destructing/destructure_slice.md b/src/flow_control/match/destructuring/destructure_slice.md similarity index 100% rename from src/flow_control/match/destructing/destructure_slice.md rename to src/flow_control/match/destructuring/destructure_slice.md diff --git a/src/fn/diverging.md b/src/fn/diverging.md index 19cf27c..9b7109c 100644 --- a/src/fn/diverging.md +++ b/src/fn/diverging.md @@ -1,5 +1,11 @@ # Diverging functions +> 🛈 안정화된 버전: 러스트 1.85 (2024 에디션) +> +> 🛈 다음과 같이 컴파일해 타겟 에디션을 지정할 수 있습니다: +> `rustc --edition=2024 main.rs` +> 혹은 `Cargo.toml`에 `edition = 2024`를 지정하세요. + Diverging functions never return. They are marked using `!`, which is an empty type. ```rust @@ -30,6 +36,7 @@ As opposed to this function, which will never return the control back to the cal ```rust,ignore #![feature(never_type)] +// TODO : ^ Remove this on Rust 2024 fn main() { let x: ! = panic!("This call never returns."); diff --git a/src/hello/print.md b/src/hello/print.md index 2b2f543..ca11c59 100644 --- a/src/hello/print.md +++ b/src/hello/print.md @@ -99,7 +99,7 @@ fn main() { 만약 `fmt::Display` 트레잇을 구현해주면 자동으로 [`ToString`] 트레잇이 구현되고, 해당 자료형을 [`String`][string]으로 [`변환(convert)`][convert] 할 수 있게됩니다. -중간의 `#[allow(dead_code)]`는 바로 다음에 오는 모듈에만 적용되는 [attribute]입니다. +중간의 `#[allow(dead_code)]`는 바로 다음에 오는 모듈에만 적용되는 [속성][attribute]입니다. ### Activities diff --git a/src/hello/print/fmt.md b/src/hello/print/fmt.md index c88198e..24ba54e 100644 --- a/src/hello/print/fmt.md +++ b/src/hello/print/fmt.md @@ -88,7 +88,7 @@ RGB (0, 0, 0) 0x000000 [rgb_color]: https://www.rapidtables.com/web/cololr/RGB_Color.html#rgb-format [named_parameters]: https://doc.rust-lang.org/std/fmt/#named-parameters -[deadbeef]: https://en.wikipedia.org/wiki/Deadbeef#Magic_debug_values +[deadbeef]: https://en.wikipedia.org/wiki/Magic_number_(programming)#DEADBEEF [fmt]: https://doc.rust-lang.org/std/fmt/ [fmt_traits]: https://doc.rust-lang.org/std/fmt/#formatting-traits [fmt_width]: https://doc.rust-lang.org/std/fmt/#width diff --git a/src/hello/print/print_display/testcase_list.md b/src/hello/print/print_display/testcase_list.md index 8e02578..2bdda07 100644 --- a/src/hello/print/print_display/testcase_list.md +++ b/src/hello/print/print_display/testcase_list.md @@ -59,7 +59,7 @@ fn main() { ### 참고 [`for`][for], [`ref`][ref], [`Result`][result], [`struct`][struct], -[`?`][q_mark], and [`vec!`][vec] +[`?`][q_mark]와 [`vec!`][vec] [for]: ../../../flow_control/for.md [result]: ../../../std/result.md diff --git a/src/std_misc/ffi.md b/src/std_misc/ffi.md index 71977e6..cee6b1a 100644 --- a/src/std_misc/ffi.md +++ b/src/std_misc/ffi.md @@ -4,6 +4,11 @@ Rust provides a Foreign Function Interface (FFI) to C libraries. Foreign functions must be declared inside an `extern` block annotated with a `#[link]` attribute containing the name of the foreign library. +> 🛈 Foreign functions are treated as [`unsafe`][unsafe]. You should call those in `unsafe` block, +> but you can still wrap with safe (standard rust) functions. +> +> `unsafe fn`s are able to use these without another `unsafe` wrapping. + ```rust,ignore use std::fmt; @@ -54,3 +59,5 @@ impl fmt::Debug for Complex { } } ``` + +[unsafe]: ../unsafe.md \ No newline at end of file diff --git a/src/unsafe/asm.md b/src/unsafe/asm.md new file mode 100644 index 0000000..c2992dc --- /dev/null +++ b/src/unsafe/asm.md @@ -0,0 +1,489 @@ +# 인라인 어셈블리 + +Rust provides support for inline assembly via the `asm!` macro. +It can be used to embed handwritten assembly in the assembly output generated by the compiler. +Generally this should not be necessary, but might be where the required performance or timing +cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality. + +> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported. + +Inline assembly is currently supported on the following architectures: + +- x86 and x86-64 +- ARM +- AArch64 +- RISC-V + +## Basic usage + +Let us start with the simplest possible example: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +unsafe { + asm!("nop"); +} +# } +``` + +This will insert a NOP (no operation) instruction into the assembly generated by the compiler. +Note that all `asm!` invocations have to be inside an `unsafe` block, as they could insert +arbitrary instructions and break various invariants. The instructions to be inserted are listed +in the first argument of the `asm!` macro as a string literal. + +## Inputs and outputs + +Now inserting an instruction that does nothing is rather boring. Let us do something that +actually acts on data: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let x: u64; +unsafe { + asm!("mov {}, 5", out(reg) x); +} +assert_eq!(x, 5); +# } +``` + +This will write the value `5` into the `u64` variable `x`. +You can see that the string literal we use to specify instructions is actually a template string. +It is governed by the same rules as Rust [format strings][format-syntax]. +The arguments that are inserted into the template however look a bit different than you may +be familiar with. First we need to specify if the variable is an input or an output of the +inline assembly. In this case it is an output. We declared this by writing `out`. +We also need to specify in what kind of register the assembly expects the variable. +In this case we put it in an arbitrary general purpose register by specifying `reg`. +The compiler will choose an appropriate register to insert into +the template and will read the variable from there after the inline assembly finishes executing. + +[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax + +Let us see another example that also uses an input: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let i: u64 = 3; +let o: u64; +unsafe { + asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, + ); +} +assert_eq!(o, 8); +# } +``` + +This will add `5` to the input in variable `i` and write the result to variable `o`. +The particular way this assembly does this is first copying the value from `i` to the output, +and then adding `5` to it. + +The example shows a few things: + +First, we can see that `asm!` allows multiple template string arguments; each +one is treated as a separate line of assembly code, as if they were all joined +together with newlines between them. This makes it easy to format assembly +code. + +Second, we can see that inputs are declared by writing `in` instead of `out`. + +Third, we can see that we can specify an argument number, or name as in any format string. +For inline assembly templates this is particularly useful as arguments are often used more than once. +For more complex inline assembly using this facility is generally recommended, as it improves +readability, and allows reordering instructions without changing the argument order. + +We can further refine the above example to avoid the `mov` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut x: u64 = 3; +unsafe { + asm!("add {0}, 5", inout(reg) x); +} +assert_eq!(x, 8); +# } +``` + +We can see that `inout` is used to specify an argument that is both input and output. +This is different from specifying an input and output separately in that it is guaranteed to assign both to the same register. + +It is also possible to specify different variables for the input and output parts of an `inout` operand: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let x: u64 = 3; +let y: u64; +unsafe { + asm!("add {0}, 5", inout(reg) x => y); +} +assert_eq!(y, 8); +# } +``` + +## Late output operands + +The Rust compiler is conservative with its allocation of operands. It is assumed that an `out` +can be written at any time, and can therefore not share its location with any other argument. +However, to guarantee optimal performance it is important to use as few registers as possible, +so they won't have to be saved and reloaded around the inline assembly block. +To achieve this Rust provides a `lateout` specifier. This can be used on any output that is +written only after all inputs have been consumed. There is also an `inlateout` variant of this +specifier. + +Here is an example where `inlateout` *cannot* be used in `release` mode or other optimized cases: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +let c: u64 = 4; +unsafe { + asm!( + "add {0}, {1}", + "add {0}, {2}", + inout(reg) a, + in(reg) b, + in(reg) c, + ); +} +assert_eq!(a, 12); +# } +``` + +In unoptimized cases (e.g. `Debug` mode), replacing `inout(reg) a` with `inlateout(reg) a` in the +above example can continue to give the expected result. However, with `release` mode or other +optimized cases, using `inlateout(reg) a` can instead lead to the final value `a = 16`, causing the +assertion to fail. + +This is because in optimized cases, the compiler is free to allocate the same register for inputs +`b` and `c` since it knows that they have the same value. Furthermore, when `inlateout` is used, `a` +and `c` could be allocated to the same register, in which case the first `add` instruction would +overwrite the initial load from variable `c`. This is in contrast to how using `inout(reg) a` +ensures a separate register is allocated for `a`. + +However, the following example can use `inlateout` since the output is only modified after all input +registers have been read: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +unsafe { + asm!("add {0}, {1}", inlateout(reg) a, in(reg) b); +} +assert_eq!(a, 8); +# } +``` + +As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register. + +## Explicit register operands + +Some instructions require that the operands be in a specific register. +Therefore, Rust inline assembly provides some more specific constraint specifiers. +While `reg` is generally available on any architecture, explicit registers are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi` among others can be addressed by their name. + +```rust,no_run +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let cmd = 0xd1; +unsafe { + asm!("out 0x64, eax", in("eax") cmd); +} +# } +``` + +In this example we call the `out` instruction to output the content of the `cmd` variable to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand we had to use the `eax` constraint specifier. + +> **Note**: unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types. + +Consider this example which uses the x86 `mul` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +fn mul(a: u64, b: u64) -> u128 { + let lo: u64; + let hi: u64; + + unsafe { + asm!( + // The x86 mul instruction takes rax as an implicit input and writes + // the 128-bit result of the multiplication to rax:rdx. + "mul {}", + in(reg) a, + inlateout("rax") b => lo, + lateout("rdx") hi + ); + } + + ((hi as u128) << 64) + lo as u128 +} +# } +``` + +This uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result. +The only explicit operand is a register, that we fill from the variable `a`. +The second operand is implicit, and must be the `rax` register, which we fill from the variable `b`. +The lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`. +The higher 64 bits are stored in `rdx` from which we fill the variable `hi`. + +## Clobbered registers + +In many cases inline assembly will modify state that is not needed as an output. +Usually this is either because we have to use a scratch register in the assembly or because instructions modify state that we don't need to further examine. +This state is generally referred to as being "clobbered". +We need to tell the compiler about this since it may need to save and restore this state around the inline assembly block. + +```rust +use std::arch::asm; + +# #[cfg(target_arch = "x86_64")] +fn main() { + // three entries of four bytes each + let mut name_buf = [0_u8; 12]; + // String is stored as ascii in ebx, edx, ecx in order + // Because ebx is reserved, the asm needs to preserve the value of it. + // So we push and pop it around the main asm. + // 64 bit mode on 64 bit processors does not allow pushing/popping of + // 32 bit registers (like ebx), so we have to use the extended rbx register instead. + + unsafe { + asm!( + "push rbx", + "cpuid", + "mov [rdi], ebx", + "mov [rdi + 4], edx", + "mov [rdi + 8], ecx", + "pop rbx", + // We use a pointer to an array for storing the values to simplify + // the Rust code at the cost of a couple more asm instructions + // This is more explicit with how the asm works however, as opposed + // to explicit register outputs such as `out("ecx") val` + // The *pointer itself* is only an input even though it's written behind + in("rdi") name_buf.as_mut_ptr(), + // select cpuid 0, also specify eax as clobbered + inout("eax") 0 => _, + // cpuid clobbers these registers too + out("ecx") _, + out("edx") _, + ); + } + + let name = core::str::from_utf8(&name_buf).unwrap(); + println!("CPU Manufacturer ID: {}", name); +} + +# #[cfg(not(target_arch = "x86_64"))] +# fn main() {} +``` + +In the example above we use the `cpuid` instruction to read the CPU manufacturer ID. +This instruction writes to `eax` with the maximum supported `cpuid` argument and `ebx`, `edx`, and `ecx` with the CPU manufacturer ID as ASCII bytes in that order. + +Even though `eax` is never read we still need to tell the compiler that the register has been modified so that the compiler can save any values that were in these registers before the asm. This is done by declaring it as an output but with `_` instead of a variable name, which indicates that the output value is to be discarded. + +This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an input or output **except** if the compiler uses it to fulfill a general register class (e.g. `in(reg)`). This makes `reg` operands dangerous when using reserved registers as we could unknowingly corrupt our input or output because they share the same register. + +To work around this we use `rdi` to store the pointer to the output array, save `ebx` via `push`, read from `ebx` inside the asm block into the array and then restore `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`. + +This can also be used with a general register class to obtain a scratch register for use inside the asm code: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +// Multiply x by 6 using shifts and adds +let mut x: u64 = 4; +unsafe { + asm!( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); +} +assert_eq!(x, 4 * 6); +# } +``` + +## Symbol operands and ABI clobbers + +By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`] argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered. Multiple `clobber_abi` arguments may be provided and all clobbers from all specified ABIs will be inserted. + +[`clobber_abi`]: https://doc.rust-lang.org/stable/reference/inline-assembly.html#abi-clobbers + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +extern "C" fn foo(arg: i32) -> i32 { + println!("arg = {}", arg); + arg * 2 +} + +fn call_foo(arg: i32) -> i32 { + unsafe { + let result; + asm!( + "call {}", + // Function pointer to call + in(reg) foo, + // 1st argument in rdi + in("rdi") arg, + // Return value in rax + out("rax") result, + // Mark all registers which are not preserved by the "C" calling + // convention as clobbered. + clobber_abi("C"), + ); + result + } +} +# } +``` + +## Register template modifiers + +In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register). + +By default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc). + +This default can be overridden by using modifiers on the template string operands, just like you would with format strings: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut x: u16 = 0xab; + +unsafe { + asm!("mov {0:h}, {0:l}", inout(reg_abcd) x); +} + +assert_eq!(x, 0xabab); +# } +``` + +In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 registers (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently. + +Let us assume that the register allocator has chosen to allocate `x` in the `ax` register. +The `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte. + +If you use a smaller data type (e.g. `u16`) with an operand and forget to use template modifiers, the compiler will emit a warning and suggest the correct modifier to use. + +## Memory address operands + +Sometimes assembly instructions require operands passed via memory addresses/memory locations. +You have to manually use the memory address syntax specified by the target architecture. +For example, on x86/x86_64 using Intel assembly syntax, you should wrap inputs/outputs in `[]` to indicate they are memory operands: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +fn load_fpu_control_word(control: u16) { + unsafe { + asm!("fldcw [{}]", in(reg) &control, options(nostack)); + } +} +# } +``` + +## Labels + +Any reuse of a named label, local or otherwise, can result in an assembler or linker error or may cause other strange behavior. Reuse of a named label can happen in a variety of ways including: + +- explicitly: using a label more than once in one `asm!` block, or multiple times across blocks. +- implicitly via inlining: the compiler is allowed to instantiate multiple copies of an `asm!` block, for example when the function containing it is inlined in multiple places. +- implicitly via LTO: LTO can cause code from *other crates* to be placed in the same codegen unit, and so could bring in arbitrary labels. + +As a consequence, you should only use GNU assembler **numeric** [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. + +Moreover, on x86 when using the default Intel syntax, due to [an LLVM bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. Using `options(att_syntax)` will avoid any ambiguity, but that affects the syntax of the *entire* `asm!` block. (See [Options](#options), below, for more on `options`.) + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a = 0; +unsafe { + asm!( + "mov {0}, 10", + "2:", + "sub {0}, 1", + "cmp {0}, 3", + "jle 2f", + "jmp 2b", + "2:", + "add {0}, 2", + out(reg) a + ); +} +assert_eq!(a, 5); +# } +``` + +This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`. + +This example shows a few things: + +- First, that the same number can be used as a label multiple times in the same inline block. +- Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes “b” (“backward”) or ”f” (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction. + +[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels +[an LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + +## Options {#options} + +By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However, in many cases it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. + +Let's take our previous example of an `add` instruction: + +```rust +# #[cfg(target_arch = "x86_64")] { +use std::arch::asm; + +let mut a: u64 = 4; +let b: u64 = 4; +unsafe { + asm!( + "add {0}, {1}", + inlateout(reg) a, in(reg) b, + options(pure, nomem, nostack), + ); +} +assert_eq!(a, 8); +# } +``` + +Options can be provided as an optional final argument to the `asm!` macro. We specified three options here: + +- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely. +- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global). +- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments. + +These allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed. + +See the [reference](https://doc.rust-lang.org/stable/reference/inline-assembly.html) for the full list of available options and their effects. From b51fb544c0041fc82914fc883a0617e81ab1506e Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sat, 12 Apr 2025 11:55:59 +0900 Subject: [PATCH 06/11] =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95(=EC=97=B4=EA=B1=B0,=20=EC=83=81=EC=88=98),?= =?UTF-8?q?=20=EB=B3=80=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - enum, constants 번역 완료 - variable bindings 파트 번역 완료 - `book.toml`: description과 author를 변경했습니다. 솔직히 `A description`은 좀 애매하죠.. --- book.toml | 4 +- src/SUMMARY.md | 8 +-- src/custom_types/constants.md | 24 ++++---- src/custom_types/enum.md | 50 ++++++++-------- src/custom_types/enum/c_like.md | 10 ++-- src/custom_types/enum/enum_use.md | 17 +++--- src/custom_types/enum/testcase_linked_list.md | 58 ++++++++++--------- src/variable_bindings.md | 11 ++-- src/variable_bindings/declare.md | 17 +++--- src/variable_bindings/freeze.md | 7 ++- src/variable_bindings/mut.md | 7 +-- src/variable_bindings/scope.md | 22 ++++--- 12 files changed, 120 insertions(+), 115 deletions(-) diff --git a/book.toml b/book.toml index bafbb22..7c01208 100644 --- a/book.toml +++ b/book.toml @@ -1,7 +1,7 @@ [book] title = "예제로 배우는 러스트 (Rust by Example) 한국어판" -description = "A description" -author = "The Rust Community" +description = "예제로 배우는 러스트 (Rust by Example)의 한국어판입니다." +author = "러스트 한국 커뮤니티" [output.html.playpen] editable = true diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ac621e5..f66a344 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -1,4 +1,4 @@ -# Summary +# 목차 [들어가며](index.md) @@ -20,14 +20,14 @@ - [열거형](custom_types/enum.md) - [use](custom_types/enum/enum_use.md) - [C언어처럼 활용하기](custom_types/enum/c_like.md) - - [테스트 케이스: linked list](custom_types/enum/testcase_linked_list.md) + - [테스트 케이스: 연결 리스트](custom_types/enum/testcase_linked_list.md) - [상수](custom_types/constants.md) -- [변수 바인딩](variable_bindings.md) +- [변수 할당](variable_bindings.md) - [수정 가능성](variable_bindings/mut.md) - [스코프와 섀도잉](variable_bindings/scope.md) - [우선 선언하기](variable_bindings/declare.md) - - [프리징](variable_bindings/freeze.md) + - [고정하기](variable_bindings/freeze.md) - [자료형](types.md) - [캐스팅](types/cast.md) diff --git a/src/custom_types/constants.md b/src/custom_types/constants.md index c060db7..61a4037 100644 --- a/src/custom_types/constants.md +++ b/src/custom_types/constants.md @@ -1,38 +1,38 @@ -# constants +# 상수 -Rust has two different types of constants which can be declared in any scope -including global. Both require explicit type annotation: +러스트에는 글로벌을 포함해 어느 스코프에서나 선언될 수 있는 두 가지의 상수가 +있습니다. 특정한 타입 어노테이션이 요구됩니다: -* `const`: An unchangeable value (the common case). -* `static`: A possibly mutable variable with [`'static`][static] lifetime. - The static lifetime is inferred and does not have to be specified. - Accessing or modifying a mutable static variable is [`unsafe`][unsafe]. +* `const`: 변경되지 않는 값 (일반적인 경우) +* `static`: [`'static`][static] 라이프타임을 가지는 값이 바뀔 수 있는 변수입니다. + static 라이프타입은 추정되므로 선언할 필요가 없습니다. static mutable 변수에 + 접근하거나 수정하는 행위는 [`unsafe`][unsafe]로 정의됩니다. ```rust,editable,ignore,mdbook-runnable -// Globals are declared outside all other scopes. +// 글로벌 상수는 모든 스코프 밖에 선언됩니다 static LANGUAGE: &str = "Rust"; const THRESHOLD: i32 = 10; fn is_big(n: i32) -> bool { - // Access constant in some function + // 함수 안에서 상수 접근 n > THRESHOLD } fn main() { let n = 16; - // Access constant in the main thread + // 메인 스레드에서 상수 접근 println!("This is {}", LANGUAGE); println!("The threshold is {}", THRESHOLD); println!("{} is {}", n, if is_big(n) { "big" } else { "small" }); - // Error! Cannot modify a `const`. + // 오류! `const`는 수정할 수 없습니다. THRESHOLD = 5; // FIXME ^ Comment out this line } ``` -### See also: +### 함께 읽기: [The `const`/`static` RFC]( https://github.com/rust-lang/rfcs/blob/master/text/0246-const-vs-static.md), diff --git a/src/custom_types/enum.md b/src/custom_types/enum.md index b7fb7e4..c5581cb 100644 --- a/src/custom_types/enum.md +++ b/src/custom_types/enum.md @@ -1,27 +1,27 @@ -# Enums +# 열거형 -The `enum` keyword allows the creation of a type which may be one of a few -different variants. Any variant which is valid as a `struct` is also valid in -an `enum`. +`enum` 키워드는 여러 종류 중 하나일 수 있는 타입을 만들 수 있게 해줍니다. +`struct`에서 유효한 어느 타입도 `enum`에서 유효합니다. ```rust,editable -// Create an `enum` to classify a web event. Note how both -// names and type information together specify the variant: -// `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`. -// Each is different and independent. +// 웹 이벤트를 클래스화하는 `enum`을 생성합니다. +// 이름과 타입 정보가 어떻게 선언되는지에 주목하세요: +// `PageLoad != PageUnload`이고 `KeyPress(char) != Paste(string)`입니다. +// 각각은 서로 다르고 독립적입니다. enum WebEvent { - // An `enum` variant may either be `unit-like`, + // `enum`의 각 항목은 `unit-like`일수도, PageLoad, PageUnload, - // like tuple structs, + // 튜플 구조체일수도, KeyPress(char), Paste(String), - // or c-like structures. + // C 스타일 구조체일 수도 있습니다. Click { x: i64, y: i64 }, } -// A function which takes a `WebEvent` enum as an argument and -// returns nothing. +// `WebEvent` 열거형을 받아 아무것도 리턴하지 않는 함수입니다. +// 역주: 함수가 정상적으로 종료되고 로직이 이어지므로 `()`를 리턴하는 것으로 +// 해석할 수 있습니다. fn inspect(event: WebEvent) { match event { WebEvent::PageLoad => println!("page loaded"), @@ -53,20 +53,20 @@ fn main() { ``` -## Type aliases +## 타입 별칭 -If you use a type alias, you can refer to each enum variant via its alias. -This might be useful if the enum's name is too long or too generic, and you -want to rename it. +타입 별칭을 사용하면 각 열거형의 항목을 별칭으로 호출할 수 있습니다. +열거형의 이름이 너무 길거나 너무 일반적이어서, +이름을 다시 지정하고 싶을 때 유용합니다. ```rust,editable -enum VeryVerboseEnumOfThingsToDoWithNumbers { +enum 이름이아주긴_숫자로할수있는작업_열거형 { Add, Subtract, } // Creates a type alias -type Operations = VeryVerboseEnumOfThingsToDoWithNumbers; +type Operations = 이름이아주긴_숫자로할수있는작업_열거형; fn main() { // We can refer to each variant via its alias, not its long and inconvenient @@ -75,7 +75,7 @@ fn main() { } ``` -The most common place you'll see this is in `impl` blocks using the `Self` alias. +가장 자주 볼 수 있는 곳은 `impl` 블록의 `Self` 별칭일 것입니다. ```rust,editable enum VeryVerboseEnumOfThingsToDoWithNumbers { @@ -93,13 +93,13 @@ impl VeryVerboseEnumOfThingsToDoWithNumbers { } ``` -To learn more about enums and type aliases, you can read the -[stabilization report][aliasreport] from when this feature was stabilized into -Rust. +열거형과 타입 별칭에 대해 더 알고 싶다면, +이 기능이 Rust로서 안정화될 때의 [안정화 리포트][aliasreport]를 +참고할 수 있습니다. -### See also: +### 함께 읽기: -[`match`][match], [`fn`][fn], and [`String`][str], ["Type alias enum variants" RFC][type_alias_rfc] +[`match`][match], [`fn`][fn]과 [`String`][str], ["Type alias enum variants" RFC][type_alias_rfc] [c_struct]: https://en.wikipedia.org/wiki/Struct_(C_programming_language) [match]: ../flow_control/match.md diff --git a/src/custom_types/enum/c_like.md b/src/custom_types/enum/c_like.md index 65f832b..133bf6b 100644 --- a/src/custom_types/enum/c_like.md +++ b/src/custom_types/enum/c_like.md @@ -1,19 +1,19 @@ -# C-like +# C 방식 열거형 -`enum` can also be used as C-like enums. +`enum`은 C 방식 열거형도 사용 가능합니다. ```rust,editable // An attribute to hide warnings for unused code. #![allow(dead_code)] -// enum with implicit discriminator (starts at 0) +// 타입과 값을 지정하지 않은 열거형 (0i32에서 시작) enum Number { Zero, One, Two, } -// enum with explicit discriminator +// 값이 지정된 열거형 enum Color { Red = 0xff0000, Green = 0x00ff00, @@ -21,7 +21,7 @@ enum Color { } fn main() { - // `enums` can be cast as integers. + // C 방식 열거형은 정수형으로 타입 캐스팅할 수 있습니다. println!("zero is {}", Number::Zero as i32); println!("one is {}", Number::One as i32); diff --git a/src/custom_types/enum/enum_use.md b/src/custom_types/enum/enum_use.md index 80a86e1..2e7844a 100644 --- a/src/custom_types/enum/enum_use.md +++ b/src/custom_types/enum/enum_use.md @@ -1,6 +1,6 @@ # use -The `use` declaration can be used so manual scoping isn't needed: +`use` 선언을 통해 수동으로 스코핑하지 않고도 내부 상수를 이용할 수 있습니다: ```rust,editable // An attribute to hide warnings for unused code. @@ -17,19 +17,18 @@ enum Role { } fn main() { - // Explicitly `use` each name so they are available without - // manual scoping. + // 각각을 `use`함으로서 스코핑 없이 사용하도록 합니다 use crate::Stage::{Beginner, Advanced}; - // Automatically `use` each name inside `Role`. + // `Role` 안의 모든 항목을 자동으로 `use`합니다 use crate::Role::*; - // Equivalent to `Stage::Beginner`. + //`Stage::Beginner`와 동일 let stage = Beginner; - // Equivalent to `Role::Student`. + // `Role::Student`와 동일 let role = Student; match stage { - // Note the lack of scoping because of the explicit `use` above. + // 위에서 `use`를 사용함으로서 수동 스코핑이 없음을 주목하세요 Beginner => println!("Beginners are starting their learning journey!"), Advanced => println!("Advanced learners are mastering their subjects..."), } @@ -42,9 +41,9 @@ fn main() { } ``` -### See also: +### 함께 읽기: -[`match`][match] and [`use`][use] +[`match`][match]와 [`use`][use] [use]: ../../mod/use.md [match]: ../../flow_control/match.md diff --git a/src/custom_types/enum/testcase_linked_list.md b/src/custom_types/enum/testcase_linked_list.md index cd4e3e6..0e91d80 100644 --- a/src/custom_types/enum/testcase_linked_list.md +++ b/src/custom_types/enum/testcase_linked_list.md @@ -1,55 +1,57 @@ -# Testcase: linked-list +# 테스트 케이스: 연결 리스트 -A common way to implement a linked-list is via `enums`: +통상적으로 연결 리스트를 구현하는 방법은 `enum`을 활용하는 것입니다: ```rust,editable +// 역주: 이렇게 하면 해당 파일 전체에서 `List` 하위 객체를 +// 스코핑 없이 바로 접근할 수 있게 됩니다. use crate::List::*; enum List { - // Cons: Tuple struct that wraps an element and a pointer to the next node + // Cons: 항목과 다음 노드로의 포인터를 감싸는 튜플 구조체 Cons(u32, Box), - // Nil: A node that signifies the end of the linked list + // Nil: 연결 리스트의 끝을 나타내는 선언자 Nil, } -// Methods can be attached to an enum +// 열거형도 메서드를 붙일 수 있습니다 impl List { - // Create an empty list + // 빈 `List`를 생성합니다 fn new() -> List { - // `Nil` has type `List` + // `Nil`은 `List` 타입입니다 Nil } - // Consume a list, and return the same list with a new element at its front + // `List`의 소유권을 가져와, 새 항목이 맨 앞에 추가된 `List`를 + // 만들어 리턴합니다. fn prepend(self, elem: u32) -> List { - // `Cons` also has type List + // `Cons`도 `List` 타입입니다 Cons(elem, Box::new(self)) } - // Return the length of the list + // `List`의 길이를 리턴합니다 fn len(&self) -> u32 { - // `self` has to be matched, because the behavior of this method - // depends on the variant of `self` - // `self` has type `&List`, and `*self` has type `List`, matching on a - // concrete type `T` is preferred over a match on a reference `&T` - // after Rust 2018 you can use self here and tail (with no ref) below as well, - // rust will infer &s and ref tail. - // See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html + // `self`의 종류에 따라 행동이 바뀌기에 매칭이 필요합니다. + // `self`는 `&List` 타입이고 `*self`는 `List` 타입이며, + // 정적 타입 `T`가 `&T`보다 매칭에 선호됩니다. + // Rust 2018부터는 레퍼런스 없이 self나 tail을 사용할 수 있으며, + // 이때 러스트는 `&s`와 `ref tail`을 추정해 컴파일합니다. + // 더 보기: https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html match *self { - // Can't take ownership of the tail, because `self` is borrowed; - // instead take a reference to the tail + // `self`가 빌려온 것으로 `tail`을 가져올 수 없어, 이의 레퍼런스를 + // 가져옵니다. Cons(_, ref tail) => 1 + tail.len(), - // Base Case: An empty list has zero length + // 빈 리스트는 기본적으로 0의 길이이죠. Nil => 0 } } - // Return representation of the list as a (heap allocated) string + // 리스트 전체를 (heap 할당된) `String`으로 리턴합니다 fn stringify(&self) -> String { match *self { Cons(head, ref tail) => { - // `format!` is similar to `print!`, but returns a heap - // allocated string instead of printing to the console + // `format!`은 `print!`와 유사하지만, 콘솔에 뿌리는 대신 + // heap 할당된 string을 리턴합니다 format!("{}, {}", head, tail.stringify()) }, Nil => { @@ -60,23 +62,23 @@ impl List { } fn main() { - // Create an empty linked list + // 빈 연결 리스트를 만듭니다 let mut list = List::new(); - // Prepend some elements + // 서두에 아이템 몇 개를 추가합니다 list = list.prepend(1); list = list.prepend(2); list = list.prepend(3); - // Show the final state of the list + // 최종 상태를 확인합니다 println!("linked list has length: {}", list.len()); println!("{}", list.stringify()); } ``` -### See also: +### 함께 읽기: -[`Box`][box] and [methods][methods] +[`Box`][box]와 [methods][methods] [box]: ../../std/box.md [methods]: ../../fn/methods.md diff --git a/src/variable_bindings.md b/src/variable_bindings.md index b280ed5..b186d21 100644 --- a/src/variable_bindings.md +++ b/src/variable_bindings.md @@ -1,11 +1,10 @@ -# Variable Bindings +# 변수 할당 -Rust provides type safety via static typing. Variable bindings can be type -annotated when declared. However, in most cases, the compiler will be able -to infer the type of the variable from the context, heavily reducing the -annotation burden. +러스트는 정적 타입을 이용해 타입 안전을 제공합니다. 이에 따라 변수를 할당할 때 타입을 +지정할 수 있습니다. 하지만 대부분의 경우, 컴파일러가 타입을 사용으로부터 추정할 +수 있기 때문에, 타입 선언의 필요성이 크게 줄어듭니다. -Values (like literals) can be bound to variables, using the `let` binding. +리터럴과 같은 모든 값은 `let` 바인딩을 통해 변수에 할당할 수 있습니다. ```rust,editable fn main() { diff --git a/src/variable_bindings/declare.md b/src/variable_bindings/declare.md index e9dee6a..d0d41fa 100644 --- a/src/variable_bindings/declare.md +++ b/src/variable_bindings/declare.md @@ -1,8 +1,12 @@ -# Declare first +# 우선 선언하기 -It's possible to declare variable bindings first, and initialize them later. -However, this form is seldom used, as it may lead to the use of uninitialized -variables. +변수 할당을 우선 선언하고 나중에 초기화할 수도 있지만, 모든 변수는 사용되기 전에 +초기화되어야 합니다: 컴파일러가 초기화되지 않은 변수의 사용을 금지하며, 이는 +지정되지 않은 행위로 이어질 수 있기 때문입니다. + +변수의 선언과 초기화를 따로 하는 경우는 흔치 않습니다. 코드를 읽을 때 초기화문이 +떨어져 있으면 이를 찾기도 어렵고요. 변수 자체를 그 변수가 사용되는 곳 근처에 선언하는 +경우가 일반적입니다. ```rust,editable,ignore,mdbook-runnable fn main() { @@ -14,7 +18,7 @@ fn main() { // Initialize the binding a_binding = x * x; - } + } // 역주: a_binding을 초기화하기 위해 사용한 x: i32는 이제 없습니다 println!("a binding: {}", a_binding); @@ -29,6 +33,3 @@ fn main() { println!("another binding: {}", another_binding); } ``` - -The compiler forbids use of uninitialized variables, as this would lead to -undefined behavior. diff --git a/src/variable_bindings/freeze.md b/src/variable_bindings/freeze.md index b399570..138127d 100644 --- a/src/variable_bindings/freeze.md +++ b/src/variable_bindings/freeze.md @@ -1,7 +1,8 @@ -# Freezing +# 고정하기 -When data is bound by the same name immutably, it also *freezes*. *Frozen* data can't be -modified until the immutable binding goes out of scope: +수정 가능한 변수가 같은 이름의 수정되지 않는 변수로 추가 선언되면, *고정* 상태가 +됩니다. *고정된* 데이터는 수정되지 않는 변수의 스코프 밖으로 나갈 때까지 수정할 수 +없게 됩니다: ```rust,editable,ignore,mdbook-runnable fn main() { diff --git a/src/variable_bindings/mut.md b/src/variable_bindings/mut.md index 0925132..d7d8fde 100644 --- a/src/variable_bindings/mut.md +++ b/src/variable_bindings/mut.md @@ -1,7 +1,6 @@ -# Mutability +# 수정 가능성 -Variable bindings are immutable by default, but this can be overridden using -the `mut` modifier. +변수 할당은 기본적으로 수정 불가능하지만, `mut` 수정자를 통해 덮어씌울 수 있습니다. ```rust,editable,ignore,mdbook-runnable fn main() { @@ -21,4 +20,4 @@ fn main() { } ``` -The compiler will throw a detailed diagnostic about mutability errors. +컴파일러는 수정 가능성 오류에 대해 자세한 분석을 리턴합니다. diff --git a/src/variable_bindings/scope.md b/src/variable_bindings/scope.md index 9e3c79e..625e99c 100644 --- a/src/variable_bindings/scope.md +++ b/src/variable_bindings/scope.md @@ -1,29 +1,32 @@ -# Scope and Shadowing +# 스코프와 섀도잉 + +변수 할당은 스코프를 가지며, *블록* 안에서 존재하는 것으로 간주됩니다. 블록은 +중괄호 `{}`로 묶인 선언문 덩어리입니다. -Variable bindings have a scope, and are constrained to live in a *block*. A -block is a collection of statements enclosed by braces `{}`. ```rust,editable,ignore,mdbook-runnable fn main() { - // This binding lives in the main function + // 이 할당문은 main 함수에 있습니다. let long_lived_binding = 1; - // This is a block, and has a smaller scope than the main function + // 이것이 블록이며, main 함수보다 작은 스코프입니다. { - // This binding only exists in this block + // 이 바인딩은 이 스코프에만 있습니다. let short_lived_binding = 2; println!("inner short: {}", short_lived_binding); } - // End of the block + // 블록 끝 - // Error! `short_lived_binding` doesn't exist in this scope + // 오류! `short_lived_binding`이 더이상 존재하지 않습니다. println!("outer short: {}", short_lived_binding); // FIXME ^ Comment out this line println!("outer long: {}", long_lived_binding); } ``` -Also, [variable shadowing][variable-shadow] is allowed. + +또, [변수 섀도잉][variable-shadow]도 가능합니다. + ```rust,editable,ignore,mdbook-runnable fn main() { let shadowed_binding = 1; @@ -43,4 +46,5 @@ fn main() { println!("shadowed in outer block: {}", shadowed_binding); } ``` + [variable-shadow]: https://en.wikipedia.org/wiki/Variable_shadowing From d8b134e666f1f20c94227502989d0b1584f056f9 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sat, 12 Apr 2025 12:06:48 +0900 Subject: [PATCH 07/11] =?UTF-8?q?index.md=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 번역되지 않았던 줄을 번역 완료했습니다. (SUMMARY 이용) --- src/index.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/index.md b/src/index.md index 0e807cf..b19e290 100644 --- a/src/index.md +++ b/src/index.md @@ -17,7 +17,7 @@ - [사용자 정의 자료형](custom_types.md) - `struct` 와 `enum`. -- [변수 바인딩](variable_bindings.md) - mutable bindings, scope, shadowing. +- [변수 바인딩](variable_bindings.md) - 수정 가능한 변수와 스코프, 섀도잉. - [자료형](types.md) - 타입을 변경하고 정의하는 방법에 대해 배워 봅니다. @@ -27,37 +27,37 @@ - [제어문](flow_control.md) - `if`/`else`, `for`, 그리고 여러 다른 것들. -- [함수](fn.md) - Learn about Methods, Closures and High Order Functions. +- [함수](fn.md) - 메서드, 클로저와 상위 순서의 함수를 알아봅시다. -- [모듈](mod.md) - Organize code using modules +- [모듈](mod.md) - 모듈을 통해 코드를 정리해봅시다. -- [크레이트(Crate)](crates.md) - A crate is a compilation unit in Rust. Learn to create a library. +- [크레이트(Crate)](crates.md) - 크레이트는 러스트의 컴파일 단위입니다. 라이브러리도 만들어봅시다. -- [카고(Cargo)](cargo.md) - Go through some basic features of the official Rust package management tool. +- [카고(Cargo)](cargo.md) - 러스트의 공식 패키지 관리 툴의 기본 기능을 알아봅시다. -- [Attributes](attribute.md) - An attribute is metadata applied to some module, crate or item. +- [속성](attribute.md) - 속성은 모듈, 크레이트 또는 아이템에 적용하는 메타데이터입니다. -- [Generics](generics.md) - Learn about writing a function or data type which can work for multiple types of arguments. +- [제네릭](generics.md) - 여러 타입의 매개변수를 가지는 함수나 데이터 타입을 작성하는 법을 알아봅시다. -- [Scoping rules](scope.md) - Scopes play an important part in ownership, borrowing, and lifetimes. +- [스코프 규칙](scope.md) - 스코프는 소유권, 빌리기와 수명 주기에 중요한 역할을 합니다. -- [Traits](trait.md) - A trait is a collection of methods defined for an unknown type: `Self` +- [트레잇](trait.md) - 트레잇은 알 수 없는 타입 `Self`를 위해 선언된 메서드 모음입니다. -- [Macros](macros.md) - Macros are a way of writing code that writes other code, which is known as metaprogramming. +- [매크로](macros.md) - 매크로는 다른 코드를 작성하는 코드로, 메타프로그래밍이라고 부르기도 합니다. -- [Error handling](error.md) - Learn Rust way of handling failures. +- [오류 다루기](error.md) - 러스트에서 실패를 다루는 법을 배워봅니다. -- [Std library types](std.md) - Learn about some custom types provided by `std` library. +- [std 라이브러리 타입](std.md) - `std` 라이브러리에서 제공하는 몇몇 커스텀 타입을 알아봅시다. -- [Std misc](std_misc.md) - More custom types for file handling, threads. +- [std 라이브러리 잡동사니](std_misc.md) - 파일 핸들링과 스레드에 대한 더 많은 커스텀 타입입니다. -- [Testing](testing.md) - All sorts of testing in Rust. +- [테스트하기](testing.md) - 러스트에서의 모든 방식의 테스트. -- [Unsafe Operations](unsafe.md) - "안전하지 않은" 동작 블록에 진입하는 방법을 알아봅니다. +- [Unsafe한 작업](unsafe.md) - "안전하지 않은" 동작 블록에 진입하는 방법을 알아봅니다. -- [Compatibility](compatibility.md) - 러스트의 발전과 가능한 호환성 문제를 다룹니다. +- [호환성](compatibility.md) - 러스트의 발전과 가능한 호환성 문제를 다룹니다. -- [Meta](meta.md) - Documentation, Benchmarking. +- [메타데이터](meta.md) - 문서화와 벤치마크. [rust]: https://www.rust-lang.org/ From a23810415cb44bb4b9c61835ef18f1ff93ad4bf0 Mon Sep 17 00:00:00 2001 From: HotoRas Date: Sat, 12 Apr 2025 14:09:26 +0900 Subject: [PATCH 08/11] =?UTF-8?q?5~7=EC=9E=A5=20=EB=B2=88=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 5. 자료형 다루기, 6. 타입 변환, 7. 표현식 파트 번역이 완료되었습니다. * 이제 진짜 시작입니다. 1/5 정도 왔네요. - 8.5.1. 아이템 해체 파트의 비-코드 부분 번역이 완료되었습니다. - 8. 제어문 파트의 일부 본문을 영문판으로 대치했습니다. --- src/SUMMARY.md | 6 +- src/conversion.md | 13 ++-- src/conversion/from_into.md | 61 +++++++++++++------ src/conversion/string.md | 56 ++++++++++++----- src/conversion/try_from_try_into.md | 9 ++- src/expression.md | 19 +++--- .../match/destructuring/destructure_slice.md | 8 +-- .../destructuring/destructure_structures.md | 4 +- .../match/destructuring/destructure_tuple.md | 4 +- src/flow_control/match/guard.md | 43 ++++++++++--- src/types.md | 15 ++--- src/types/alias.md | 32 +++++----- src/types/cast.md | 59 ++++++++++-------- src/types/inference.md | 26 ++++---- src/types/literals.md | 24 ++++---- 15 files changed, 225 insertions(+), 154 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f66a344..a36dad6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -29,16 +29,16 @@ - [우선 선언하기](variable_bindings/declare.md) - [고정하기](variable_bindings/freeze.md) -- [자료형](types.md) +- [자료형 다루기](types.md) - [캐스팅](types/cast.md) - [리터럴](types/literals.md) - [추적](types/inference.md) - [별명 붙이기](types/alias.md) -- [형변환](conversion.md) +- [타입 변환](conversion.md) - [`From`과 `Into`](conversion/from_into.md) - [`TryFrom`과 `TryInto`](conversion/try_from_try_into.md) - - [`string`으로, `string`에서](conversion/string.md) + - [`String`으로, `String`에서](conversion/string.md) - [표현식](expression.md) diff --git a/src/conversion.md b/src/conversion.md index 52a96d0..24c01fd 100644 --- a/src/conversion.md +++ b/src/conversion.md @@ -1,12 +1,11 @@ -# Conversion +# 타입 변환 -Primitive types can be converted to each other through [casting]. +기본 타입은 [캐스팅][casting]을 통해 변환이 가능합니다. -Rust addresses conversion between custom types (i.e., `struct` and `enum`) -by the use of [traits]. The generic -conversions will use the [`From`] and [`Into`] traits. However there are more -specific ones for the more common cases, in particular when converting to and -from `String`s. +러스트에서는 커스텀 타입(`struct`, `enum`과 같은)간의 전환을 [트레잇][traits]을 이용해 +진행합니다. 기본적인 변환은 [`From`]과 [`Into`] 트레잇을 이용합니다. 하지만 +더 흔한 경우에 대해 특정한 행위가 있을 수 있습니다. 특히 `String`으로, +또는 `String`에서 전환하는 경우엔 말이죠. [casting]: types/cast.md [traits]: trait.md diff --git a/src/conversion/from_into.md b/src/conversion/from_into.md index 266d10f..b1e6df7 100644 --- a/src/conversion/from_into.md +++ b/src/conversion/from_into.md @@ -1,24 +1,23 @@ -# `From` and `Into` +# `From`과 `Into` -The [`From`] and [`Into`] traits are inherently linked, and this is actually part of -its implementation. If you are able to convert type A from type B, then it -should be easy to believe that we should be able to convert type B to type A. +[`From`]과 [`Into`]는 내부적으로 연결되어 있고, 실제로 구현의 일부입니다. +타입 A에서 B로 변환이 가능하다면, B에서 A로 또한 손쉽게 가능하다고 확신할 수 있죠. ## `From` -The [`From`] trait allows for a type to define how to create itself from another -type, hence providing a very simple mechanism for converting between several -types. There are numerous implementations of this trait within the standard -library for conversion of primitive and common types. +`From` 트레잇은 다른 타입에서 자신을 만드는 방법을 정의하게 합니다. 이로서 서로 다른 +많은 타입과 전환하는 아주 간단한 방식을 제공할 수 있죠. +표준 라이브러리만에서도 이미 사전 정의 타입과 표준 타입 간의 변환을 수행하는 +수많은 구현체가 있습니다. -For example we can easily convert a `str` into a `String` +예를 들어 `&str`에서 `String`으로 아주 간단하게 변환할 수 있죠. ```rust let my_str = "hello"; let my_string = String::from(my_str); ``` -We can do similar for defining a conversion for our own type. +비슷한 동작을 우리가 만든 타입에서도 변환을 선언해 진행할 수 있습니다. ```rust,editable use std::convert::From; @@ -42,13 +41,40 @@ fn main() { ## `Into` -The [`Into`] trait is simply the reciprocal of the `From` trait. That is, if you -have implemented the `From` trait for your type, `Into` will call it when -necessary. +`Into` 트레잇은 `From` 트레잇의 반대 동작입니다. 타입을 다른 타입으로 전환하는 +방법을 선언하죠. -Using the `Into` trait will typically require specification of the type to -convert into as the compiler is unable to determine this most of the time. -However this is a small trade-off considering we get the functionality for free. +`into()`를 호출하는 경우 도착 타입을 지정해야 합니다. 대부분의 경우 컴파일러가 +타입 추정을 확실하게 하기 어렵거든요. + +```rust, editable +use std::convert::Into; + +#[derive(Debug)] +struct Number { + value: i32, +} + +impl Into for i32 { + fn into(self) -> Number { + Number { value: self } + } +} + +fn main() { + let int = 5; + // 타입 어노테이션을 지워보세요 + let num: Number = int.into(); + println!("My number is {:?}", num); +} +``` + +## `From`과 `Into`는 상호 전환이 가능합니다 + +`From`과 `Into`는 상호 전환이 가능하도록 설계되어 있어, 두 트레잇 모두에 대해 선언할 +필요가 없습니다. `From` 트레잇을 타입에 선언했다면 `Into`는 필요할 때 선언됩니다. +하지만 반대 방향은 그렇지 않습니다: `Into`를 선언한 타입은 `From` 트레잇을 자동으로 +선언하지 않습니다. ```rust,editable use std::convert::From; @@ -58,6 +84,7 @@ struct Number { value: i32, } +// `From`을 선언합니다 impl From for Number { fn from(item: i32) -> Self { Number { value: item } @@ -66,7 +93,7 @@ impl From for Number { fn main() { let int = 5; - // Try removing the type declaration + // `Into`를 사용합니다 let num: Number = int.into(); println!("My number is {:?}", num); } diff --git a/src/conversion/string.md b/src/conversion/string.md index ab14521..deeba58 100644 --- a/src/conversion/string.md +++ b/src/conversion/string.md @@ -1,11 +1,11 @@ -# To and from Strings +# `String`으로, `String`에서 -## Converting to String +## 문자열로 변환하기 -To convert any type to a `String` is as simple as implementing the [`ToString`] -trait for the type. Rather than doing so directly, you should implement the -[`fmt::Display`][Display] trait which automagically provides [`ToString`] and -also allows printing the type as discussed in the section on [`print!`][print]. +어떤 타입을 `String`으로 변환하는 것은 [`ToString`] 트레잇을 타입에 구현하는 것 +정도로 아주 간단합니다만, 이를 직접 하는 것보다는 [`ToString`]과 [`print!`]에서 +다룬 타입 프린팅을 자동으로 제공하는 [`fmt::Display`][Display] 트레잇을 구현해야 +합니다. ```rust,editable use std::fmt; @@ -26,17 +26,12 @@ fn main() { } ``` -## Parsing a String +## 문자열 파싱하기 -One of the more common types to convert a string into is a number. The idiomatic -approach to this is to use the [`parse`] function and either to arrange for -type inference or to specify the type to parse using the 'turbofish' syntax. -Both alternatives are shown in the following example. - -This will convert the string into the type specified so long as the [`FromStr`] -trait is implemented for that type. This is implemented for numerous types -within the standard library. To obtain this functionality on a user defined type -simply implement the [`FromStr`] trait for that type. +문자열을 여러 타입으로 변환하는 것은 매우 유용하지만, 대부분의 일반적인 문자열 작업은 +이를 수로 변환하는 것입니다. 이상적인 접근은 [`parse`] 함수를 사용하고 타입 추정으로 +정형화하거나, 고속 파싱을 이용하는 것입니다. 두 방식 모두 아래에 잘 설명되어 +있습니다. ```rust,editable fn main() { @@ -48,6 +43,35 @@ fn main() { } ``` +이런 작업을 사용자 지정 타입에 가져오려면 [`FromStr`] 트레잇을 그 타입에 구현하면 +됩니다. + +```rust,editable +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug)] +struct Circle { + radius: i32, +} + +impl FromStr for Circle { + type Err = ParseIntError; + fn from_str(s: &str) -> Result { + match s.trim().parse() { + Ok(num) => Ok(Circle{ radius: num }), + Err(e) => Err(e), + } + } +} + +fn main() { + let radius = " 3 "; + let circle: Circle = radius.parse().unwrap(); + println!("{:?}", circle); +} +``` + [`ToString`]: https://doc.rust-lang.org/std/string/trait.ToString.html [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html [print]: ../hello/print.md diff --git a/src/conversion/try_from_try_into.md b/src/conversion/try_from_try_into.md index ac62ad1..efa9afb 100644 --- a/src/conversion/try_from_try_into.md +++ b/src/conversion/try_from_try_into.md @@ -1,9 +1,8 @@ -# `TryFrom` and `TryInto` +# `TryFrom`과 `TryInto` -Similar to [`From` and `Into`][from-into], [`TryFrom`] and [`TryInto`] are -generic traits for converting between types. Unlike `From`/`Into`, the -`TryFrom`/`TryInto` traits are used for fallible conversions, and as such, -return [`Result`]s. +[`From`과 `Into`][from-into]와 비슷하게, [`TryFrom`]과 [`TryInto`]는 두 타입간 +변환을 수행하는 표준 트레잇입니다. `From`/`Into`와 다르게 `TryFrom`/`TryInto`는 +실패할 수 있는 변환을 위해 사용하며, 그에 따라 [`Result`]를 반환하죠. [from-into]: from_into.html [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html diff --git a/src/expression.md b/src/expression.md index 467bc10..622d1fb 100644 --- a/src/expression.md +++ b/src/expression.md @@ -1,8 +1,8 @@ -# Expressions +# 표현식 -A Rust program is (mostly) made up of a series of statements: +(대부분의) 러스트 프로그램은 선언문들로 이루어져 있습니다. -``` +```rust,editable fn main() { // statement // statement @@ -10,10 +10,10 @@ fn main() { } ``` -There are a few kinds of statements in Rust. The most common two are declaring -a variable binding, and using a `;` with an expression: +러스트엔 몇몇 종류의 선언문이 있습니다. +가장 흔한 것은 변수 선언과 표현식 끝에 `;`를 두는 것이죠: -``` +```rust,editable fn main() { // variable binding let x = 5; @@ -25,10 +25,9 @@ fn main() { } ``` -Blocks are expressions too, so they can be used as values in -assignments. The last expression in the block will be assigned to the -place expression such as a local variable. However, if the last expression of the block ends with a -semicolon, the return value will be `()`. +블록 또한 표현식이어서, 값이나 대입에 사용될 수 있습니다. 블록의 가장 마지막 표현식의 +결과가 내부 변수와 같은 대응식에 전달됩니다. 하지만 마지막 표현식이 `;`로 끝난다면, +`()`가 리턴됩니다. ```rust,editable fn main() { diff --git a/src/flow_control/match/destructuring/destructure_slice.md b/src/flow_control/match/destructuring/destructure_slice.md index 93b7e42..dd26335 100644 --- a/src/flow_control/match/destructuring/destructure_slice.md +++ b/src/flow_control/match/destructuring/destructure_slice.md @@ -1,6 +1,6 @@ -# arrays/slices +# 배열/슬라이스 -Like tuples, arrays and slices can be destructured this way: +튜플과 같이, 배열과 슬라이스도 이런 식으로 해체할 수 있습니다: ```rust,editable fn main() { @@ -43,6 +43,6 @@ fn main() { } ``` -### See also: +### 함께 읽기: -[Arrays and Slices](../../../primitives/array.md) and [Binding](../binding.md) for `@` sigil +[배열과 슬라이스](../../../primitives/array.md), [바인딩](../binding.md) for `@` sigil diff --git a/src/flow_control/match/destructuring/destructure_structures.md b/src/flow_control/match/destructuring/destructure_structures.md index 9e43b70..35e5061 100644 --- a/src/flow_control/match/destructuring/destructure_structures.md +++ b/src/flow_control/match/destructuring/destructure_structures.md @@ -1,6 +1,6 @@ -# structs +# 구조체 -Similarly, a `struct` can be destructured as shown: +비슷하게, 구조체도 아래와 같이 해체할 수 있습니다: ```rust,editable fn main() { diff --git a/src/flow_control/match/destructuring/destructure_tuple.md b/src/flow_control/match/destructuring/destructure_tuple.md index 30ae68f..c027f7f 100644 --- a/src/flow_control/match/destructuring/destructure_tuple.md +++ b/src/flow_control/match/destructuring/destructure_tuple.md @@ -1,6 +1,6 @@ -# tuples +# 튜플 -Tuples can be destructured in a `match` as follows: +튜플은 `match`로 아래와 같이 해체할 수 있습니다: ```rust,editable fn main() { diff --git a/src/flow_control/match/guard.md b/src/flow_control/match/guard.md index 336b7f1..df9fb2c 100644 --- a/src/flow_control/match/guard.md +++ b/src/flow_control/match/guard.md @@ -3,17 +3,39 @@ A `match` *guard* can be added to filter the arm. ```rust,editable +#[allow(dead_code)] +enum Temperature { + Celsius(i32), + Fahrenheit(i32), +} + fn main() { - let pair = (2, -2); - // TODO ^ Try different values for `pair` - - println!("Tell me about {:?}", pair); - match pair { - (x, y) if x == y => println!("These are twins"), - // The ^ `if condition` part is a guard - (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), - (x, _) if x % 2 == 1 => println!("The first one is odd"), - _ => println!("No correlation..."), + let temperature = Temperature::Celsius(35); + // ^ TODO try different values for `temperature` + + match temperature { + Temperature::Celsius(t) if t > 30 => println!("{}C is above 30 Celsius", t), + // The `if condition` part ^ is a guard + Temperature::Celsius(t) => println!("{}C is equal to or below 30 Celsius", t), + + Temperature::Fahrenheit(t) if t > 86 => println!("{}F is above 86 Fahrenheit", t), + Temperature::Fahrenheit(t) => println!("{}F is equal to or below 86 Fahrenheit", t), + } +} +``` + +Note that the compiler won't take guard conditions into account when checking +if all patterns are covered by the match expression. + +```rust,editable,ignore,mdbook-runnable +fn main() { + let number: u8 = 4; + + match number { + i if i == 0 => println!("Zero"), + i if i > 0 => println!("Greater than zero"), + // _ => unreachable!("Should never happen."), + // TODO ^ uncomment to fix compilation } } ``` @@ -21,3 +43,4 @@ fn main() { ### See also: [Tuples](../../primitives/tuples.md) +[Enums](../../custom_types/enum.md) diff --git a/src/types.md b/src/types.md index 1d3b50a..115bdec 100644 --- a/src/types.md +++ b/src/types.md @@ -1,11 +1,12 @@ -# Types +# 자료형 다루기 -Rust provides several mechanisms to change or define the type of primitive and -user defined types. The following sections cover: -* [Casting] between primitive types -* Specifying the desired type of [literals] -* Using [type inference] -* [Aliasing] types +러스트는 사전 제공 또는 사용자 지정된 타입에 대해 여러 수정 및 정의법을 제공합니다. +다음 섹션에서는 다음을 다룹니다: + +* 사전 제공되는 타입 간 [캐스팅][Casting] +* [리터럴][literals]에 원하는 타입 특정 +* [타입 추정][type inference] 활용하기 +* 타입에 [별명 주기][Aliasing] [Casting]: types/cast.md [literals]: types/literals.md diff --git a/src/types/alias.md b/src/types/alias.md index 5fda947..7c5a5a3 100644 --- a/src/types/alias.md +++ b/src/types/alias.md @@ -1,23 +1,19 @@ -# Aliasing +# 별명 만들기 -The `type` statement can be used to give a new name to an existing type. Types -must have `UpperCamelCase` names, or the compiler will raise a warning. The -exception to this rule are the primitive types: `usize`, `f32`, etc. +`type` 선언문은 기존 타입에 새로운 이름을 제공합니다. 타입 이름은 반드시 +`UpperCamelCase`(대문자 시작 카멜-케이스)로 지어야 하며, 아닌 경우 컴파일러에서 +경고를 낼 겁니다. `usize`나 `f32` 등의 사전 정의된 타입은 예외입니다. ```rust,editable -// `NanoSecond` is a new name for `u64`. +// `NanoSecond`, `Inch`, and `U64` are new names for `u64`. type NanoSecond = u64; type Inch = u64; - -// Use an attribute to silence warning. -#[allow(non_camel_case_types)] -type u64_t = u64; -// TODO ^ Try removing the attribute +type U64 = u64; fn main() { - // `NanoSecond` = `Inch` = `u64_t` = `u64`. - let nanoseconds: NanoSecond = 5 as u64_t; - let inches: Inch = 2 as u64_t; + // `NanoSecond` = `Inch` = `U64` = `u64`. + let nanoseconds: NanoSecond = 5 as u64; + let inches: Inch = 2 as U64; // Note that type aliases *don't* provide any extra type safety, because // aliases are *not* new types @@ -28,9 +24,11 @@ fn main() { } ``` -The main use of aliases is to reduce boilerplate; for example the `IoResult` type -is an alias for the `Result` type. +이런 타입 별명은 불필요한 코드를 줄이기 위해 사용됩니다. 예를 들어 +- `io::Result`는 `Result`의 별칭입니다. +- 역주: `Some()`과 `None::`은 각각 `std::option::Option::Some()`과 `std::option::Option::None`의 별칭입니다. + * `Option`는 다시 `std::option::Option`의 별칭입니다. -### See also: +### 함께 보기: -[Attributes](../attribute.md) +[속성](../attribute.md) diff --git a/src/types/cast.md b/src/types/cast.md index 2ca18c1..1782305 100644 --- a/src/types/cast.md +++ b/src/types/cast.md @@ -1,11 +1,10 @@ -# Casting +# 타입 캐스팅 -Rust provides no implicit type conversion (coercion) between primitive types. -But, explicit type conversion (casting) can be performed using the `as` keyword. +러스트는 기본 타입에서 특정하지 않은 경우의 타입 변경(간주하기)을 지원하지 않습니다. +하지만 특정된 타입 변환(캐스팅)은 `as` 키워드를 통해 제공합니다. -Rules for converting between integral types follow C conventions generally, -except in cases where C has undefined behavior. The behavior of all casts -between integral types is well defined in Rust. +일반적인 경우 타입 변환은 C를 준수하지만, C에서 정의되지 않는 경우는 그렇지 않습니다. +러스트는 모든 숫자 타입 변환 방식에 대해 자세하게 정의하고 있습니다. ```rust,editable,ignore,mdbook-runnable // Suppress all warnings from casts which overflow. @@ -14,64 +13,70 @@ between integral types is well defined in Rust. fn main() { let decimal = 65.4321_f32; - // Error! No implicit conversion + // Error! 특정하지 않은 타입 변경 불가 let integer: u8 = decimal; // FIXME ^ Comment out this line - // Explicit conversion + // 특정된 타입 변환 let integer = decimal as u8; let character = integer as char; - // Error! There are limitations in conversion rules. A float cannot be directly converted to a char. + // Error! 타입 변환에는 제한이 있습니다. + // float는 char로 바로 변환할 수 없습니다. let character = decimal as char; // FIXME ^ Comment out this line println!("Casting: {} -> {} -> {}", decimal, integer, character); - // when casting any value to an unsigned type, T, - // T::MAX + 1 is added or subtracted until the value - // fits into the new type + // 값을 음수가 없는 타입 T로 캐스팅할 때, 값이 새 타입에 맞아들어갈 때까지 + // T::MAX + 1을 더하거나 뺍니다. - // 1000 already fits in a u16 + // 1000은 이미 u16에 들어갑니다 println!("1000 as a u16 is: {}", 1000 as u16); // 1000 - 256 - 256 - 256 = 232 - // Under the hood, the first 8 least significant bits (LSB) are kept, - // while the rest towards the most significant bit (MSB) get truncated. + // 내부적으로, 낮은 8비트 값(LSB)은 유지되고 높은 8비트 값(MSB)는 삭제됩니다. println!("1000 as a u8 is : {}", 1000 as u8); // -1 + 256 = 255 println!(" -1 as a u8 is : {}", (-1i8) as u8); - // For positive numbers, this is the same as the modulus + // 양수에서 이 작업은 modulus와 같습니다 println!("1000 mod 256 is : {}", 1000 % 256); - // When casting to a signed type, the (bitwise) result is the same as - // first casting to the corresponding unsigned type. If the most significant - // bit of that value is 1, then the value is negative. + // 음수가 있는 타입으로 변환하면 (이진 수준) 결과가 음수가 없는 것과 동일하게 + // 변환됩니다. 가장 높은 비트가 1이면 음수가 됩니다. - // Unless it already fits, of course. + // 당연히, 이미 맞는 경우엔 딱히 뭐가 없죠. println!(" 128 as a i16 is: {}", 128 as i16); - // 128 as u8 -> 128, whose two's complement in eight bits is: + + // 경계면의 경우로, 128의 8비트 2의 보수는 -128입니다 println!(" 128 as a i8 is : {}", 128 as i8); - // repeating the example above + // 위의 예시를 반복합니다 // 1000 as u8 -> 232 println!("1000 as a u8 is : {}", 1000 as u8); - // and the two's complement of 232 is -24 + // 그리고 232의 2의 보수는 -24죠 println!(" 232 as a i8 is : {}", 232 as i8); - // Since Rust 1.45, the `as` keyword performs a *saturating cast* when casting from float to int. - // If the floating point value exceeds the upper bound or is less than the lower bound, the returned value will be equal to the bound crossed. + // 러스트 1.45부터, `as` 키워드가 float -> int 캐스팅에서 + // *saturating cast*로 동작합니다. 부동소수점 값이 최대값을 넘거나 최소값보다 + // 작아지면, 그 결과값은 그 경계값이 됩니다. - // 300.0 is 255 + // 300.0 as u8 is 255 println!("300.0 is {}", 300.0_f32 as u8); // -100.0 as u8 is 0 println!("-100.0 as u8 is {}", -100.0_f32 as u8); // nan as u8 is 0 println!("nan as u8 is {}", f32::NAN as u8); - // This behavior incures a small runtime cost and can be avoided with unsafe methods, however the results might overflow and return **unsound values**. Use these methods wisely: + // 약간의 런타임 비용을 소모하며, unsafe 메서드를 이용해 피할 수 있습니다. + // 결과값이 오버플로우되어 **이상한 값**을 리턴하는 경우도 있으니, + // 현명하게 사용해 주세요! unsafe { + // 역주: unsafe fn {f32, f64}.to_int_unchecked:: -> T + // where T is one of + // {u8 u16, u32, u64, u128, i8, i16, i32, i64, i128} + // 300.0 is 44 println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); // -100.0 as u8 is 156 diff --git a/src/types/inference.md b/src/types/inference.md index 5d10301..dfbeac7 100644 --- a/src/types/inference.md +++ b/src/types/inference.md @@ -1,28 +1,26 @@ -# Inference +# 타입 추정 -The type inference engine is pretty smart. It does more than looking at the -type of the value expression -during an initialization. It also looks at how the variable is used afterwards -to infer its type. Here's an advanced example of type inference: +타입 추정 엔진은 아주 똑똑합니다. 선언할 때의 값만 보는 것이 아니라, 그 변수가 이후 +어떻게 이용되는지까지 모두 추적해 타입을 추정합니다. 여기 타입 추정에 대한 고급 +예시가 있습니다: ```rust,editable fn main() { - // Because of the annotation, the compiler knows that `elem` has type u8. + // 접미사에 의해 컴파일러는 `elem`이 `u8`임을 확인합니다. let elem = 5u8; - // Create an empty vector (a growable array). + // 빈 벡터(늘어날 수 있는 배열) 선언 let mut vec = Vec::new(); - // At this point the compiler doesn't know the exact type of `vec`, it - // just knows that it's a vector of something (`Vec<_>`). + // 여기에서 컴파일러는 `vec`이 정확하게 어느 타입인지 모릅니다. 그저 무언가의 + // 벡터(`Vec<_>`)라는 것 정도만 확인하죠. - // Insert `elem` in the vector. + // `elem`을 이제 vec에 넣어봅시다. vec.push(elem); - // Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec`) - // TODO ^ Try commenting out the `vec.push(elem)` line + // 아하! 이 시점에서 컴파일러는 `vec`이 `u8`의 벡터(`Vec`)임을 확인합니다. + // TODO ^ 위의 `vec.push(elem)` 줄을 주석 처리해보세요 println!("{:?}", vec); } ``` -No type annotation of variables was needed, the compiler is happy and so is the -programmer! +변수에 대한 타입 선언 없이도, 컴파일러도 행복하고 프로그래머도 행복합니다! diff --git a/src/types/literals.md b/src/types/literals.md index fd26b9d..f3db228 100644 --- a/src/types/literals.md +++ b/src/types/literals.md @@ -1,11 +1,10 @@ -# Literals +# 타입 리터럴 -Numeric literals can be type annotated by adding the type as a suffix. As an example, -to specify that the literal `42` should have the type `i32`, write `42i32`. +숫자 리터럴은 접미어를 통해 타입 선언될 수 있습니다. 예를 들어 `42`가 `i32` +타입이어야 하는 경우, `42i32`로 선언하면 됩니다. -The type of unsuffixed numeric literals will depend on how they are used. If no -constraint exists, the compiler will use `i32` for integers, and `f64` for -floating-point numbers. +접미어 없이 선언된 숫자 리터럴은 사용에 따라 타입이 지정됩니다. 맥락을 확인할 수 +없는 경우, 컴파일러는 정수는 `i32`, 부동소수점은 `f64`로 지정합니다. ```rust,editable fn main() { @@ -27,14 +26,13 @@ fn main() { } ``` -There are some concepts used in the previous code that haven't been explained -yet, here's a brief explanation for the impatient readers: +위 코드에는 아직 설명하지 않은 컨셉이 사용되어, 급한 분들을 위해 짧은 설명을 +덧붙입니다: -* `std::mem::size_of_val` is a function, but called with its *full path*. Code - can be split in logical units called *modules*. In this case, the - `size_of_val` function is defined in the `mem` module, and the `mem` module - is defined in the `std` *crate*. For more details, see - [modules][mod] and [crates][crate]. +* `std::mem::size_of_val`은 함수이지만 *전체 경로*를 통해 호출되었습니다. 코드는 + *모듈*이라는 논리적 단위로 나뉩니다. 여기서는 `size_of_val` 함수는 `mem` 모듈에, + `mem` 모듈은 `std` *크레이트*에 선언되어 있습니다. 더 자세한 것은 + [모듈][mod]과 [크레이트][crate]를 참고하세요. [mod]: ../mod.md [crate]: ../crates.md From 1ea00405111bacf2668db8393f9b3f5fffd6399d Mon Sep 17 00:00:00 2001 From: Hoto Ras Date: Sat, 7 Jun 2025 11:37:24 +0900 Subject: [PATCH 09/11] =?UTF-8?q?8=EC=9E=A5=20=ED=9D=90=EB=A6=84=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=20(1=EC=B0=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit if-else문 완료 loop문 2/3 사실 수명주기(`'_lifetime`)를 직접 지정하는 경우는 없다고 설명하고 있는데 아무리 봐도 loop/for/while의 라벨링이 수명주기랑 비슷하단 말이죠.. --- src/flow_control.md | 6 +++--- src/flow_control/if_else.md | 27 +++++++++++++-------------- src/flow_control/loop.md | 7 +++---- src/flow_control/loop/nested.md | 11 +++++------ 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/flow_control.md b/src/flow_control.md index c8a2f9e..a1292f1 100644 --- a/src/flow_control.md +++ b/src/flow_control.md @@ -1,4 +1,4 @@ -# Flow of Control +# 흐름 제어 -An essential part of any programming languages are ways to modify control flow: -`if`/`else`, `for`, and others. Let's talk about them in Rust. +프로그래밍 언어에서 필수적인 부분은 `if`/`else`, `for` 등과 같이 흐름을 제어하는 방법입니다. +러스트에서 이게 어떻게 동작하는지 알아봅시다. diff --git a/src/flow_control/if_else.md b/src/flow_control/if_else.md index d761aa8..ea8519c 100644 --- a/src/flow_control/if_else.md +++ b/src/flow_control/if_else.md @@ -1,36 +1,35 @@ # if/else -Branching with `if`-`else` is similar to other languages. Unlike many of them, -the boolean condition doesn't need to be surrounded by parentheses, and each -condition is followed by a block. `if`-`else` conditionals are expressions, -and, all branches must return the same type. +`if`와 `else`를 이용한 분기는 다른 언어와 유사합니다. +하지만 대부분과 달리 이진 조건문을 괄호로 감쌀 필요가 없으며, 각 조건문 뒤에는 블록이 따라옵니다. +`if`-`else` 조건문은 표현식이며, 모든 분기는 같은 타입을 반환해야 합니다. ```rust,editable fn main() { let n = 5; if n < 0 { - print!("{} is negative", n); + print!("{}은/는 음수입니다", n); } else if n > 0 { - print!("{} is positive", n); + print!("{}은/는 양수입니다", n); } else { - print!("{} is zero", n); - } + print!("{}는 0입니다", n); + } // 이 블록의 반환값은 Rust 2024부터 `!`이며, 이전 버전에서는 `()`입니다. let big_n = if n < 10 && n > -10 { - println!(", and is a small number, increase ten-fold"); + println!(", 그리고 작은 수이므로 10을 곱하겠습니다."); - // This expression returns an `i32`. + // 이 표현식은 `i32`를 반환합니다. 10 * n } else { - println!(", and is a big number, halve the number"); + println!(", 그리고 큰 수이므로 2로 나누겠습니다."); - // This expression must return an `i32` as well. + // 위 표현식이 `i32`를 반환하므로, 이 표현식도 `i32`를 반환해야 합니다. n / 2 - // TODO ^ Try suppressing this expression with a semicolon. + // TODO ^ 이 표현식 뒤에 `;`를 넣어 보세요 }; - // ^ Don't forget to put a semicolon here! All `let` bindings need it. + // ^ 여기에 세미콜론을 붙여야 함을 기억하세요! 모든 `let` 바인딩 끝에는 세미콜론이 필요합니다. println!("{} -> {}", n, big_n); } diff --git a/src/flow_control/loop.md b/src/flow_control/loop.md index 4a2405b..bc81b6f 100644 --- a/src/flow_control/loop.md +++ b/src/flow_control/loop.md @@ -1,10 +1,9 @@ # loop -Rust provides a `loop` keyword to indicate an infinite loop. +러스트는 무한 반복을 위해 `loop` 키워드를 제공합니다. -The `break` statement can be used to exit a loop at anytime, whereas the -`continue` statement can be used to skip the rest of the iteration and start a -new one. +`break` 선언문을 이용해 무한 반복을 언제든 빠져나올 수 있으며, +`continue` 선언문을 이용해 남은 순서를 건너뛰고 다음 반복으로 넘어갈 수 있습니다. ```rust,editable fn main() { diff --git a/src/flow_control/loop/nested.md b/src/flow_control/loop/nested.md index 01c55a5..4ade11d 100644 --- a/src/flow_control/loop/nested.md +++ b/src/flow_control/loop/nested.md @@ -1,8 +1,7 @@ -# Nesting and labels +# 중첩과 라벨링 -It's possible to `break` or `continue` outer loops when dealing with nested -loops. In these cases, the loops must be annotated with some `'label`, and the -label must be passed to the `break`/`continue` statement. +반복문이 중첩되었을 때 바깥의 반복문을 `break`하거나 `continue`할 수도 있습니다. +이 경우 반복문을 `'라벨`과 함께 선언해야 하며, 대상이 되는 라벨을 `break`/`continue`문에 제공해야 합니다. ```rust,editable #![allow(unreachable_code)] @@ -19,10 +18,10 @@ fn main() { // This breaks the outer loop break 'outer; - } + } // end of 'inner loop println!("This point will never be reached"); - } + } // end of 'outer loop println!("Exited the outer loop"); } From 7542504ad38b952d865da225fd453dacf3b3a599 Mon Sep 17 00:00:00 2001 From: Hoto Ras Date: Sat, 7 Jun 2025 12:53:51 +0900 Subject: [PATCH 10/11] =?UTF-8?q?8=EC=9E=A5=20=ED=9D=90=EB=A6=84=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=20(2=EC=B0=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - for, while, let..else, if let/while let 등 번역 완료 - 반복문에서 리턴하기, 매칭 가드 번역 완료 - 초기 번역인 구조체 해체 부분 표현 일부 변경 We're quarter-a-way done! 1/4이 완성됐습니다! --- src/flow_control/for.md | 51 +++++++++---------- src/flow_control/if_let.md | 16 +++--- src/flow_control/let_else.md | 21 ++++---- src/flow_control/loop/return.md | 8 ++- src/flow_control/match.md | 3 +- src/flow_control/match/binding.md | 11 ++-- .../match/destructuring/destructure_enum.md | 4 +- .../destructuring/destructure_pointers.md | 2 +- .../destructuring/destructure_structures.md | 2 +- .../match/destructuring/destructure_tuple.md | 2 +- src/flow_control/match/guard.md | 9 ++-- src/flow_control/while.md | 4 +- src/flow_control/while_let.md | 22 ++++---- 13 files changed, 75 insertions(+), 80 deletions(-) diff --git a/src/flow_control/for.md b/src/flow_control/for.md index 99e6458..1b48c8b 100644 --- a/src/flow_control/for.md +++ b/src/flow_control/for.md @@ -1,13 +1,12 @@ -# for loops +# for 반복문 -## for and range +## for와 range -The `for in` construct can be used to iterate through an `Iterator`. -One of the easiest ways to create an iterator is to use the range -notation `a..b`. This yields values from `a` (inclusive) to `b` -(exclusive) in steps of one. +`for in` 생성자는 `Iterator`를 이용해 순서를 매길 수 있습니다. +가장 쉬운 방법은 범위 노테이션 `a..b`를 이용하는 거죠. +이는 `a`(포함)부터 `b`(미포함)까지 1 단위로 증가하는 값을 선언합니다. -Let's write FizzBuzz using `for` instead of `while`. +FizzBuzz를 `while` 대신 `for`를 이용해 작성해봅시다. ```rust,editable fn main() { @@ -26,8 +25,8 @@ fn main() { } ``` -Alternatively, `a..=b` can be used for a range that is inclusive on both ends. -The above can be written as: +또는, `a..=b`를 이용할 수도 있습니다. 이 표현은 양쪽 끝이 범위에 포함됩니다. +그래서 위 코드는 아래처럼 작성될 수도 있습니다. ```rust,editable fn main() { @@ -46,19 +45,16 @@ fn main() { } ``` -## for and iterators +## for와 반복자 -The `for in` construct is able to interact with an `Iterator` in several ways. -As discussed in the section on the [Iterator][iter] trait, by default the `for` -loop will apply the `into_iter` function to the collection. However, this is -not the only means of converting collections into iterators. +`for in` 구조는 `Iterator`를 여러 방법으로 접근할 수 있습니다. +[반복자][iter] 트레잇에서 얘기했듯이, `for` 반복문은 집합에서 `into_iter` 함수를 적용합니다. +하지만 이것만이 집합을 반복자로 변환하는 방법은 아닙니다. -`into_iter`, `iter` and `iter_mut` all handle the conversion of a collection -into an iterator in different ways, by providing different views on the data -within. +`into_iter`, `iter`와 `iter_mut`는 내부의 데이터에 대한 서로 다른 시야를 제공함으로서 집합을 반복자로 변환합니다. -* `iter` - This borrows each element of the collection through each iteration. - Thus leaving the collection untouched and available for reuse after the loop. +* `iter` - 매 반복마다 집합에서 각각의 항목을 빌려옵니다. + 원래 집합을 가져오지 않으므로, 원래 집합을 반복문 후에도 활용할 수 있죠. ```rust, editable fn main() { @@ -73,9 +69,9 @@ fn main() { } ``` -* `into_iter` - This consumes the collection so that on each iteration the exact - data is provided. Once the collection has been consumed it is no longer - available for reuse as it has been 'moved' within the loop. +* `into_iter` - 원래 집합을 가져다 반복자로 만들기 때문에 매 반복마다 정확히 그 데이터가 제공됩니다. + 원래 집합을 그대로 가져다 썼기 때문에 그 집합이 반복문으로 '이동'했고, + 반복 이후에 해당 집합을 다시 사용할 수도 없습니다. ```rust, editable fn main() { @@ -90,12 +86,12 @@ fn main() { } ``` -* `iter_mut` - This mutably borrows each element of the collection, allowing for - the collection to be modified in place. +* `iter_mut` - 매 반복마다 항목을 수정 가능한 형태로 빌려오며, 그 값이 변경될 수 있도록 해줍니다. ```rust, editable fn main() { let mut names = vec!["Bob", "Frank", "Ferris"]; + // ^^^ 역주: iter_mut를 호출하려면 원본이 수정 가능해야 합니다. 따라서 mut 키워드를 붙입니다. for name in names.iter_mut() { *name = match name { @@ -108,11 +104,10 @@ fn main() { } ``` -In the above snippets note the type of `match` branch, that is the key -difference in the types of iteration. The difference in type then of course -implies differing actions that are able to be performed. +위 예시에서 `match` 분기의 타입을 확인하세요. 이 부분이 각 반복자 생성 함수의 핵심 차이점입니다. +타입이 다르다는 것은 또한 서로 다른 작업이 가능하다는 것을 의미하기도 합니다. -### See also: +### 함께 읽기: [Iterator][iter] diff --git a/src/flow_control/if_let.md b/src/flow_control/if_let.md index 3742b6f..b2dddf2 100644 --- a/src/flow_control/if_let.md +++ b/src/flow_control/if_let.md @@ -1,6 +1,6 @@ # if let -For some use cases, when matching enums, `match` is awkward. For example: +어떤 사용 환경에서는 `match`를 활용하는 게 애매한 경우가 있습니다. 예를 들어: ```rust // Make `optional` of type `Option` @@ -19,8 +19,7 @@ match optional { ``` -`if let` is cleaner for this use case and in addition allows various -failure options to be specified: +이런 경우 `if let`을 사용하는 것이 더 간단하며, 추가로 실패했을 때의 처리도 가능합니다: ```rust,editable fn main() { @@ -59,7 +58,7 @@ fn main() { } ``` -In the same way, `if let` can be used to match any enum value: +같은 방법으로 `if let`을 다른 열거형에 활용할 수도 있습니다: ```rust,editable // Our example enum @@ -99,9 +98,12 @@ fn main() { } ``` -Another benefit is that `if let` allows us to match non-parameterized enum variants. This is true even in cases where the enum doesn't implement or derive `PartialEq`. In such cases `if Foo::Bar == a` would fail to compile, because instances of the enum cannot be equated, however `if let` will continue to work. +`if let`의 또다른 장점은 파라미터화되지 않은 열거형 멤버도 매칭할 수 있다는 것입니다. +그 열거형이 `PartialEq`를 구현하지도 포함하지도 않는 경우에도 가능하죠. +이런 경우 열거형 내부의 멤버에 접근할 수 없어 `if Foo::Bar == a`는 실패하지만, +`if let`은 여전히 동작한다는 것입니다. -Would you like a challenge? Fix the following example to use `if let`: +도전을 즐기시나요? 다음 예제를 `if let`을 이용해 고쳐봅시다: ```rust,editable,ignore,mdbook-runnable // This enum purposely neither implements nor derives PartialEq. @@ -119,7 +121,7 @@ fn main() { } ``` -### See also: +### 함께 읽기: [`enum`][enum], [`Option`][option], and the [RFC][if_let_rfc] diff --git a/src/flow_control/let_else.md b/src/flow_control/let_else.md index 2d07de7..38fc570 100644 --- a/src/flow_control/let_else.md +++ b/src/flow_control/let_else.md @@ -1,20 +1,24 @@ # let-else -> 🛈 stable since: rust 1.65 +> 🛈 안정화 버전: rust 1.65 > -> 🛈 you can target specific edition by compiling like this +> 🛈 다음과 같이 컴파일해 해당 에디션으로 컴파일할 수 있습니다: > `rustc --edition=2021 main.rs` -With `let`-`else`, a refutable pattern can match and bind variables -in the surrounding scope like a normal `let`, or else diverge (e.g. `break`, -`return`, `panic!`) when the pattern doesn't match. +`let`-`else`를 사용하면, 일반 `let`처럼 실패할 수 있는 패턴을 매칭하고 해당 스코프에 변수를 할당할 +수 있습니다. 패턴이 맞지 않는다면 `break`, `return`, `panic!`으로 발산하는 것도 가능합니다. ```rust use std::str::FromStr; fn get_count_item(s: &str) -> (u64, &str) { + /* + 역주: 이 코드에서 Rust 2021까지는 panic!은 (0u64, nullptr)를, Rust 2024부터는 !을 반환합니다. + 어느 버전이든, 이 함수는 유효한 러스트 코드입니다. + */ let mut it = s.split(' '); let (Some(count_str), Some(item)) = (it.next(), it.next()) else { + // 매칭 실패, panic! panic!("Can't segment count item pair: '{s}'"); }; let Ok(count) = u64::from_str(count_str) else { @@ -28,9 +32,8 @@ fn main() { } ``` -The scope of name bindings is the main thing that makes this different from -`match` or `if let`-`else` expressions. You could previously approximate these -patterns with an unfortunate bit of repetition and an outer `let`: +이름 바인딩의 스코프가 `match`와 `if let`-`else` 표현의 차이를 만듭니다. +처음을 `match`로 바인딩할 때는, 약간의 표현 반복과 외부 `let`을 이용해야 위 표현을 유사하게 구현할 수 있죠: ```rust # use std::str::FromStr; @@ -52,7 +55,7 @@ patterns with an unfortunate bit of repetition and an outer `let`: # assert_eq!(get_count_item("3 chairs"), (3, "chairs")); ``` -### See also: +### 함께 읽기: [option][option], [match][match], [if let][if_let] and the [let-else RFC][let_else_rfc]. diff --git a/src/flow_control/loop/return.md b/src/flow_control/loop/return.md index 8511b1e..2aab8cf 100644 --- a/src/flow_control/loop/return.md +++ b/src/flow_control/loop/return.md @@ -1,9 +1,7 @@ -# Returning from loops +# loop에서 리턴하기 -One of the uses of a `loop` is to retry an operation until it succeeds. If the -operation returns a value though, you might need to pass it to the rest of the -code: put it after the `break`, and it will be returned by the `loop` -expression. +`loop`의 용법 중 하나는 작업이 성공할 때까지 반복하는 겁니다. 하지만 작업이 값을 반환하면 나머지를 건너뛸 수도 있겠죠. +반환할 값을 `break` 뒤에 얹어주세요. 값이 `loop` 제어문에서 반환될 겁니다. ```rust,editable fn main() { diff --git a/src/flow_control/match.md b/src/flow_control/match.md index 02a8067..c2f8a1a 100644 --- a/src/flow_control/match.md +++ b/src/flow_control/match.md @@ -1,7 +1,6 @@ # match -Rust provides pattern matching via the `match` keyword, which can be used like -a C `switch`. +러스트는 패턴 매칭 과정에서 C의 `switch`와 비슷하게 활용할 수 있는 `match` 키워드를 제공합니다. ```rust,editable fn main() { diff --git a/src/flow_control/match/binding.md b/src/flow_control/match/binding.md index ccbb7c2..8445066 100644 --- a/src/flow_control/match/binding.md +++ b/src/flow_control/match/binding.md @@ -1,8 +1,7 @@ -# Binding +# 바인딩 -Indirectly accessing a variable makes it impossible to branch and use that -variable without re-binding. `match` provides the `@` sigil for binding values to -names: +변수를 간접적으로 접근하는 것은 그 변수를 다시 할당하지 않으면 분기를 하지 못하게 하는 주 원인입니다. +`match`는 `@` 한정자를 이용해 해당 값을 변수로 재할당합니다: ```rust,editable // A function `age` which returns a `u32`. @@ -26,7 +25,7 @@ fn main() { } ``` -You can also use binding to "destructure" `enum` variants, such as `Option`: +이 바인딩을 통해 `Option`과 같은 `enum`형을 해체하는 것도 가능합니다: ```rust,editable fn some_number() -> Option { @@ -46,7 +45,7 @@ fn main() { } ``` -### See also: +### 함께 읽기: [`functions`][functions], [`enums`][enums] and [`Option`][option] [functions]: ../../fn.md diff --git a/src/flow_control/match/destructuring/destructure_enum.md b/src/flow_control/match/destructuring/destructure_enum.md index 1b7d57c..add5fb1 100644 --- a/src/flow_control/match/destructuring/destructure_enum.md +++ b/src/flow_control/match/destructuring/destructure_enum.md @@ -45,9 +45,9 @@ fn main() { } ``` -### 참고 +### 함께 읽기: -[`#[allow(...)]`][allow], [색상 모델][color_models] and [`enum`][enum] +[`#[allow(...)]`][allow], [색상 모델][color_models]과 [`enum`][enum] [allow]: ../../../attribute/unused.md [color_models]: https://en.wikipedia.org/wiki/Color_model diff --git a/src/flow_control/match/destructuring/destructure_pointers.md b/src/flow_control/match/destructuring/destructure_pointers.md index d554598..9d241ea 100644 --- a/src/flow_control/match/destructuring/destructure_pointers.md +++ b/src/flow_control/match/destructuring/destructure_pointers.md @@ -59,6 +59,6 @@ fn main() { } ``` -### 참고 +### 함께 읽기: [The ref pattern](../../../scope/borrow/ref.md) diff --git a/src/flow_control/match/destructuring/destructure_structures.md b/src/flow_control/match/destructuring/destructure_structures.md index 35e5061..9ba7901 100644 --- a/src/flow_control/match/destructuring/destructure_structures.md +++ b/src/flow_control/match/destructuring/destructure_structures.md @@ -27,6 +27,6 @@ fn main() { } ``` -### See also: +### 함께 읽: [Structs](../../../custom_types/structs.md) diff --git a/src/flow_control/match/destructuring/destructure_tuple.md b/src/flow_control/match/destructuring/destructure_tuple.md index c027f7f..f41f8c3 100644 --- a/src/flow_control/match/destructuring/destructure_tuple.md +++ b/src/flow_control/match/destructuring/destructure_tuple.md @@ -20,6 +20,6 @@ fn main() { } ``` -### See also: +### 함께 읽기: [Tuples](../../../primitives/tuples.md) diff --git a/src/flow_control/match/guard.md b/src/flow_control/match/guard.md index df9fb2c..710eb7e 100644 --- a/src/flow_control/match/guard.md +++ b/src/flow_control/match/guard.md @@ -1,6 +1,6 @@ -# Guards +# 가드 -A `match` *guard* can be added to filter the arm. +`match` *가드*를 이용해 각 행을 필터링할 수 있습니다. ```rust,editable #[allow(dead_code)] @@ -24,8 +24,7 @@ fn main() { } ``` -Note that the compiler won't take guard conditions into account when checking -if all patterns are covered by the match expression. +컴파일러는 매칭 표현식에서 가능한 모든 경우가 처리 가능하지 않으면 컴파일을 거부한다는 걸 알아두세요. ```rust,editable,ignore,mdbook-runnable fn main() { @@ -40,7 +39,7 @@ fn main() { } ``` -### See also: +### 함께 읽기: [Tuples](../../primitives/tuples.md) [Enums](../../custom_types/enum.md) diff --git a/src/flow_control/while.md b/src/flow_control/while.md index 01a8dc9..e98cc68 100644 --- a/src/flow_control/while.md +++ b/src/flow_control/while.md @@ -1,8 +1,8 @@ # while -The `while` keyword can be used to run a loop while a condition is true. +`while` 키워드는 조건이 참인 동안 반복할 때 사용할 수 있습니다. -Let's write the infamous [FizzBuzz][fizzbuzz] using a `while` loop. +유명한 [FizzBuzz][fizzbuzz] 예제를 `while` 반복을 이용해 작성해봅시다. ```rust,editable fn main() { diff --git a/src/flow_control/while_let.md b/src/flow_control/while_let.md index 897375a..1458af3 100644 --- a/src/flow_control/while_let.md +++ b/src/flow_control/while_let.md @@ -1,7 +1,7 @@ # while let -Similar to `if let`, `while let` can make awkward `match` sequences -more tolerable. Consider the following sequence that increments `i`: +`if let`과 비슷하게, `while let`을 이용해 짜증나는 `match` 시퀸스를 좀 더 통제 가능하게 +바꿀 수 있습니다. `i`를 증가시키기 위한 다음 코드를 생각해 봅시다: ```rust // Make `optional` of type `Option` @@ -21,22 +21,22 @@ loop { } // ^ Requires 3 indentations! }, - // Quit the loop when the destructure fails: + // 해체가 불가하면 반복문 밖으로 나갑니다: _ => { break; } - // ^ Why should this be required? There must be a better way! + // ^ 이 줄을 굳이 써야 할까요? 더 나은 방법이 분명 있을 겁니다! } } ``` -Using `while let` makes this sequence much nicer: +`while let`을 이용하면 이 과정이 좀 더 봐줄 만해집니다. ```rust,editable fn main() { // Make `optional` of type `Option` let mut optional = Some(0); - // This reads: "while `let` destructures `optional` into - // `Some(i)`, evaluate the block (`{}`). Else `break`. + // 이 블록은 `let`이 `optional`을 `Some(i)`로 해체할 수 있으면 내부 블록을 + // 실행하고, 아니라면 `break`합니다 while let Some(i) = optional { if i > 9 { println!("Greater than 9, quit!"); @@ -48,14 +48,14 @@ fn main() { // ^ Less rightward drift and doesn't require // explicitly handling the failing case. } - // ^ `if let` had additional optional `else`/`else if` - // clauses. `while let` does not have these. + // ^ `if let`은 `else`나 `else if`를 붙일 수 있었죠. + // 반면 `while let`은 그렇지 않습니다. } ``` -### See also: +### 함께 읽기: -[`enum`][enum], [`Option`][option], and the [RFC][while_let_rfc] +[`enum`][enum], [`Option`][option]와 [RFC][while_let_rfc] [enum]: ../custom_types/enum.md [option]: ../std/option.md From 165b562cffde134d90ef7f3ba63152cc41ab2a21 Mon Sep 17 00:00:00 2001 From: Hoto Ras Date: Sat, 7 Jun 2025 13:53:20 +0900 Subject: [PATCH 11/11] =?UTF-8?q?=EC=9E=84=EC=9D=98=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=20=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit request by: https://github.com/rust-kr/rust-by-example-ko/issues/7#issuecomment-2951701487 - 위 issuecomment에서 제시한, 원문에서 추가한 내용 중 사실과 다른 부분을 전부 삭제하였습니다. * never type (`!`)의 안정화 관련: 현재 `!`를 타입으로 지정할 수 있는 Rust 2024가 릴리스된 것으로 해당 에디션이 절대다수가 사용하는 에디션이 된 이후에 `!`의 안정화가 진행된다는 Tracking Issue comment에 의거하여 해당 구문은 전부 삭제합니다. - issuecomment: github.com/rust-lang/rust/issues/35121#issuecomment-2241009577 * FFI에 unsafe in unsafe에 대해서도 추가한 내용이 있었는데 들어냈습니다. 역시 기술 서적은 아무나 쓰는 거 아닌가 봅니다. 이해가 딸리는데 막 추가했다가 결국엔 전부 삭제하네요... --- src/flow_control/if_else.md | 2 +- src/flow_control/let_else.md | 4 ---- src/fn/diverging.md | 6 ------ src/std_misc/ffi.md | 5 ----- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/flow_control/if_else.md b/src/flow_control/if_else.md index ea8519c..9604c09 100644 --- a/src/flow_control/if_else.md +++ b/src/flow_control/if_else.md @@ -14,7 +14,7 @@ fn main() { print!("{}은/는 양수입니다", n); } else { print!("{}는 0입니다", n); - } // 이 블록의 반환값은 Rust 2024부터 `!`이며, 이전 버전에서는 `()`입니다. + } let big_n = if n < 10 && n > -10 { diff --git a/src/flow_control/let_else.md b/src/flow_control/let_else.md index 38fc570..15f27be 100644 --- a/src/flow_control/let_else.md +++ b/src/flow_control/let_else.md @@ -12,10 +12,6 @@ use std::str::FromStr; fn get_count_item(s: &str) -> (u64, &str) { - /* - 역주: 이 코드에서 Rust 2021까지는 panic!은 (0u64, nullptr)를, Rust 2024부터는 !을 반환합니다. - 어느 버전이든, 이 함수는 유효한 러스트 코드입니다. - */ let mut it = s.split(' '); let (Some(count_str), Some(item)) = (it.next(), it.next()) else { // 매칭 실패, panic! diff --git a/src/fn/diverging.md b/src/fn/diverging.md index 9b7109c..6d8a1fe 100644 --- a/src/fn/diverging.md +++ b/src/fn/diverging.md @@ -1,11 +1,5 @@ # Diverging functions -> 🛈 안정화된 버전: 러스트 1.85 (2024 에디션) -> -> 🛈 다음과 같이 컴파일해 타겟 에디션을 지정할 수 있습니다: -> `rustc --edition=2024 main.rs` -> 혹은 `Cargo.toml`에 `edition = 2024`를 지정하세요. - Diverging functions never return. They are marked using `!`, which is an empty type. ```rust diff --git a/src/std_misc/ffi.md b/src/std_misc/ffi.md index cee6b1a..7ce3a25 100644 --- a/src/std_misc/ffi.md +++ b/src/std_misc/ffi.md @@ -4,11 +4,6 @@ Rust provides a Foreign Function Interface (FFI) to C libraries. Foreign functions must be declared inside an `extern` block annotated with a `#[link]` attribute containing the name of the foreign library. -> 🛈 Foreign functions are treated as [`unsafe`][unsafe]. You should call those in `unsafe` block, -> but you can still wrap with safe (standard rust) functions. -> -> `unsafe fn`s are able to use these without another `unsafe` wrapping. - ```rust,ignore use std::fmt;