├── .gitattributes ├── .gitignore ├── .travis.yml ├── .yaspellerrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── book.toml ├── deploy.sh └── src ├── SUMMARY.md ├── attribute.md ├── attribute ├── cfg.md ├── cfg │ └── custom.md ├── crate.md └── unused.md ├── cargo.md ├── cargo ├── build_scripts.md ├── conventions.md ├── deps.md └── test.md ├── compatibility.md ├── compatibility └── raw_identifiers.md ├── conversion.md ├── conversion ├── from_into.md └── string.md ├── crates.md ├── crates ├── lib.md └── link.md ├── custom_types.md ├── custom_types ├── constants.md ├── enum.md ├── enum │ ├── c_like.md │ ├── enum_use.md │ └── testcase_linked_list.md └── structs.md ├── error.md ├── error ├── iter_result.md ├── multiple_error_types.md ├── multiple_error_types │ ├── boxing_errors.md │ ├── define_error_type.md │ ├── option_result.md │ ├── reenter_question_mark.md │ └── wrap_error.md ├── option_unwrap.md ├── option_unwrap │ ├── and_then.md │ └── map.md ├── panic.md ├── result.md └── result │ ├── early_returns.md │ ├── enter_question_mark.md │ ├── result_alias.md │ └── result_map.md ├── expression.md ├── flow_control.md ├── flow_control ├── for.md ├── if_else.md ├── if_let.md ├── loop.md ├── loop │ ├── nested.md │ └── return.md ├── match.md ├── match │ ├── binding.md │ ├── destructuring.md │ ├── destructuring │ │ ├── destructure_enum.md │ │ ├── destructure_pointers.md │ │ ├── destructure_structures.md │ │ └── destructure_tuple.md │ └── guard.md ├── while.md └── while_let.md ├── fn.md ├── fn ├── closures.md ├── closures │ ├── anonymity.md │ ├── capture.md │ ├── closure_examples.md │ ├── closure_examples │ │ ├── iter_any.md │ │ └── iter_find.md │ ├── input_functions.md │ ├── input_parameters.md │ └── output_parameters.md ├── diverging.md ├── hof.md └── methods.md ├── generics.md ├── generics ├── assoc_items.md ├── assoc_items │ ├── the_problem.md │ └── types.md ├── bounds.md ├── bounds │ └── testcase_empty.md ├── gen_fn.md ├── gen_trait.md ├── impl.md ├── multi_bounds.md ├── new_types.md ├── phantom.md ├── phantom │ └── testcase_units.md └── where.md ├── hello.md ├── hello ├── comment.md ├── print.md └── print │ ├── fmt.md │ ├── print_debug.md │ ├── print_display.md │ └── print_display │ └── testcase_list.md ├── index.md ├── macros.md ├── macros ├── designators.md ├── dry.md ├── dsl.md ├── overload.md ├── repeat.md ├── syntax.md └── variadics.md ├── meta.md ├── meta └── doc.md ├── mod.md ├── mod ├── split.md ├── struct_visibility.md ├── super.md ├── use.md └── visibility.md ├── primitives.md ├── primitives ├── array.md ├── literals.md └── tuples.md ├── scope.md ├── scope ├── borrow.md ├── borrow │ ├── alias.md │ ├── freeze.md │ ├── mut.md │ └── ref.md ├── lifetime.md ├── lifetime │ ├── elision.md │ ├── explicit.md │ ├── fn.md │ ├── lifetime_bounds.md │ ├── lifetime_coercion.md │ ├── methods.md │ ├── static_lifetime.md │ ├── struct.md │ └── trait.md ├── move.md ├── move │ └── mut.md └── raii.md ├── std.md ├── std ├── box.md ├── hash.md ├── hash │ ├── alt_key_types.md │ └── hashset.md ├── option.md ├── panic.md ├── rc.md ├── result.md ├── result │ └── question_mark.md ├── str.md └── vec.md ├── std_misc.md ├── std_misc ├── arg.md ├── arg │ └── matching.md ├── channels.md ├── ffi.md ├── file.md ├── file │ ├── create.md │ ├── open.md │ └── read_lines.md ├── fs.md ├── path.md ├── process.md ├── process │ ├── pipe.md │ └── wait.md ├── threads.md └── threads │ └── testcase_mapreduce.md ├── testing.md ├── testing ├── dev_dependencies.md ├── doc_testing.md ├── integration_testing.md └── unit_testing.md ├── trait.md ├── trait ├── clone.md ├── derive.md ├── drop.md ├── iter.md └── ops.md ├── types.md ├── types ├── alias.md ├── cast.md ├── inference.md └── literals.md ├── unsafe.md ├── variable_bindings.md └── variable_bindings ├── declare.md ├── mut.md └── scope.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: rust 3 | cache: cargo 4 | rust: 5 | - nightly 6 | branches: 7 | only: 8 | - master 9 | before_script: 10 | - npm install yaspeller -g 11 | - cargo install mdbook --force || true 12 | script: 13 | - yaspeller --only-errors src/**/*.md 14 | - mdbook build 15 | - mdbook test 16 | after_success: 17 | - test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && bash deploy.sh 18 | env: 19 | global: 20 | - secure: hueiOpMNOpjn4QKkR4LQ48FCkdfl6YEVS8Y5uHZE+UcQnp2cECXQmHPdOKdWpQJunjHGlmI9h1q7m/aNfEt4t5gemRQWxalhTjnraA39uANtcWnBVH7lF7viJcMUXn5CgPNHE+0ybDISaSw2/dDdh/FcyHxdIncUSoXsZ8O7mCcwzekEQa8IBiR9siUzr+psYE7GI1E0Bfi1vMo3Rgnn/wwYYokbEJc6i1S5EZOpy36v9i5ou3YTgnmZITKQN9iH1osqxidyjWMRqHesSjEHNXQla8K4+nlh7Nhe/dPklnQFJCynmt3D3GuRVJYZzTxYuHvs3c0yfs2I+grXnTOYckCZjj0x9Q11nYtQhG9rWDfOZkD/dcfWSFjotu9X0ezoslTFGpzDDCI+0MdOLSshDdr7bXXze4v2WfWXVY+wpFcEP/1LhwSsPIHvfo6zlI5h4q3HkKUWaHmoae3tS5H9SrwNj/ErZQBqWUs72aUaCf0bNK996y04LV3xliOLmJxgfxTb0RIKPeClYMEtQOTwdnMPiJJYUofu9NrwDO48kDiNWA0dobhV6nqH6c72tSCN7vPuu+EyL5biM1c/0vpC3rQHjWQnXjFS3hrYpaeIBRhVbWXodRnjpzQ5oUSehykJbGoPgw02DUCm6OcOvCzIpBLXOq6++liDkdRLZiwJgpQ= 21 | -------------------------------------------------------------------------------- /.yaspellerrc: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "ru", 3 | "dictionary": [ 4 | "аллоцирован(ы|ый|о|ное)", 5 | "аллокаци(я|и|ю|ей)", 6 | "аллоцируются", 7 | "беззнаков(ы(й|м)|ое)", 8 | "кодогенераци(я|и|ю|ей)", 9 | "суффиксная", 10 | "метапрограммирование", 11 | "раскомментировать", 12 | "Слэйтер", 13 | "багов", 14 | "Ок", 15 | "деструктуризирует", 16 | "платформозависимы(й|е)", 17 | "некопируемы(е|й)", 18 | "дебаг", 19 | "версионирования" 20 | ], 21 | "report": [ 22 | "console" 23 | ], 24 | "checkYo": true, 25 | "ignoreUppercase": true 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust By Example" 3 | description = "A description" 4 | author = "The Rust Community" 5 | 6 | [output.html] 7 | google-analytics="UA-142155799-3" 8 | 9 | [output.html.playpen] 10 | editable = true 11 | editor = "ace" 12 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT 2 | # file at the top-level directory of this distribution and at 3 | # http://rust-lang.org/COPYRIGHT. 4 | # 5 | # Licensed under the Apache License, Version 2.0 or the MIT license 7 | # , at your 8 | # option. This file may not be copied, modified, or distributed 9 | # except according to those terms. 10 | 11 | #!/bin/bash 12 | 13 | rev=$(git rev-parse --short HEAD) 14 | 15 | cd book 16 | 17 | git init 18 | git config user.name "Igor.Shaposhnik" 19 | git config user.email "shaposhnikigor95@bk.ru" 20 | git remote add upstream "https://$GH_TOKEN@github.com/ruRust/rust-by-example-ru.git" 21 | git fetch upstream && git reset upstream/gh-pages 22 | 23 | #echo "rustbyexample.com" > CNAME 24 | 25 | touch . 26 | 27 | git add -A . 28 | 29 | git commit -m "rebuild pages at ${rev}" 30 | git push -q upstream HEAD:gh-pages 31 | -------------------------------------------------------------------------------- /src/attribute.md: -------------------------------------------------------------------------------- 1 | # Атрибуты 2 | 3 | Атрибуты - это метаданные, применяемые к какому-либо модулю, контейнеру или их элементу. 4 | Благодаря атрибутам можно: 5 | 6 | 7 | 8 | - [задать условия компиляции кода](attribute/cfg.md) 9 | - [задать имя, версию и тип (библиотека или исполняемый файл) контейнера](attribute/crate.md) 10 | - отключить [lints](https://en.wikipedia.org/wiki/Lint_%28software%29) (предупреждения) 11 | - включить возможности компилятора (макросы, глобальный импорт и другое.) 12 | - связаться с внешней библиотекой 13 | - пометить функции как юнит тесты 14 | - пометить функции, которые будут частью бенчмарка 15 | 16 | Когда атрибуты применяются ко всему контейнеру, их синтаксис будет `#![crate_attribute]`, 17 | а когда они применяются к модулю или элементу модуля, 18 | их синтаксис станет `#[item_attribute]` (обратите внимание на отсутствие `!`). 19 | 20 | Атрибуты могут принимать аргументы с различным синтаксисом: 21 | 22 | - `#[attribute = "value"]` 23 | - `#[attribute(key = "value")]` 24 | - `#[attribute(value)]` 25 | 26 | Атрибуты могут иметь несколько значений и могут быть разделены несколькими строками: 27 | 28 | ```rust,ignore 29 | #[attribute(value, value2)] 30 | 31 | 32 | #[attribute(value, value2, value3, 33 | value4, value5)] 34 | ``` 35 | -------------------------------------------------------------------------------- /src/attribute/cfg.md: -------------------------------------------------------------------------------- 1 | # `cfg` 2 | 3 | Условная компиляция возможна благодаря двум операторам: 4 | 5 | - Атрибуту `cfg`: `#[cfg(...)]`, который указывается на месте атрибута 6 | - Макросу `cfg!`: `cfg!(...)`, который можно использовать в условных выражениях 7 | 8 | Оба имеют идентичный синтаксис для принятия аргументов. 9 | 10 | ```rust,editable 11 | // Эта функция будет скомпилирована только в том случае, если целевая ОС будет linux 12 | #[cfg(target_os = "linux")] 13 | fn are_you_on_linux() { 14 | println!("Вы работаете в linux!"); 15 | } 16 | 17 | // А эта функция будет скомпилирована, если целевая ОС *не* linux 18 | #[cfg(not(target_os = "linux"))] 19 | fn are_you_on_linux() { 20 | println!("Вы работаете *не* в linux!"); 21 | } 22 | 23 | fn main() { 24 | are_you_on_linux(); 25 | 26 | println!("Вы уверены?"); 27 | if cfg!(target_os = "linux") { 28 | println!("Да. Это точно linux!"); 29 | } else { 30 | println!("Да. Это точно *не* linux!"); 31 | } 32 | } 33 | ``` 34 | 35 | ### Смотрите также: 36 | 37 | [the reference](https://doc.rust-lang.org/reference/attributes.html#conditional-compilation), [`cfg!`](https://doc.rust-lang.org/std/macro.cfg!.html), и [macros](macros.html). 38 | -------------------------------------------------------------------------------- /src/attribute/cfg/custom.md: -------------------------------------------------------------------------------- 1 | # Собственные условия 2 | 3 | Некоторые условия, например, `target_os` предоставляются компилятором. 4 | Если мы хотим создать собственные условия, 5 | то их необходимо передать компилятору используя флаг `--cfg`. 6 | 7 | ```rust,editable,ignore,mdbook-runnable 8 | #[cfg(some_condition)] 9 | fn conditional_function() { 10 | println!("condition met!"); 11 | } 12 | 13 | fn main() { 14 | conditional_function(); 15 | } 16 | ``` 17 | 18 | Попробуйте запустить без указания флага `cfg`. 19 | 20 | С указанием флага `cfg`: 21 | 22 | ```bash 23 | $ rustc --cfg some_condition custom.rs && ./custom 24 | condition met! 25 | ``` 26 | -------------------------------------------------------------------------------- /src/attribute/crate.md: -------------------------------------------------------------------------------- 1 | # Контейнеры 2 | 3 | Атрибут `crate_type` используется, чтобы сказать компилятору, 4 | какой контейнер является библиотекой (и каким типом библиотеки), 5 | а какой исполняемым файлом. Атрибут `crate_name` используется для указания имени контейнера. 6 | 7 | Однако важно отметить, что атрибуты `crate_type` и `create_name` **не имеют значения** при использовании пакетного менеджера Cargo. 8 | В виду того, что Cargo используется для большинства проектов на Rust, 9 | это значит в реальном мире использование `crate_type` и `crate_name` 10 | достаточно ограничено. 11 | 12 | ```rust,editable 13 | // Этот контейнер - библиотека 14 | #![crate_type = "lib"] 15 | // Эта библиотека называется "rary" 16 | #![crate_name = "rary"] 17 | 18 | pub fn public_function() { 19 | println!("вызвана `public_function()` библиотеки `rary`"); 20 | } 21 | 22 | fn private_function() { 23 | println!("вызвана `private_function()` библиотеки `rary`"); 24 | } 25 | 26 | pub fn indirect_access() { 27 | print!("вызвана `indirect_access()` библиотеки `rary`, и в ней\n> "); 28 | 29 | private_function(); 30 | } 31 | ``` 32 | 33 | Если мы используем атрибут `crate_type`, 34 | то нам больше нет необходимости передавать флаг `--crate-type` компилятору. 35 | 36 | ```bash 37 | $ rustc lib.rs 38 | $ ls lib* 39 | library.rlib 40 | ``` 41 | -------------------------------------------------------------------------------- /src/attribute/unused.md: -------------------------------------------------------------------------------- 1 | # `dead_code` 2 | 3 | Компилятор предоставляет [*проверку*](https://en.wikipedia.org/wiki/Lint_%28software%29) `dead_code`, 4 | которая предупреждает о неиспользованных функциях. Атрибут *dead_code* можно использовать, чтобы отключить данную проверку. 5 | 6 | ```rust,editable 7 | fn used_function() {} 8 | 9 | // `#[allow(dead_code)]` - атрибут, который убирает проверку на неиспользуемый код 10 | #[allow(dead_code)] 11 | fn unused_function() {} 12 | 13 | fn noisy_unused_function() {} 14 | // ИСПРАВЬТЕ ^ Добавьте атрибут `dead_code`, чтобы убрать предупреждение 15 | 16 | fn main() { 17 | used_function(); 18 | } 19 | ``` 20 | 21 | Обратите внимание, что в реальных программах, вы должны удалить неиспользуемый код. 22 | В этих примерах мы разрешаем оставить неиспользуемый код в некоторых местах, 23 | но, это только для примера! 24 | -------------------------------------------------------------------------------- /src/cargo.md: -------------------------------------------------------------------------------- 1 | # Cargo 2 | 3 | `cargo` - официальный менеджер пакетов языка Rust. В нем много функций 4 | для улучшения качества кода и увеличения скорости разработки! К ним относятся: 5 | 6 | - Управление зависимостями и интеграция с [crates.io](https://crates.io) (официальный реестр пакетов Rust) 7 | - Осведомлённость о модульных тестах 8 | - Осведомлённость о тестах производительности 9 | 10 | Эта глава рассказывает об основах, но вы можете найти полное описание 11 | по адресу [The Cargo Book](https://doc.rust-lang.org/cargo/). 12 | -------------------------------------------------------------------------------- /src/cargo/build_scripts.md: -------------------------------------------------------------------------------- 1 | # Скрипты сборки 2 | 3 | Иногда обычной сборки, предоставляемой cargo, недостаточно. Возможно вашему крейту нужны некоторые предварительные условия, прежде чем он успешно скомпилируется, например кодогенерация или предварительно должен скомпилироваться какой-то нативный код. Для решения этой проблемы, мы имеем скрипты сборки, которые cargo может запустить. 4 | 5 | Для добавления скрипта сборки в ваш пакет, вы можете указать его в `Cargo.toml` следующим образом: 6 | 7 | ```toml 8 | [package] 9 | ... 10 | build = "build.rs" 11 | ``` 12 | 13 | Иначе по умолчанию cargo будет искать файл `build.rs` в директории проекта. 14 | 15 | ## Как использовать скрипт сборки 16 | 17 | Скрипт сборки - это просто другой файл на Rust, который будет скомпилирован и вызван до компиляции чего-либо другого в пакете. Следовательно он может быть использовать для выполнения предварительных условий вашего крейта. 18 | 19 | Через переменные окружения cargo предоставляет скрипту входные параметры [описанные здесь](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts), которые могут быть использованы. 20 | 21 | Скрипт возвращает значения через stdout. Все напечатанные строки записываются в 22 | `target/debug/build//output`. Кроме того, строки с префиксом `cargo:` напрямую интерпретируются cargo и следовательно могут быть использованы для объявления параметров для компиляции пакета. 23 | 24 | Больше информации и примеров можно найти в [спецификации cargo](https://doc.rust-lang.org/cargo/reference/build-scripts.html). 25 | -------------------------------------------------------------------------------- /src/cargo/conventions.md: -------------------------------------------------------------------------------- 1 | # Соглашения 2 | 3 | В предыдущей главе мы видели следующую иерархию каталогов: 4 | 5 | ```txt 6 | foo 7 | ├── Cargo.toml 8 | └── src 9 | └── main.rs 10 | ``` 11 | 12 | Предположим, что мы хотим иметь два двоичных файла в одном проекте. Что 13 | тогда? 14 | 15 | Оказывается, `cargo` это поддерживает. Двоичный файл по умолчанию называется `main.rs`, 16 | это мы видели раньше, но вы можете добавить дополнительные файлы, поместив 17 | их в каталог `bin/`: 18 | 19 | ```txt 20 | foo 21 | ├── Cargo.toml 22 | └── src 23 | ├── main.rs 24 | └── bin 25 | └── my_other_bin.rs 26 | ``` 27 | 28 | Чтобы сказать `cargo` скомпилировать или запустить этот двоичный файл, 29 | мы просто передаём `cargo` флаг `--bin my_other_bin`, где `my_other_bin` 30 | это имя двоичного файла, с которым мы хотим работать. 31 | 32 | Помимо дополнительных двоичных файлов, в `cargo` есть 33 | [встроенная поддержка](https://doc.rust-lang.org/cargo/guide/project-layout.html) примеров, модульных тестов, 34 | интеграционных тестов и тестов на производительность. 35 | 36 | В следующей главе мы более подробно рассмотрим тесты. 37 | -------------------------------------------------------------------------------- /src/cargo/test.md: -------------------------------------------------------------------------------- 1 | # Тестирование 2 | 3 | Как мы знаем, тестирование является неотъемлемой частью любого программного обеспечения! Rust имеет первоклассную поддержку модульного и интеграционного тестирования (см. 4 | [главу о тестировании в TRPL](https://doc.rust-lang.org/book/ch11-00-testing.html)). 5 | 6 | Из разделов тестирования, приведённых выше, мы знаем, как писать модульные и интеграционные тесты. Организационно, мы можем расположить модульные тесты в модулях, которые они тестируют, а интеграционные - в собственном каталоге `tests/`: 7 | 8 | ```txt 9 | foo 10 | ├── Cargo.toml 11 | ├── src 12 | │ └── main.rs 13 | └── tests 14 | ├── my_test.rs 15 | └── my_other_test.rs 16 | ``` 17 | 18 | Каждый файл в каталоге `tests` - это отдельный интеграционный тест. 19 | 20 | `cargo` естественно, обеспечивает простой способ запуска всех ваших тестов! 21 | 22 | ```shell 23 | $ cargo test 24 | ``` 25 | 26 | Вы должны увидеть примерно такой результат: 27 | 28 | ```shell 29 | $ cargo test 30 | Compiling blah v0.1.0 (file:///nobackup/blah) 31 | Finished dev [unoptimized + debuginfo] target(s) in 0.89 secs 32 | Running target/debug/deps/blah-d3b32b97275ec472 33 | 34 | running 3 tests 35 | test test_bar ... ok 36 | test test_baz ... ok 37 | test test_foo_bar ... ok 38 | test test_foo ... ok 39 | 40 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 41 | ``` 42 | 43 | Вы также можете запустить тесты, чьё имя соответствует шаблону: 44 | 45 | ```shell 46 | $ cargo test test_foo 47 | ``` 48 | 49 | ```shell 50 | $ cargo test test_foo 51 | Compiling blah v0.1.0 (file:///nobackup/blah) 52 | Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs 53 | Running target/debug/deps/blah-d3b32b97275ec472 54 | 55 | running 2 tests 56 | test test_foo ... ok 57 | test test_foo_bar ... ok 58 | 59 | test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out 60 | ``` 61 | 62 | Одно слово предостережения: Cargo может выполнять несколько тестов одновременно, поэтому убедитесь, что они не участвуют в гонках друг с другом. Например, если они все выводят в файл, вы должны заставить их записывать в разные файлы. 63 | -------------------------------------------------------------------------------- /src/compatibility.md: -------------------------------------------------------------------------------- 1 | # Совместимость 2 | 3 | Rust быстро развивается и из-за этого могут возникнуть определённые проблемы совместимости, не смотря на усилия по обеспечению обратной совместимости везде, где это возможно. 4 | 5 | - [Сырые идентификаторы](compatibility/raw_identifiers.md) 6 | -------------------------------------------------------------------------------- /src/compatibility/raw_identifiers.md: -------------------------------------------------------------------------------- 1 | # Сырые идентификаторы 2 | 3 | В Rust, как и во многих других языках программирования, существует концепция "ключевых слов". 4 | Эти идентификаторы что-то значат для языка и из-за этого вы не можете использовать их в качестве названия переменных, именах функций и других местах. 5 | Сырые идентификаторы позволяют использовать ключевые слова там, где они обычно не разрешены. 6 | Это особенно полезно, когда Rust вводит новые ключевые слова и библиотеки, использующие старую редакцию Rust, имеют переменные или функции с таким же именем, как и ключевое слово, введённое в новой редакции. 7 | 8 | Например, рассмотрим крейт `foo`, скомпилированный с 2015 редакцией Rust, и который экспортирует функцию с именем `try`. Это ключевое слово зарезервировано для новой функциональности в 2018 редакции, из-за чего без сырых идентификаторов мы не можем назвать так функцию. 9 | 10 | ```rust,ignore 11 | extern crate foo; 12 | 13 | fn main() { 14 | foo::try(); 15 | } 16 | ``` 17 | 18 | Вы получите ошибку: 19 | 20 | ```text 21 | error: expected identifier, found keyword `try` 22 | --> src/main.rs:4:4 23 | | 24 | 4 | foo::try(); 25 | | ^^^ expected identifier, found keyword 26 | ``` 27 | 28 | Вы можете записать это при помощи сырого идентификатора: 29 | 30 | ```rust,ignore 31 | extern crate foo; 32 | 33 | fn main() { 34 | foo::r#try(); 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /src/conversion.md: -------------------------------------------------------------------------------- 1 | # Приведение типов 2 | 3 | Приведение типов в Rust осуществляется с помощью [типажей]. 4 | В общем, при приведении к типу используются [`From`] и [`Into`], 5 | но есть и более специфические типажи для часто используемых 6 | случаев, например, для конвертации в `String` и обратно. 7 | 8 | [типажей]: trait.html 9 | [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html 10 | [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html 11 | -------------------------------------------------------------------------------- /src/conversion/from_into.md: -------------------------------------------------------------------------------- 1 | # `From` и `Into` 2 | 3 | Типажи [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) и [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) связаны по своей сути, и это стало частью их реализации. Если вы можете конвертировать тип `А` в тип `В`, то будет легко предположить, что мы должны быть в состоянии конвертировать тип `В` в тип `А`. 4 | 5 | ## `From` 6 | 7 | Типаж [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) позволяет типу определить, как он будет создаваться из другого типа, что предоставляет очень простой механизм конвертации между несколькими типами. Есть несколько реализаций этот типажа в стандартной библиотеке для преобразования примитивов и общих типов. 8 | 9 | Для примера, мы можем легко конвертировать `str` в `String` 10 | 11 | ```rust 12 | let my_str = "привет"; 13 | let my_string = String::from(my_str); 14 | ``` 15 | 16 | Мы можем сделать нечто похожее для определения конвертации для нашего собственного типа. 17 | 18 | ```rust,editable 19 | use std::convert::From; 20 | 21 | #[derive(Debug)] 22 | struct Number { 23 | value: i32, 24 | } 25 | 26 | impl From for Number { 27 | fn from(item: i32) -> Self { 28 | Number { value: item } 29 | } 30 | } 31 | 32 | fn main() { 33 | let num = Number::from(30); 34 | println!("Мой номер {:?}", num); 35 | } 36 | ``` 37 | 38 | ## `Into` 39 | 40 | Типаж [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) является полной противоположностью типажа `From`. Так что если вы реализовали для вашего типа типаж `From`, реализацию типажа `Into` вы получите бесплатно. 41 | 42 | Использование типажа `Into` обычно требует спецификации типа, в который мы собираемся конвертировать, так как компилятор чаще всего не может это вывести. 43 | Однако это небольшой компромисс, учитывая, что данную функциональность мы получаем бесплатно. 44 | 45 | ```rust,editable 46 | use std::convert::From; 47 | 48 | #[derive(Debug)] 49 | struct Number { 50 | value: i32, 51 | } 52 | 53 | impl From for Number { 54 | fn from(item: i32) -> Self { 55 | Number { value: item } 56 | } 57 | } 58 | 59 | fn main() { 60 | let int = 5; 61 | // Попробуйте убрать аннотацию типа 62 | let num: Number = int.into(); 63 | println!("Мой номер {:?}", num); 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /src/conversion/string.md: -------------------------------------------------------------------------------- 1 | # `FromStr` и `ToString` 2 | 3 | ## Конвертация в строку 4 | 5 | Преобразовать любой тип в `String` так же просто, как и реализовать для него типаж [`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html). Вместо того, чтобы делать это напрямую, вы должны реализовать типаж [`fmt::Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html), который автоматически предоставляет реализацию [`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html), а 6 | также позволяет распечатать тип, как обсуждалось в секции [`print!`](../hello/print.md). 7 | 8 | ```rust,editable 9 | use std::fmt; 10 | 11 | struct Circle { 12 | radius: i32 13 | } 14 | 15 | impl fmt::Display for Circle { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | write!(f, "Круг радиусом {}", self.radius) 18 | } 19 | } 20 | 21 | fn main() { 22 | let circle = Circle { radius: 6 }; 23 | println!("{}", circle.to_string()); 24 | } 25 | ``` 26 | 27 | ## Парсинг строки 28 | 29 | Один из наиболее общим типов конвертации - это преобразование строки в число. Идиоматический подход это сделать при помощи функции [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) и указания типа, в который будем преобразовывать, что можно сделать либо через выведение типа, либо при помощи 'turbofish'-синтаксиса. 30 | 31 | Это преобразует строку в указанный тип при условии, что для этого типа реализован типаж [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html). 32 | Он реализован для множества типов стандартной библиотеки. 33 | Чтобы получить эту функциональность для пользовательского типа, надо просто реализовать для этого типа типаж [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html). 34 | 35 | ```rust 36 | fn main() { 37 | let parsed: i32 = "5".parse().unwrap(); 38 | let turbo_parsed = "10".parse::().unwrap(); 39 | 40 | let sum = parsed + turbo_parsed; 41 | println!("Сумма: {:?}", sum); 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /src/crates.md: -------------------------------------------------------------------------------- 1 | # Контейнеры 2 | 3 | Контейнер (`crate`) — единица компиляции в языке Rust. 4 | Когда вызывается `rustc some_file.rs`, `some_file.rs` обрабатывается как *файл контейнера*. 5 | Если в `some_file.rs` есть декларация `mod`, то содержимое модуля 6 | будет объединено с файлом контейнера *перед* его компиляцией. 7 | Другими словами, модули *не* собираются отдельно, собираются лишь контейнеры. 8 | 9 | Контейнер может быть скомпилирован в исполняемый файл или в библиотеку. 10 | По умолчанию, `rustc` создаёт из контейнера исполняемый файл. 11 | Это поведение может быть изменено добавлением флага `--crate-type` к `rustc`. 12 | -------------------------------------------------------------------------------- /src/crates/lib.md: -------------------------------------------------------------------------------- 1 | # Библиотеки 2 | 3 | Давайте создадим библиотеку и посмотрим, как связать её с другим контейнером. 4 | 5 | ```rust,ignore 6 | pub fn public_function() { 7 | println!("вызвана `public_function()` библиотеки rary"); 8 | } 9 | 10 | fn private_function() { 11 | println!("вызвана `private_function()` библиотеки rary"); 12 | } 13 | 14 | pub fn indirect_access() { 15 | print!("вызвана `indirect_access()` библиотеки rary, и в ней\n> "); 16 | 17 | private_function(); 18 | } 19 | ``` 20 | 21 | ```bash 22 | $ rustc --crate-type=lib rary.rs 23 | $ ls lib* 24 | library.rlib 25 | ``` 26 | 27 | Библиотеки получают префикс «lib», и по умолчанию имеют то же имя, 28 | что и их контейнеры, но это имя можно изменить 29 | с помощью [атрибута `crate_name`](attribute/crate.html). 30 | -------------------------------------------------------------------------------- /src/crates/link.md: -------------------------------------------------------------------------------- 1 | # `extern crate` 2 | 3 | Чтобы связать контейнер с новой библиотекой, нужна декларация `extern crate`. 4 | Она не только свяжет библиотеку, но и импортирует все элементы в модуль 5 | с тем же именем, что и сама библиотека. 6 | Правила видимости, применимые к модулям, так же применимы и к библиотекам. 7 | 8 | ```rust,ignore 9 | // Ссылка на `library`. Импортируем элементы, как модуль `rary` 10 | extern crate rary; 11 | 12 | fn main() { 13 | rary::public_function(); 14 | 15 | // Ошибка! Функция `private_function` приватная 16 | //rary::private_function(); 17 | 18 | rary::indirect_access(); 19 | } 20 | ``` 21 | 22 | ```bash 23 | # Где library.rlib путь к скомпилированной библиотеке. Предположим, что 24 | # она находится в той же директории: 25 | $ rustc executable.rs --extern rary=library.rlib && ./executable 26 | вызвана `public_function()` библиотеки rary 27 | вызвана `indirect_access()` библиотеки rary, и в ней 28 | > вызвана `private_function()` библиотеки rary 29 | ``` 30 | -------------------------------------------------------------------------------- /src/custom_types.md: -------------------------------------------------------------------------------- 1 | # Пользовательские типы 2 | 3 | В языке программирования Rust пользовательские типы данных 4 | в основном создаются при помощи двух ключевых слов: 5 | 6 | * `struct`: определение структуры 7 | * `enum`: определение перечисления 8 | 9 | Константы так же могут быть созданы с помощью ключевых слов `const` и `static`. -------------------------------------------------------------------------------- /src/custom_types/constants.md: -------------------------------------------------------------------------------- 1 | # Константы 2 | 3 | В Rust есть два типа констант, которые могут быть объявлены 4 | в любой области видимости, включая глобальную. Оба требуют явной аннотации типа: 5 | 6 | - `const`: Неизменяемая переменная (в общем случае). 7 | - `static`: Возможно, `изменяемая` переменная с временем жизни [`'static`](../scope/lifetime/static_lifetime.md). Статическое время жизни выводится и не должно быть указано. Доступ или модификация изменяемой статической переменной [небезопасно (см. `unsafe`)](../unsafe.md). 8 | 9 | ```rust,editable,ignore,mdbook-runnable 10 | // Константы объявлены в глобальной области видимости. 11 | static LANGUAGE: &str = "Rust"; 12 | const THRESHOLD: i32 = 10; 13 | 14 | fn is_big(n: i32) -> bool { 15 | // Получаем доступ к константе внутри функции 16 | n > THRESHOLD 17 | } 18 | 19 | fn main() { 20 | let n = 16; 21 | 22 | // Получаем доступ к константе внутри функции main 23 | println!("Это язык {}", LANGUAGE); 24 | println!("Установим предел, равный {}", THRESHOLD); 25 | println!("Число {} {} предела", n, if is_big(n) { "больше" } else { "меньше" }); 26 | 27 | // Ошибка! `const` нельзя изменить. 28 | THRESHOLD = 5; 29 | // ИСПРАВЬТЕ ^ Закомментируйте эту строчку 30 | } 31 | ``` 32 | 33 | ### Смотрите также: 34 | 35 | [RFC для `const`/`static`](https://github.com/rust-lang/rfcs/blob/master/text/0246-const-vs-static.md), 36 | [время жизни `'static`](../scope/lifetime/static_lifetime.md) 37 | -------------------------------------------------------------------------------- /src/custom_types/enum.md: -------------------------------------------------------------------------------- 1 | # Перечисления 2 | 3 | Ключевое слово `enum` позволяет создавать тип данных, 4 | который представляет собой один из нескольких возможных вариантов. 5 | Любой вариант, действительный как `struct`, также действителен как `enum`. 6 | 7 | ```rust,editable 8 | // Create an `enum` to classify a web event. Note how both 9 | // names and type information together specify the variant: 10 | // `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`. 11 | // Each is different and independent. 12 | enum WebEvent { 13 | // An `enum` may either be `unit-like`, 14 | PageLoad, 15 | PageUnload, 16 | // like tuple structs, 17 | KeyPress(char), 18 | Paste(String), 19 | // or c-like structures. 20 | Click { x: i64, y: i64 }, 21 | } 22 | 23 | // A function which takes a `WebEvent` enum as an argument and 24 | // returns nothing. 25 | fn inspect(event: WebEvent) { 26 | match event { 27 | WebEvent::PageLoad => println!("page loaded"), 28 | WebEvent::PageUnload => println!("page unloaded"), 29 | // Destructure `c` from inside the `enum`. 30 | WebEvent::KeyPress(c) => println!("pressed '{}'.", c), 31 | WebEvent::Paste(s) => println!("pasted \"{}\".", s), 32 | // Destructure `Click` into `x` and `y`. 33 | WebEvent::Click { x, y } => { 34 | println!("clicked at x={}, y={}.", x, y); 35 | }, 36 | } 37 | } 38 | 39 | fn main() { 40 | let pressed = WebEvent::KeyPress('x'); 41 | // `to_owned()` creates an owned `String` from a string slice. 42 | let pasted = WebEvent::Paste("my text".to_owned()); 43 | let click = WebEvent::Click { x: 20, y: 80 }; 44 | let load = WebEvent::PageLoad; 45 | let unload = WebEvent::PageUnload; 46 | 47 | inspect(pressed); 48 | inspect(pasted); 49 | inspect(click); 50 | inspect(load); 51 | inspect(unload); 52 | } 53 | 54 | ``` 55 | 56 | ### Смотрите также: 57 | 58 | [`match`](../flow_control/match.md), [`fn`](../fn.md) и [`String`](../std/str.md) 59 | -------------------------------------------------------------------------------- /src/custom_types/enum/c_like.md: -------------------------------------------------------------------------------- 1 | # С-подобные 2 | 3 | `enum` могут быть использованы как C-подобные перечисления. 4 | 5 | ```rust,editable 6 | // Атрибут, который убирает предупреждения компилятора 7 | // о неиспользуемом коде 8 | #![allow(dead_code)] 9 | 10 | // enum с неявным дискриминатором (начинается с 0) 11 | enum Number { 12 | Zero, 13 | One, 14 | Two, 15 | } 16 | 17 | // enum с явным дискриминатором 18 | enum Color { 19 | Red = 0xff0000, 20 | Green = 0x00ff00, 21 | Blue = 0x0000ff, 22 | } 23 | 24 | fn main() { 25 | // `enums` может быть преобразован в целочисленное значение. 26 | println!("нулевой элемент {}", Number::Zero as i32); 27 | println!("первый элемент {}", Number::One as i32); 28 | 29 | println!("красный цвет #{:06x}", Color::Red as i32); 30 | println!("голубой цвет #{:06x}", Color::Blue as i32); 31 | } 32 | ``` 33 | 34 | ### Смотрите также: 35 | 36 | [приведение типа](../../types/cast.md) 37 | -------------------------------------------------------------------------------- /src/custom_types/enum/enum_use.md: -------------------------------------------------------------------------------- 1 | # Декларация use 2 | 3 | Декларация `use` используется, чтобы 4 | убрать необходимость указывать область видимости: 5 | 6 | ```rust,editable 7 | // Атрибут, который убирает предупреждения компилятора 8 | // о неиспользуемом коде 9 | #![allow(dead_code)] 10 | 11 | enum Status { 12 | Rich, 13 | Poor, 14 | } 15 | 16 | enum Work { 17 | Civilian, 18 | Soldier, 19 | } 20 | 21 | fn main() { 22 | // Используем `use` для каждого из вариантов, чтобы они были доступны 23 | // без указания области видимости. 24 | use Status::{Poor, Rich}; 25 | // Автоматически используем `use` для каждого из вариантов в `Work`. 26 | use Work::*; 27 | 28 | // Эквивалентно `Status::Poor`. 29 | let status = Poor; 30 | // Эквивалентно to `Work::Civilian`. 31 | let work = Civilian; 32 | 33 | match status { 34 | // Обратите внимание, как используются варианты из перечисления `Status` 35 | // благодаря `use` 36 | Rich => println!("У богатого куча денег!"), 37 | Poor => println!("У бедняка денег нет, но он держится..."), 38 | } 39 | 40 | match work { 41 | // И снова используем варианты напрямую. 42 | Civilian => println!("Гражданин работает!"), 43 | Soldier => println!("Солдаты служат!"), 44 | } 45 | } 46 | ``` 47 | 48 | ### Смотрите также: 49 | 50 | [`match` (сопоставление с образцом)](../../flow_control/match.md) и [`use`](../../mod/use.md) 51 | -------------------------------------------------------------------------------- /src/custom_types/enum/testcase_linked_list.md: -------------------------------------------------------------------------------- 1 | # Пример: Связанный список 2 | 3 | Пример использования `enums` для создания связанного списка: 4 | 5 | ```rust,editable 6 | use List::*; 7 | 8 | enum List { 9 | // Cons: Кортежная структура, которая хранит элемент 10 | // и указатель на следующий узел 11 | Cons(u32, Box), 12 | // Nil: Узел, обозначающий конец связанного списка 13 | Nil, 14 | } 15 | 16 | // Методы могут быть присоединены к перечислению 17 | impl List { 18 | // Создаём пустой список 19 | fn new() -> List { 20 | // `Nil` имеет тип `List` 21 | Nil 22 | } 23 | 24 | // Функция, которая принимает список и возвращает тот же список, 25 | // но с новым элементом в начале 26 | fn prepend(self, elem: u32) -> List { 27 | // `Cons` также имеет тип `List` 28 | Cons(elem, Box::new(self)) 29 | } 30 | 31 | // Возвращаем длину списка 32 | fn len(&self) -> u32 { 33 | // `self` должен быть сопоставлен (проверен на соответствие), 34 | // поскольку поведение этого метода зависит от варианта `self` 35 | // `self` имеет тип `&List`, а `*self` имеет тип `List`, сопоставление на 36 | // конкретном типе `T` предпочтительнее, чем сопоставление по ссылке `&T` 37 | match *self { 38 | // Мы не можем завладеть `tail`, т.к. `self` заимствован; 39 | // вместо этого возьмём ссылку на `tail` 40 | Cons(_, ref tail) => 1 + tail.len(), 41 | // Базовый случай: Пустой список имеет нулевую длину 42 | Nil => 0 43 | } 44 | } 45 | 46 | // Возвращаем представление списка в виде (размещённой в куче) строки 47 | fn stringify(&self) -> String { 48 | match *self { 49 | Cons(head, ref tail) => { 50 | // `format!` похож на `print!`, но возвращает строку 51 | // размещённую в куче, вместо вывода на консоль 52 | format!("{}, {}", head, tail.stringify()) 53 | }, 54 | Nil => { 55 | format!("Nil") 56 | }, 57 | } 58 | } 59 | } 60 | 61 | fn main() { 62 | // Создаём пустой связанный список 63 | let mut list = List::new(); 64 | 65 | // Присоединяем несколько элементов 66 | list = list.prepend(1); 67 | list = list.prepend(2); 68 | list = list.prepend(3); 69 | 70 | // Отображаем окончательное состояние списка 71 | println!("размер связанного списка: {}", list.len()); 72 | println!("{}", list.stringify()); 73 | } 74 | ``` 75 | 76 | ### Смотрите также: 77 | 78 | [`Box`](std/box.html) и [`методы`](fn/methods.html) 79 | -------------------------------------------------------------------------------- /src/error.md: -------------------------------------------------------------------------------- 1 | # Обработка ошибок 2 | 3 | Обработка ошибок - это процесс управления возможными сбоями. 4 | Например ошибка чтения файла и последующее использование *плохих* данных могут прояснить проблематику. 5 | Уведомление и явное управление этими ошибками сохранит оставшуюся часть программы от различных неожиданностей. 6 | 7 | В Rust есть разные пути работы с ошибками, которые описаны в следующих главах. Они все имеют те или иные отличия и разные варианты использования. Как правило большого пальца: 8 | 9 | Явный `panic` в основном применим для тестирования и работы с невосстановимыми ошибками. 10 | При прототипировании его можно использовать, например, когда работаем с ещё не реализованными функциями, но в этом случае лучше использовать более говорящее `unimplemented`. 11 | В тестах `panic` - разумный способ явного оповещения об ошибке. 12 | 13 | Тип `Option` предназначен для случаев, когда значение не обязательно или когда отсутствие значения не является ошибкой. 14 | Например, корневые директории `/` и `C:` не имеют родителя. При работе с `Option`, 15 | для прототипирования и случаев, когда мы точно знаем, что 16 | значение должно быть, отлично подходит `unwrap`. Однако более полезен `expect`, так как он позволяет 17 | указать сообщение об ошибке на случай, если что-то пойдёт не так. 18 | 19 | Когда есть вероятность, что что-то пойдёт не так и вызывающая 20 | сторона должна как-то обработать эту ситуацию, используйте `Result`. 21 | Вы также можете использовать `unwrap` и `expect` (пожалуйста, не делайте этого, если вы не пишете тест или не прототипируете). 22 | 23 | Для более полного изучения обработки ошибок, обратитесь к [соответствующему разделу в книге](https://doc.rust-lang.org/book/ch09-00-error-handling.html). 24 | -------------------------------------------------------------------------------- /src/error/multiple_error_types.md: -------------------------------------------------------------------------------- 1 | # Несколько типов ошибок 2 | 3 | Предыдущие примеры всегда были очень удобны: `Result` взаимодействовали с другими `Result`, а `Option` - с другими `Option`. 4 | 5 | Иногда `Option` необходимо взаимодействовать с 6 | `Result`, или `Result` с 7 | `Result`. В этих случаях, нам нужно 8 | управлять этими разными типами ошибок таким образом, чтобы 9 | можно было их компоновать и легко взаимодействовать с ними. 10 | 11 | В следующем коде, два варианта `unwrap` 12 | генерируют разные типы ошибок. `Vec::first` 13 | возвращает `Option`, в то время как 14 | `parse::` возвращает 15 | `Result`: 16 | 17 | ```rust,editable,ignore,mdbook-runnable 18 | fn double_first(vec: Vec<&str>) -> i32 { 19 | let first = vec.first().unwrap(); // Генерирует ошибку 1 20 | 2 * first.parse::().unwrap() // Генерирует ошибку 2 21 | } 22 | 23 | fn main() { 24 | let numbers = vec!["42", "93", "18"]; 25 | let empty = vec![]; 26 | let strings = vec!["tofu", "93", "18"]; 27 | 28 | println!("Первое удвоенное {}", double_first(numbers)); 29 | 30 | println!("Первое удвоенное {}", double_first(empty)); 31 | // Ошибка 1: входной вектор пустой 32 | 33 | println!("Первое удвоенное {}", double_first(strings)); 34 | // Ошибка 2: элемент не может быть преобразован в число 35 | } 36 | ``` 37 | 38 | В следующих главах мы рассмотрим различные стратегии обработки этих типов проблем. 39 | -------------------------------------------------------------------------------- /src/error/multiple_error_types/boxing_errors.md: -------------------------------------------------------------------------------- 1 | # Упаковка ошибок (`Box`) 2 | 3 | Чтобы написать простой код и при этом использовать 4 | оригинальные ошибки, необходимо упаковать 5 | ([`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html)) их. 6 | Минусом данного способа является то, что тип ошибок известен 7 | только во время выполнения программы, а не [определён 8 | статически](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch). 9 | 10 | Стандартная библиотека помогает упаковывать наши ошибки. 11 | Это достигается за счёт того, что для `Box` 12 | реализована конвертация из любого типа, реализующего типаж 13 | `Error`, в типаж-объект `Box` 14 | через [`From`](https://doc.rust-lang.org/std/convert/trait.From.html). 15 | 16 | ```rust,editable 17 | use std::error; 18 | use std::fmt; 19 | 20 | // Создадим псевдоним с типом ошибки `Box`. 21 | type Result = std::result::Result>; 22 | 23 | #[derive(Debug, Clone)] 24 | struct EmptyVec; 25 | 26 | impl fmt::Display for EmptyVec { 27 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 28 | write!(f, "неверный первый элемент") 29 | } 30 | } 31 | 32 | impl error::Error for EmptyVec { 33 | fn description(&self) -> &str { 34 | "неверный первый элемент" 35 | } 36 | 37 | fn cause(&self) -> Option<&error::Error> { 38 | // Общая ошибка, основная причина не отслеживается. 39 | None 40 | } 41 | } 42 | 43 | fn double_first(vec: Vec<&str>) -> Result { 44 | vec.first() 45 | .ok_or_else(|| EmptyVec.into()) // Упаковка (преобразование в Box) 46 | .and_then(|s| { 47 | s.parse::() 48 | .map_err(|e| e.into()) // Упаковка (преобразование в Box) 49 | .map(|i| 2 * i) 50 | }) 51 | } 52 | 53 | fn print(result: Result) { 54 | match result { 55 | Ok(n) => println!("Удвоенный первый элемент: {}", n), 56 | Err(e) => println!("Ошибка: {}", e), 57 | } 58 | } 59 | 60 | fn main() { 61 | let numbers = vec!["42", "93", "18"]; 62 | let empty = vec![]; 63 | let strings = vec!["tofu", "93", "18"]; 64 | 65 | print(double_first(numbers)); 66 | print(double_first(empty)); 67 | print(double_first(strings)); 68 | } 69 | ``` 70 | 71 | ### Смотрите также: 72 | 73 | [Динамическая диспетчеризация](https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch) и [типаж `Error`](https://doc.rust-lang.org/std/error/trait.Error.html) 74 | -------------------------------------------------------------------------------- /src/error/multiple_error_types/option_result.md: -------------------------------------------------------------------------------- 1 | # Извлечение `Result` из `Option` 2 | 3 | Наиболее простой способ обработки ошибок разных типов - это встраивание их друг в друга. 4 | 5 | ```rust,editable 6 | use std::num::ParseIntError; 7 | 8 | fn double_first(vec: Vec<&str>) -> Option> { 9 | vec.first().map(|first| { 10 | first.parse::().map(|n| 2 * n) 11 | }) 12 | } 13 | 14 | fn main() { 15 | let numbers = vec!["42", "93", "18"]; 16 | let empty = vec![]; 17 | let strings = vec!["tofu", "93", "18"]; 18 | 19 | println!("Первое удвоенное: {:?}", double_first(numbers)); 20 | 21 | println!("Первое удвоенное: {:?}", double_first(empty)); 22 | // Ошибка первая: исходный вектор пустой 23 | 24 | println!("Первое удвоенное {:?}", double_first(strings)); 25 | // Ошибка вторая: элемент не переводится в число 26 | } 27 | ``` 28 | 29 | Бывает, мы хотим приостановить работу при ошибке (как при 30 | помощи оператора [`?`](../result/enter_question_mark.md)), но продолжать 31 | работать, если `Option` `None`. Есть 32 | пара комбинаторов, которые поменяют местами 33 | `Result` и `Option`. 34 | 35 | ```rust,editable 36 | use std::num::ParseIntError; 37 | 38 | fn double_first(vec: Vec<&str>) -> Result, ParseIntError> { 39 | let opt = vec.first().map(|first| { 40 | first.parse::().map(|n| 2 * n) 41 | }); 42 | 43 | let opt = opt.map_or(Ok(None), |r| r.map(Some))?; 44 | 45 | Ok(opt) 46 | } 47 | 48 | fn main() { 49 | let numbers = vec!["42", "93", "18"]; 50 | let empty = vec![]; 51 | let strings = vec!["tofu", "93", "18"]; 52 | 53 | println!("Первое удвоенное: {:?}", double_first(numbers)); 54 | println!("Первое удвоенное: {:?}", double_first(empty)); 55 | println!("Первое удвоенное: {:?}", double_first(strings)); 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /src/error/panic.md: -------------------------------------------------------------------------------- 1 | # `panic` 2 | 3 | Самый простой механизм обработки ошибок, с которым мы познакомимся – это `panic`. 4 | Он печатает сообщение с ошибкой, начинает процедуру 5 | раскрутки стека и, чаще всего, завершает программу. В данном примере мы явно вызываем `panic` в случае ошибки: 6 | 7 | ```rust,editable,ignore,mdbook-runnable 8 | fn give_princess(gift: &str) { 9 | // Принцесса ненавидит змей, поэтому нам нужно остановиться, если она не одобрит! 10 | if gift == "змея" { panic!("AAAaaaaa!!!!"); } 11 | 12 | println!("Я люблю тебя, {}!!!!!", gift); 13 | } 14 | 15 | fn main() { 16 | give_princess("плюшевый мишка"); 17 | give_princess("змея"); 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /src/error/result/early_returns.md: -------------------------------------------------------------------------------- 1 | # Ранний выход 2 | 3 | В предыдущем примере мы явно обработали ошибки при помощи комбинаторов. 4 | Другой способ сделать это - использовать комбинацию выражения 5 | `match` и *раннего выхода*. 6 | 7 | Таким образом мы просто можем остановить работу функции и 8 | вернуть ошибку, если она произошла. Для некоторых, такой код 9 | будет легче в чтении и написании. Посмотрите код из предыдущего 10 | примера, переписанный с использованием раннего выхода: 11 | 12 | ```rust,editable 13 | use std::num::ParseIntError; 14 | 15 | fn multiply(first_number_str: &str, second_number_str: &str) -> Result { 16 | let first_number = match first_number_str.parse::() { 17 | Ok(first_number) => first_number, 18 | Err(e) => return Err(e), 19 | }; 20 | 21 | let second_number = match second_number_str.parse::() { 22 | Ok(second_number) => second_number, 23 | Err(e) => return Err(e), 24 | }; 25 | 26 | Ok(first_number * second_number) 27 | } 28 | 29 | fn print(result: Result) { 30 | match result { 31 | Ok(n) => println!("n равно {}", n), 32 | Err(e) => println!("Ошибка: {}", e), 33 | } 34 | } 35 | 36 | fn main() { 37 | print(multiply("10", "2")); 38 | print(multiply("t", "2")); 39 | } 40 | ``` 41 | 42 | На данный момент, мы изучили обработку ошибок при помощи 43 | комбинаторов и раннего выхода. Мы хотим избежать паники, но 44 | явная обработка всех ошибок достаточно громоздка. 45 | 46 | В следующем разделе, мы познакомимся с `?` для 47 | случаев, где нам просто хотим сделать `unwrap` без 48 | возможности вызова `panic`. 49 | -------------------------------------------------------------------------------- /src/error/result/result_alias.md: -------------------------------------------------------------------------------- 1 | # Псевдонимы для `Result` 2 | 3 | Как насчёт случая, когда мы хотим использовать конкретный тип `Result` много раз? 4 | Напомним, что Rust позволяет нам создавать [псевдонимы][typealias]. Мы можем 5 | удобно объявить псевдоним для конкретного `Result`. 6 | 7 | Особенно полезным может быть создание псевдонимов на уровне модулей. Ошибки, 8 | найденные в конкретном модуле, часто имеют один и тот же тип `Err`, поэтому один 9 | псевдоним может лаконично объявить *все* ассоциированные `Results`. 10 | Это настолько полезно, что библиотека `std` обеспечивает даже один: `io::Result`! 11 | 12 | Ниже приведён краткий пример для демонстрации синтаксиса: 13 | 14 | ```rust,editable 15 | use std::num::ParseIntError; 16 | 17 | // Объявим обобщённый псевдоним для `Result` с типом ошибки `ParseIntError`. 18 | type AliasedResult = Result; 19 | 20 | // Используем вышеуказанный псевдоним для обозначения 21 | // нашего конкретного типа `Result`. 22 | fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult { 23 | first_number_str.parse::().and_then(|first_number| { 24 | second_number_str.parse::().map(|second_number| first_number * second_number) 25 | }) 26 | } 27 | 28 | // Здесь псевдоним снова позволяет нам сэкономить место. 29 | fn print(result: AliasedResult) { 30 | match result { 31 | Ok(n) => println!("n это {}", n), 32 | Err(e) => println!("Ошибка: {}", e), 33 | } 34 | } 35 | 36 | fn main() { 37 | print(multiply("10", "2")); 38 | print(multiply("t", "2")); 39 | } 40 | ``` 41 | 42 | ### Смотрите также: 43 | 44 | [`io::Result`][io_result] 45 | 46 | [typealias]: types/alias.html 47 | [io_result]: https://doc.rust-lang.org/std/io/type.Result.html 48 | -------------------------------------------------------------------------------- /src/expression.md: -------------------------------------------------------------------------------- 1 | # Выражения 2 | 3 | Программы на языке Rust - это (в основном) набор последовательных операторов: 4 | 5 | ``` 6 | fn main() { 7 | // оператор 8 | // оператор 9 | // оператор 10 | } 11 | ``` 12 | 13 | Существует несколько типов операторов в Rust. 14 | Наиболее распространённые - оператор связывания и выражение, заканчивающееся `;`: 15 | 16 | ``` 17 | fn main() { 18 | // оператор связывания 19 | let x = 5; 20 | 21 | // оператор выражения 22 | x; 23 | x + 1; 24 | 15; 25 | } 26 | ``` 27 | 28 | Блоки так же могут быть частью оператора выражения. 29 | Они используются в качестве [r-values](https://en.wikipedia.org/wiki/Value_%28computer_science%29#lrvalue) при присваивании. 30 | Последнее выражение в блоке будет присвоено [l-value](https://en.wikipedia.org/wiki/Value_%28computer_science%29#lrvalue). 31 | Однако, если последнее выражение в блоке оканчивается точкой с запятой, 32 | в качестве значения будет возвращено `()`. 33 | 34 | ```rust,editable 35 | fn main() { 36 | let x = 5u32; 37 | 38 | let y = { 39 | let x_squared = x * x; 40 | let x_cube = x_squared * x; 41 | 42 | // Результат этого выражение будет присвоен переменной `y` 43 | x_cube + x_squared + x 44 | }; 45 | 46 | let z = { 47 | // Т.к это выражение оканчивается на `;`, переменной `z` будет присвоен `()` 48 | 2 * x; 49 | }; 50 | 51 | println!("x равен {:?}", x); 52 | println!("y равен {:?}", y); 53 | println!("z равен {:?}", z); 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /src/flow_control.md: -------------------------------------------------------------------------------- 1 | # Управление потоком 2 | 3 | Неотъемлемой частью любого языка программирования является изменение потоков управления: 4 | `if`/`else`, `for` и другие. Давайте поговорим о них в языке Rust. 5 | -------------------------------------------------------------------------------- /src/flow_control/if_else.md: -------------------------------------------------------------------------------- 1 | # if/else 2 | 3 | Ветвление с помощью `if`-`else` такое же, как и в других языка программирования. 4 | В отличие от многих других языков программирования, логические условия не должны быть заключены 5 | в круглые скобки и после каждого условия должен следовать блок. 6 | Условия `if`-`else` являются выражениями, и все ветки должны возвращать одинаковый тип данных. 7 | 8 | ```rust,editable 9 | fn main() { 10 | let n = 5; 11 | 12 | if n < 0 { 13 | print!("{} — отрицательное", n); 14 | } else if n > 0 { 15 | print!("{} — положительное", n); 16 | } else { 17 | print!("{} — нуль", n); 18 | } 19 | 20 | let big_n = 21 | if n < 10 && n > -10 { 22 | println!(", малое по модулю число, умножим его в десять раз"); 23 | 24 | // Это выражение вернёт `i32`. 25 | 10 * n 26 | } else { 27 | println!(", большое по модулю число, уменьшим его вдвое"); 28 | 29 | // И это выражение вернёт `i32`. 30 | n / 2 31 | // ЗАДАНИЕ ^ Попробуйте отбросить значение, добавив точку с запятой. 32 | }; 33 | // ^ Не забудьте добавить тут точку с запятой! Все операторы `let` требуют её.. 34 | 35 | println!("{} -> {}", n, big_n); 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /src/flow_control/loop.md: -------------------------------------------------------------------------------- 1 | # loop 2 | 3 | Rust предоставляет ключевое слово `loop` для обозначения бесконечного цикла. 4 | 5 | Оператор `break` используется чтобы выйти из цикла в любое время, оператор 6 | `continue` используется чтобы пропустить оставшуюся часть цикла и начать новую итерацию. 7 | 8 | ```rust,editable 9 | fn main() { 10 | let mut count = 0u32; 11 | 12 | println!("Давайте считать до бесконечности!"); 13 | 14 | // Бесконечный цикл 15 | loop { 16 | count += 1; 17 | 18 | if count == 3 { 19 | println!("три"); 20 | 21 | // Пропустить оставшуюся часть цикла 22 | continue; 23 | } 24 | 25 | println!("{}", count); 26 | 27 | if count == 5 { 28 | println!("Всё, достаточно"); 29 | 30 | // Выйти из цикла 31 | break; 32 | } 33 | } 34 | } 35 | ``` -------------------------------------------------------------------------------- /src/flow_control/loop/nested.md: -------------------------------------------------------------------------------- 1 | # Вложенность и метки 2 | 3 | Можно прерывать выполнение внешних циклов с помощью `break` или `continue`, 4 | когда речь заходит о вложенных циклах. 5 | Для этого циклы должны быть обозначены метками вроде `'label`, 6 | а метки должны быть переданы операторам `break` или `continue`. 7 | 8 | ```rust,editable 9 | #![allow(unreachable_code)] 10 | 11 | fn main() { 12 | 'outer: loop { 13 | println!("Вошли во внешний цикл"); 14 | 15 | 'inner: loop { 16 | println!("Вошли во внутренний цикл"); 17 | 18 | // Это прервёт лишь внутренний цикл 19 | //break; 20 | 21 | // Это прервёт внешний цикл 22 | break 'outer; 23 | } 24 | 25 | println!("Эта точка не будет достигнута"); 26 | } 27 | 28 | println!("Вышли из внешнего цикла"); 29 | } 30 | ``` -------------------------------------------------------------------------------- /src/flow_control/loop/return.md: -------------------------------------------------------------------------------- 1 | # Возврат из циклов 2 | 3 | Одним из видов использования цикла `loop` является повторение операции, пока 4 | она не будет выполнена. Если операция возвращает значение, вам может 5 | потребоваться передать его в другую часть кода: поместите его после `break`, 6 | и оно будет возвращено выражением `loop`. 7 | 8 | ```rust,editable 9 | fn main() { 10 | let mut counter = 0; 11 | 12 | let result = loop { 13 | counter += 1; 14 | 15 | if counter == 10 { 16 | break counter * 2; 17 | } 18 | }; 19 | 20 | assert_eq!(result, 20); 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /src/flow_control/match.md: -------------------------------------------------------------------------------- 1 | # match 2 | 3 | Rust предоставляет ключевое слово `match`, которое используется для проверки на 4 | соответствие шаблону. `match` можно использовать как `switch` в языке C. 5 | 6 | ```rust,editable 7 | fn main() { 8 | let number = 13; 9 | // ЗАДАНИЕ ^ Попробуйте присвоить `number` другое значение 10 | 11 | println!("Tell me about {}", number); 12 | match number { 13 | // Сопоставление с одним значением 14 | 1 => println!("One!"), 15 | // Сопоставление с несколькими значениями 16 | 2 | 3 | 5 | 7 | 11 => println!("This is a prime"), 17 | // Сопоставление с диапазоном значений 18 | 13...19 => println!("A teen"), 19 | // Обработка остальных случаев 20 | _ => println!("Ain't special"), 21 | } 22 | 23 | let boolean = true; 24 | // Match так же является выражением 25 | let binary = match boolean { 26 | // Ветви match должны обработать все возможные значения переменной 27 | false => 0, 28 | true => 1, 29 | // ЗАДАНИЕ ^ Попробуйте закомментировать эту ветвь 30 | }; 31 | 32 | println!("{} -> {}", boolean, binary); 33 | } 34 | ``` -------------------------------------------------------------------------------- /src/flow_control/match/binding.md: -------------------------------------------------------------------------------- 1 | # Связывание 2 | 3 | Косвенный доступ к переменной делает невозможным ветвление и использование 4 | переменной без повторной привязки. `match` предоставляет символ `@` 5 | для привязки значения к имени: 6 | 7 | ```rust,editable 8 | // Функция `age`, возвращающая `u32`. 9 | fn age() -> u32 { 10 | 15 11 | } 12 | 13 | fn main() { 14 | println!("Скажи мне свой возраст"); 15 | 16 | match age() { 17 | 0 => println!("Я ещё не родился"), 18 | // Можно было бы использовать только 1 ... 12 в `match`, 19 | // но какого возраста тогда был бы ребёнок? Вместо этого мы 20 | // привязываем `n` к последовательности 1 .. 12. 21 | // Теперь мы можем сообщить возраст. 22 | n @ 1 ..= 12 => println!("Я ребёнок. Мне {:?}", n), 23 | n @ 13 ..= 19 => println!("Я подросток. Мне {:?}", n), 24 | // Ничего не привязываем. 25 | n => println!("Я взрослый. Мне {:?}", n), 26 | } 27 | } 28 | ``` 29 | 30 | Вы также можете использовать привязку для "деструктурирования" 31 | вариантов `enum`, таких как `Option`: 32 | 33 | ```rust,editable 34 | fn some_number() -> Option { 35 | Some(42) 36 | } 37 | 38 | fn main() { 39 | match some_number() { 40 | // Вариант `Some`, выбираем, если его значение, привязанное к `n`, 41 | // равно 42. 42 | Some(n @ 42) => println!("Ответ: {}!", n), 43 | // При других числах. 44 | Some(n) => println!("Не интересно... {}", n), 45 | // Для всего остального (вариант `None`). 46 | _ => (), 47 | } 48 | } 49 | ``` 50 | 51 | ### Смотрите также: 52 | 53 | [Функции](../../fn.md), [`enum`](../../custom_types/enum.md) и [`Option`](../../std/option.md) 54 | -------------------------------------------------------------------------------- /src/flow_control/match/destructuring.md: -------------------------------------------------------------------------------- 1 | # Деструктуризация 2 | 3 | Блок `match` может деструктурировать элементы в различных формах. 4 | 5 | - [Перечисления](destructuring/destructure_enum.md) 6 | - [Указатели](destructuring/destructure_pointers.md) 7 | - [Структуры](destructuring/destructure_structures.md) 8 | - [Кортежи](destructuring/destructure_tuple.md) 9 | -------------------------------------------------------------------------------- /src/flow_control/match/destructuring/destructure_enum.md: -------------------------------------------------------------------------------- 1 | # Перечисления 2 | 3 | Деструктуризация `enum` происходит следующим образом: 4 | 5 | ```rust,editable 6 | // `allow` необходим, чтобы компилятор не выводил предупреждения, 7 | // т.к используется только один вариант 8 | #[allow(dead_code)] 9 | enum Color { 10 | // Эти 3 перечисления определяют цвет по названию. 11 | Red, 12 | Blue, 13 | Green, 14 | // Остальные используют `u32` кортежи для идентификации цветовых моделей. 15 | RGB(u32, u32, u32), 16 | HSV(u32, u32, u32), 17 | HSL(u32, u32, u32), 18 | CMY(u32, u32, u32), 19 | CMYK(u32, u32, u32, u32), 20 | } 21 | 22 | fn main() { 23 | let color = Color::RGB(122, 17, 40); 24 | // ЗАДАНИЕ ^ Попробуйте другие значения для `color` 25 | 26 | println!("Какой это цвет?"); 27 | // `enum` может быть деструктурирован с помощью `match`. 28 | match color { 29 | Color::Red => println!("Красный цвет!"), 30 | Color::Blue => println!("Синий цвет!"), 31 | Color::Green => println!("Зелёный цвет!"), 32 | Color::RGB(r, g, b) => 33 | println!("Красный: {}, зелёный: {}, и синий: {}!", r, g, b), 34 | Color::HSV(h, s, v) => 35 | println!("Тон: {}, насыщенность: {}, значение: {}!", h, s, v), 36 | Color::HSL(h, s, l) => 37 | println!("Тон: {}, насыщенность: {}, светлота: {}!", h, s, l), 38 | Color::CMY(c, m, y) => 39 | println!("Голубой: {}, пурпурный: {}, жёлтый: {}!", c, m, y), 40 | Color::CMYK(c, m, y, k) => 41 | println!("Голубой: {}, пурпурный: {}, жёлтый: {}, key (чёрный): {}!", 42 | c, m, y, k), 43 | // Нет необходимости в других ветвях, т.к были рассмотрены все варианты 44 | } 45 | } 46 | ``` 47 | 48 | ### Смотрите также: 49 | 50 | [`#[allow(...)]`](../../../attribute/unused.md), [цветовая модель](https://en.wikipedia.org/wiki/Color_model) и [`перечисления`](../../../custom_types/enum.md) 51 | -------------------------------------------------------------------------------- /src/flow_control/match/destructuring/destructure_pointers.md: -------------------------------------------------------------------------------- 1 | # Указатели и ссылки 2 | 3 | Для указателей необходимо различать деструктуризацию и разыменование, 4 | поскольку это разные концепции, которые используются иначе, чем в языке `С`. 5 | 6 | * Разыменование использует `*` 7 | * Деструктуризация использует `&`, `ref` и `ref mut` 8 | 9 | ```rust,editable 10 | fn main() { 11 | // Присваиваем ссылку на тип `i32`. 12 | // Символ `&` означает, что присваивается ссылка. 13 | let reference = &4; 14 | 15 | match reference { 16 | // Если `reference` - это шаблон, который сопоставляется с `&val`, 17 | // то это приведёт к сравнению: 18 | // `&i32` 19 | // `&val` 20 | // ^ Мы видим, что если отбросить сопоставляемые `&`, 21 | // то переменной `val` должно быть присвоено `i32`. 22 | &val => println!("Получаем значение через деструктуризацию: {:?}", val), 23 | } 24 | 25 | // Чтобы избежать символа `&`, нужно разыменовывать ссылку до сопоставления. 26 | match *reference { 27 | val => println!("Получаем значение через разыменование: {:?}", val), 28 | } 29 | 30 | // Что если у нас нет ссылки? `reference` была с `&`, 31 | // потому что правая часть была ссылкой. Но это не ссылка, 32 | // потому что правая часть ею не является. 33 | let _not_a_reference = 3; 34 | 35 | // Rust предоставляет ключевое слово `ref` именно для этой цели. 36 | // Оно изменяет присваивание так, что создаётся ссылка для элемента. 37 | // Теперь ссылка присвоена. 38 | let ref _is_a_reference = 3; 39 | 40 | // Соответственно, для определения двух значений без ссылок, 41 | // ссылки можно назначить с помощью `ref` и `ref mut`. 42 | let value = 5; 43 | let mut mut_value = 6; 44 | 45 | // Используйте ключевое слово `ref` для создания ссылки. 46 | match value { 47 | ref r => println!("Получили ссылку на значение: {:?}", r), 48 | } 49 | 50 | // Используйте `ref mut` аналогичным образом. 51 | match mut_value { 52 | ref mut m => { 53 | // Получаем ссылку. Её нужно разыменовать, 54 | // прежде чем мы сможем что-то добавить. 55 | *m += 10; 56 | println!("Мы добавили 10. `mut_value`: {:?}", m); 57 | }, 58 | } 59 | } 60 | ``` -------------------------------------------------------------------------------- /src/flow_control/match/destructuring/destructure_structures.md: -------------------------------------------------------------------------------- 1 | # Структуры 2 | 3 | `Структуры` могут быть деструктурированы следующим образом: 4 | 5 | ```rust,editable 6 | fn main() { 7 | struct Foo { x: (u32, u32), y: u32 } 8 | 9 | // деструктуризация члена структуры 10 | let foo = Foo { x: (1, 2), y: 3 }; 11 | let Foo { x: (a, b), y } = foo; 12 | 13 | println!("a = {}, b = {}, y = {} ", a, b, y); 14 | 15 | // Вы можете деструктурировать структуру и переименовывать переменные, 16 | // порядок при этом не важен 17 | 18 | let Foo { y: i, x: j } = foo; 19 | println!("i = {:?}, j = {:?}", i, j); 20 | 21 | // а так же можно проигнорировать часть переменных: 22 | let Foo { y, .. } = foo; 23 | println!("y = {}", y); 24 | 25 | // следующий код выдаст ошибку: в шаблоне нет упоминания поля `x` 26 | // let Foo { y } = foo; 27 | } 28 | ``` 29 | 30 | ### Смотрите также: 31 | 32 | [стуктуры](../../../custom_types/structs.md), [шаблон ref](../../../scope/borrow/ref.md) 33 | -------------------------------------------------------------------------------- /src/flow_control/match/destructuring/destructure_tuple.md: -------------------------------------------------------------------------------- 1 | # Кортежи 2 | 3 | Кортежи можно деструктурировать с помощью `match` следующим образом: 4 | 5 | ```rust,editable 6 | fn main() { 7 | let pair = (0, -2); 8 | // ЗАДАНИЕ ^ Попробуйте другие значения для `pair` 9 | 10 | println!("Tell me about {:?}", pair); 11 | // Match можно использовать для деструктуризации кортежей 12 | match pair { 13 | // Деструктурируем два значения 14 | (0, y) => println!("Первое значение `0`, а `y` равно `{:?}`", y), 15 | (x, 0) => println!("`x` равно `{:?}`, а второе значение `0`", x), 16 | _ => println!("Неважно, какого они значения"), 17 | // `_` означает, что значение не будет связано с переменной 18 | } 19 | } 20 | ``` 21 | 22 | ### Смотрите также: 23 | 24 | [Tuples](primitives/tuples.html) 25 | -------------------------------------------------------------------------------- /src/flow_control/match/guard.md: -------------------------------------------------------------------------------- 1 | # Ограничители шаблонов 2 | 3 | Внутри конструкции `match` можно добавить *ограничитель шаблонов* 4 | для фильтрации возможных вариантов. 5 | 6 | ```rust,editable 7 | fn main() { 8 | let pair = (2, -2); 9 | // ЗАДАНИЕ ^ Попробуйте разные значения `pair` 10 | 11 | println!("Расскажи мне о {:?}", pair); 12 | match pair { 13 | (x, y) if x == y => println!("Близнецы"), 14 | // Данное ^ `условие if` является ограничителем шаблонов 15 | (x, y) if x + y == 0 => println!("Антиматерия, бабах!"), 16 | (x, _) if x % 2 == 1 => println!("Первое число нечётно"), 17 | _ => println!("Нет корреляции..."), 18 | } 19 | } 20 | ``` 21 | 22 | ### Смотрите также: 23 | 24 | [Tuples](primitives/tuples.html) 25 | -------------------------------------------------------------------------------- /src/flow_control/while.md: -------------------------------------------------------------------------------- 1 | # while 2 | 3 | Ключевое слово `while` используется для создания цикла, который будет выполняться, 4 | пока условие истинно. 5 | 6 | Давайте напишем печально известный [FizzBuzz][fizzbuzz] используя цикл `while`. 7 | 8 | ```rust,editable 9 | fn main() { 10 | // Переменная счётчик 11 | let mut n = 1; 12 | 13 | // Цикл while будет работать, пока `n` меньше 101 14 | while n < 101 { 15 | if n % 15 == 0 { 16 | println!("fizzbuzz"); 17 | } else if n % 3 == 0 { 18 | println!("fizz"); 19 | } else if n % 5 == 0 { 20 | println!("buzz"); 21 | } else { 22 | println!("{}", n); 23 | } 24 | 25 | // Увеличиваем значение счётчика 26 | n += 1; 27 | } 28 | } 29 | ``` 30 | 31 | [fizzbuzz]: https://en.wikipedia.org/wiki/Fizz_buzz 32 | -------------------------------------------------------------------------------- /src/flow_control/while_let.md: -------------------------------------------------------------------------------- 1 | # while let 2 | 3 | Так же, как и`if let`, `while let` может сделать неудобный `match` 4 | более терпимым. Рассмотрим следующий пример, в котором мы увеличиваем значение `i`: 5 | 6 | ```rust 7 | // Создадим переменную `optional` с типом `Option` 8 | let mut optional = Some(0); 9 | 10 | // Неоднократно повторим наш тест. 11 | loop { 12 | match optional { 13 | // Если `optional` деструктурируется, выполним следующий блок. 14 | Some(i) => { 15 | if i > 9 { 16 | println!("Больше 9, уходим отсюда!"); 17 | optional = None; 18 | } else { 19 | println!("`i` равен `{:?}`. Попробуем еще раз.", i); 20 | optional = Some(i + 1); 21 | } 22 | // ^ Требует 3 уровня вложенности! 23 | }, 24 | // Выходим из цикла в случаи ошибки деструктуризации: 25 | _ => { break; } 26 | // ^ Зачем это нужно? Должен быть способ сделать это лучше! 27 | } 28 | } 29 | ``` 30 | 31 | Использование `while let` делает этот пример немного приятнее: 32 | 33 | ```rust,editable 34 | fn main() { 35 | // Создадим переменную `optional` с типом `Option` 36 | let mut optional = Some(0); 37 | 38 | // Это можно прочитать так: "Пока `let` деструктурирует `optional` в 39 | // `Some(i)`, выполняем блок (`{}`). В противном случае `break`. 40 | while let Some(i) = optional { 41 | if i > 9 { 42 | println!("Больше 9, уходим отсюда!"); 43 | optional = None; 44 | } else { 45 | println!("`i` равен `{:?}`. Попробуем ещё раз.", i); 46 | optional = Some(i + 1); 47 | } 48 | // ^ Меньше смещаемся вправо, к тому же 49 | // нет необходимости обрабатывать ошибки. 50 | } 51 | // ^ К `if let` можно добавить дополнительный блок `else`/`else if` 52 | // `while let` подобного нет. 53 | } 54 | ``` 55 | 56 | ### Смотрите также: 57 | 58 | [`enum`][enum], [`Option`][option], and the [RFC][while_let_rfc] 59 | 60 | [enum]: custom_types/enum.html 61 | [option]: std/option.html 62 | [while_let_rfc]: https://github.com/rust-lang/rfcs/pull/214 63 | -------------------------------------------------------------------------------- /src/fn.md: -------------------------------------------------------------------------------- 1 | # Функции 2 | 3 | Функции объявляются с помощью ключевого слова `fn`. Их аргументы имеют явно заданный тип, 4 | как у переменных, и, если функция возвращает значение, 5 | возвращаемый тип должен быть указан после стрелки `->`. 6 | 7 | Последнее выражение в функции будет использовано как возвращаемое значение. 8 | Так же можно использовать оператор `return`, чтобы вернуть значение из функции раньше, 9 | даже из цикла или оператора `if`. 10 | 11 | Давайте перепишем FizzBuzz используя функции! 12 | 13 | ```rust,editable 14 | // В отличие от С/С++, нет никаких ограничений касаемо порядка определений функций 15 | fn main() { 16 | // Можно использовать функцию здесь, а определить где-нибудь потом 17 | fizzbuzz_to(100); 18 | } 19 | 20 | // Функция, возвращающая логическое значение 21 | fn is_divisible_by(lhs: u32, rhs: u32) -> bool { 22 | // Граничный случай, ранний возврат 23 | if rhs == 0 { 24 | return false; 25 | } 26 | 27 | // Это - выражение, ключевое слово `return` здесь не требуется 28 | lhs % rhs == 0 29 | } 30 | 31 | // Функция, которая «не возвращает» значение, на самом деле возвращает единичный тип `()` 32 | fn fizzbuzz(n: u32) -> () { 33 | if is_divisible_by(n, 15) { 34 | println!("fizzbuzz"); 35 | } else if is_divisible_by(n, 3) { 36 | println!("fizz"); 37 | } else if is_divisible_by(n, 5) { 38 | println!("buzz"); 39 | } else { 40 | println!("{}", n); 41 | } 42 | } 43 | 44 | // Когда функция возвращает `()`, возвращаемый тип можно не указывать 45 | fn fizzbuzz_to(n: u32) { 46 | for n in 1..n + 1 { 47 | fizzbuzz(n); 48 | } 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /src/fn/closures.md: -------------------------------------------------------------------------------- 1 | # Замыкания 2 | 3 | Замыкания в Rust, так же называемые лямбда, это функции, 4 | которые замыкают своё окружение. 5 | Для примера, замыкание, которое захватывает значение переменной x: 6 | 7 | ```rust,ignore 8 | |val| val + x 9 | ``` 10 | 11 | Синтаксис и возможности замыканий делают их очень удобными 12 | для использования "на лету". Использование замыканий похоже на использование функций. 13 | Однако, тип входных и возвращаемых значений *может* быть выведен, а 14 | название аргумента *должно* быть указано. 15 | 16 | Другие характеристики замыканий включают в себя: 17 | * использование `||` вместо `()` для аргументов. 18 | * опциональное ограничения тела функции (`{}`) для одного выражения 19 | (в противном случае обязательно). 20 | * возможность захвата переменных за пределами окружения 21 | 22 | ```rust,editable 23 | fn main() { 24 | // Инкремент с помощью замыкания и функции. 25 | fn function (i: i32) -> i32 { i + 1 } 26 | 27 | // Замыкания анонимны. Тут мы связываем их с ссылками 28 | // Аннотация идентичны аннотации типов функции, но является опциональной 29 | // как и оборачивания тела в `{}`. Эти безымянные функции 30 | // назначены соответствующе названным переменным. 31 | let closure_annotated = |i: i32| -> i32 { i + 1 }; 32 | let closure_inferred = |i | i + 1 ; 33 | 34 | let i = 1; 35 | // Вызов функции и замыкания. 36 | println!("функция: {}", function(i)); 37 | println!("замыкание с указанием типа: {}", closure_annotated(i)); 38 | println!("замыкание с выводом типа: {}", closure_inferred(i)); 39 | 40 | // Замыкание не принимает аргументов, но возвращает `i32`. 41 | // Тип возвращаемого значения выведен автоматически. 42 | let one = || 1; 43 | println!("замыкание, возвращающее один: {}", one()); 44 | 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /src/fn/closures/anonymity.md: -------------------------------------------------------------------------------- 1 | # Анонимность типов 2 | 3 | Замыкания временно захватывают переменные из окружающих областей видимости. 4 | Имеет ли это какие-либо последствия? Конечно. Как видите, использование 5 | замыкания в аргументах функции требует [обобщённых типов](generics.html) из-за 6 | особенностей реализации замыканий: 7 | 8 | ```rust 9 | // `F` должен быть обобщённым типом. 10 | fn apply(f: F) where 11 | F: FnOnce() { 12 | f(); 13 | } 14 | ``` 15 | 16 | Во время определения замыкания компилятор неявно создаёт новую анонимную 17 | структуру для хранения захваченных переменных, тем временем реализуя 18 | функциональность для некого неизвестного типа с помощью одного из типажей: `Fn`, 19 | `FnMut`, или `FnOnce`. Этот тип присваивается переменной, которая хранится до 20 | самого вызова замыкания. 21 | 22 | Так как этот новый тип заранее неизвестен, любое его использование в функции 23 | потребует обобщённых типов. Тем не менее, неограниченный параметр типа `` 24 | по прежнему будет неоднозначным и недопустим. Таким образом, ограничение по 25 | одному из типажей: `Fn`, `FnMut`, или `FnOnce` (которые он реализует) необходимо 26 | для использования этого типа. 27 | 28 | ```rust,editable 29 | // `F` должен реализовать `Fn` для замыкания, которое 30 | // ничего не принимает и не возвращает - именно то, 31 | // что нужно для `print`. 32 | fn apply(f: F) where 33 | F: Fn() { 34 | f(); 35 | } 36 | 37 | fn main() { 38 | let x = 7; 39 | 40 | // Захватываем `x` в анонимный тип и реализуем 41 | // `Fn` для него. Сохраняем его как `print`. 42 | let print = || println!("{}", x); 43 | 44 | apply(print); 45 | } 46 | ``` 47 | 48 | ### Смотрите также: 49 | 50 | [Подробный разбор](https://huonw.github.io/blog/2015/05/finding-closure-in-rust/), [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), 51 | и [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) 52 | -------------------------------------------------------------------------------- /src/fn/closures/closure_examples.md: -------------------------------------------------------------------------------- 1 | # Примеры из библиотеки `std` 2 | 3 | Этот раздел содержит несколько примеров использования замыканий из библиотеки `std`. -------------------------------------------------------------------------------- /src/fn/closures/closure_examples/iter_any.md: -------------------------------------------------------------------------------- 1 | # Iterator::any 2 | 3 | `Iterator::any` - это функция, которая принимает итератор и возвращает `true`, 4 | если любой элемент удовлетворяет предикату. Иначе возвращает `false`. Её 5 | объявление: 6 | 7 | ```rust,ignore 8 | pub trait Iterator { 9 | // Тип, по которому выполняется итерирование 10 | type Item; 11 | 12 | // `any` принимает `&mut self`, что означает заимствование 13 | // и изменение, но не поглощение `self`. 14 | fn any(&mut self, f: F) -> bool where 15 | // `FnMut` означает, что любая захваченная переменная 16 | // может быть изменена, но не поглощена. `Self::Item` 17 | // указывает на захват аргументов замыкания по значению. 18 | F: FnMut(Self::Item) -> bool {} 19 | } 20 | ``` 21 | 22 | ```rust,editable 23 | fn main() { 24 | let vec1 = vec![1, 2, 3]; 25 | let vec2 = vec![4, 5, 6]; 26 | 27 | // `iter()` для векторов даёт `&i32`. Приводим к `i32`. 28 | println!("2 в vec1: {}", vec1.iter() .any(|&x| x == 2)); 29 | // `into_iter()` для векторов даёт `i32`. Приведения не требуется. 30 | println!("2 в vec2: {}", vec2.into_iter().any(| x| x == 2)); 31 | 32 | let array1 = [1, 2, 3]; 33 | let array2 = [4, 5, 6]; 34 | 35 | // `iter()` для массивов даёт `&i32`. 36 | println!("2 в array1: {}", array1.iter() .any(|&x| x == 2)); 37 | // `into_iter()` для массивов неожиданно даёт `&i32`. 38 | println!("2 в array2: {}", array2.into_iter().any(|&x| x == 2)); 39 | } 40 | ``` 41 | 42 | ### Смотрите также: 43 | 44 | [`std::iter::Iterator::any`][any] 45 | 46 | [any]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any 47 | -------------------------------------------------------------------------------- /src/fn/closures/closure_examples/iter_find.md: -------------------------------------------------------------------------------- 1 | # Iterator::find 2 | 3 | `Iterator::find` - это функция, которая принимает итератор и возвращает первый 4 | элемент, который удовлетворяет предикату, в виде `Option`. Её объявление: 5 | 6 | ```rust,ignore 7 | pub trait Iterator { 8 | // Тип, по которому выполняется итерирование 9 | type Item; 10 | 11 | // `find` принимает `&mut self`, что означает заимствование и 12 | // изменение, но не поглощение `self`. 13 | fn find

(&mut self, predicate: P) -> Option where 14 | // `FnMut` означает, что любая захваченная переменная 15 | // может быть изменена, но не поглощена. `&Self::Item` 16 | // указывает на захват аргументов замыкания по ссылке. 17 | P: FnMut(&Self::Item) -> bool {} 18 | } 19 | ``` 20 | 21 | ```rust,editable 22 | fn main() { 23 | let vec1 = vec![1, 2, 3]; 24 | let vec2 = vec![4, 5, 6]; 25 | 26 | // `iter()` для векторов даёт `&i32`. 27 | let mut iter = vec1.iter(); 28 | // `into_iter()` для векторов даёт `i32`. 29 | let mut into_iter = vec2.into_iter(); 30 | 31 | // Ссылка на это даёт `&&i32`. Приводим к `i32`. 32 | println!("Найти 2 в vec1: {:?}", iter .find(|&&x| x == 2)); 33 | // Ссылка на это даёт `&i32`. Приводим к `i32`. 34 | println!("Найти 2 в vec2: {:?}", into_iter.find(| &x| x == 2)); 35 | 36 | let array1 = [1, 2, 3]; 37 | let array2 = [4, 5, 6]; 38 | 39 | // `iter()` для массивов даёт `&i32` 40 | println!("Найти 2 в array1: {:?}", array1.iter() .find(|&&x| x == 2)); 41 | // `into_iter()` для массивов неожиданно даёт `&i32` 42 | println!("Найти 2 в array2: {:?}", array2.into_iter().find(|&&x| x == 2)); 43 | } 44 | ``` 45 | 46 | ### Смотрите также: 47 | 48 | [`std::iter::Iterator::find`][find] 49 | 50 | [find]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find 51 | -------------------------------------------------------------------------------- /src/fn/closures/input_functions.md: -------------------------------------------------------------------------------- 1 | # Входные функции 2 | 3 | Так как замыкания могут использоваться в аргументах, вы можете ожидать, что то 4 | же самое можно сказать и про функции. И это действительно так! Если вы 5 | объявляете функцию, принимающую замыкание как аргумент, то любая функция, 6 | удовлетворяющая ограничениям типажа этого замыкания, может быть передана как 7 | аргумент. 8 | 9 | ```rust,editable 10 | // Объявляем функцию, которая принимает обобщённый тип `F`, 11 | // ограниченный типажом `Fn`, и вызывает его. 12 | fn call_me(f: F) { 13 | f(); 14 | } 15 | 16 | // Объявляем функцию-обёртку, удовлетворяющую ограничению `Fn` 17 | fn function() { 18 | println!("Я функция!"); 19 | } 20 | 21 | fn main() { 22 | // Определяем замыкание, удовлетворяющее ограничению `Fn` 23 | let closure = || println!("Я замыкание!"); 24 | 25 | call_me(closure); 26 | call_me(function); 27 | } 28 | ``` 29 | 30 | Стоит отметить, что типажи `Fn`, `FnMut` и `FnOnce` указывают, как 31 | замыкание захватывает переменные из своей области видимости. 32 | 33 | ### Смотрите также: 34 | 35 | [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), и [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) 36 | -------------------------------------------------------------------------------- /src/fn/closures/output_parameters.md: -------------------------------------------------------------------------------- 1 | # Как выходные параметры 2 | 3 | Замыкания могут использоваться как входные параметры, следовательно их возврат в 4 | качестве выходных параметров также должен быть возможен. Однако, это сопряжено 5 | с трудностями из-за того, что Rust в настоящее время поддерживает только возврат 6 | конкретных (не обобщённых) типов. Типы анонимных замыканий, по определению, 7 | неизвестны. И поэтому возвращение замыканий возможно только путём конкретизации 8 | их типов. Это можно реализовать упаковав замыкание. 9 | 10 | Возможные типажи для возвращаемых значений немного отличаются от прежних: 11 | 12 | - `Fn`: как раньше 13 | - `FnMut`: как раньше 14 | - `FnOnce`: здесь присутствуют некоторые неожиданности, поэтому необходим тип[`FnBox`](https://doc.rust-lang.org/std/boxed/trait.FnBox.html), но он нестабилен в настоящее время. В будущем ожидаютсяизменения этой ситуации. 15 | 16 | Помимо этого, должно быть использовано ключевое слово `move`, чтобы 17 | сигнализировать о том, что все переменные захватываются по значению. Это 18 | необходимо, так как любые захваченные по ссылке значения будут удалены после 19 | выхода из функции, оставляя недопустимые ссылки в замыкании. 20 | 21 | ```rust,editable 22 | fn create_fn() -> Box { 23 | let text = "Fn".to_owned(); 24 | 25 | Box::new(move || println!("Это a: {}", text)) 26 | } 27 | 28 | fn create_fnmut() -> Box { 29 | let text = "FnMut".to_owned(); 30 | 31 | Box::new(move || println!("Это a: {}", text)) 32 | } 33 | 34 | fn main() { 35 | let fn_plain = create_fn(); 36 | let mut fn_mut = create_fnmut(); 37 | 38 | fn_plain(); 39 | fn_mut(); 40 | } 41 | ``` 42 | 43 | ### Смотрите также: 44 | 45 | [Упаковка](../../std/box.md), [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html), [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html), и [Обобщения](../../generics.md). 46 | -------------------------------------------------------------------------------- /src/fn/diverging.md: -------------------------------------------------------------------------------- 1 | # Расходящиеся функции 2 | 3 | Расходящиеся функции никогда не возвращают результат. Они помечены с помощью `!`, который является пустым типом. 4 | 5 | ```rust 6 | fn foo() -> ! { 7 | panic!("Этот вызов никогда не вернёт управление."); 8 | } 9 | ``` 10 | 11 | В отличие от всех других типов, этот не может быть создан, потому что 12 | набор всех возможных значений этого типа пуст. Обратите 13 | внимание, что он отличается от типа `()`, который 14 | имеет ровно одно возможное значение. 15 | 16 | Например, эта функция имеет возвращаемое значение, хотя о нём 17 | нет информации. 18 | 19 | ```rust 20 | fn some_fn() { 21 | () 22 | } 23 | 24 | fn main() { 25 | let a: () = some_fn(); 26 | println!("Эта функция возвращает управление и вы можете увидеть эту строку.") 27 | } 28 | ``` 29 | 30 | В отличие от этой функции, которая никогда не вернёт элемент управления вызывающей стороне. 31 | 32 | ```rust,ignore 33 | #![feature(never_type)] 34 | 35 | fn main() { 36 | let x: ! = panic!("Этот вызов никогда не вернёт управление."); 37 | println!("вы никогда не увидете эту строку!"); 38 | } 39 | ``` 40 | 41 | Хотя это может показаться абстрактным понятием, на самом деле 42 | это очень полезно и может пригодится. Главное преимущество 43 | этого типа в том, что его можно привести к любому другому типу и 44 | поэтому используется в местах, где требуется точный тип, 45 | например в ветвях `match`. Это позволяет нам писать 46 | такой код: 47 | 48 | ```rust 49 | fn main() { 50 | fn sum_odd_numbers(up_to: u32) -> u32 { 51 | let mut acc = 0; 52 | for i in 0..up_to { 53 | // Обратите внимание, что возвращаемый тип этого выражения match должен быть u32 54 | // потому что такой тип в переменной "addition" . 55 | let addition: u32 = match i%2 == 1 { 56 | // Переменная "i" типа u32, что совершенно нормально. 57 | true => i, 58 | // С другой стороны выражение "continue" не возвращает 59 | // u32, но это тоже нормально, потому что это тип не возвращающий управление, 60 | // не нарушает требования к типу выражения match. 61 | false => continue, 62 | }; 63 | acc += addition; 64 | } 65 | acc 66 | } 67 | println!("Сумма нечётных чисел до 9 (исключая): {}", sum_odd_numbers(9)); 68 | } 69 | ``` 70 | 71 | Это также возвращаемый тип функций, которые содержат вечный 72 | цикл (например, `loop {}`), как сетевые серверы или 73 | функции, завершающие процесс (например, `exit()`). 74 | -------------------------------------------------------------------------------- /src/fn/hof.md: -------------------------------------------------------------------------------- 1 | # Функции высшего порядка 2 | 3 | Rust предоставляет Функции Высшего Порядка (ФВП). Это функции, 4 | которые получают на вход одну или больше функций и 5 | производят более полезные функции. 6 | ФВП и ленивые итераторы дают языку Rust функциональный вид. 7 | 8 | ```rust,editable 9 | fn is_odd(n: u32) -> bool { 10 | n % 2 == 1 11 | } 12 | 13 | fn main() { 14 | println!("Найти сумму всех квадратов нечётных чисел не больше 1000"); 15 | let upper = 1000; 16 | 17 | // Императивный подход 18 | // Объявляем переменную-накопитель 19 | let mut acc = 0; 20 | // Итерировать: 0, 1, 2, ... до бесконечности 21 | for n in 0.. { 22 | // Квадрат числа 23 | let n_squared = n * n; 24 | 25 | if n_squared >= upper { 26 | // Остановить цикл, если превысили верхний лимит 27 | break; 28 | } else if is_odd(n_squared) { 29 | // Складывать число, если оно нечётное 30 | acc += n_squared; 31 | } 32 | } 33 | println!("императивный стиль: {}", acc); 34 | 35 | // Функциональный подход 36 | let sum_of_squared_odd_numbers: u32 = 37 | (0..).map(|n| n * n) // Все натуральные числа возводим в квадрат 38 | .take_while(|&n| n < upper) // Ниже верхнего предела 39 | .filter(|n| is_odd(*n)) // Выбираем нечётные 40 | .fold(0, |sum, i| sum + i); // Суммируем 41 | println!("функциональный стиль: {}", sum_of_squared_odd_numbers); 42 | } 43 | ``` 44 | 45 | [Option](https://doc.rust-lang.org/core/option/enum.Option.html) 46 | и 47 | [Iterator](https://doc.rust-lang.org/core/iter/trait.Iterator.html) 48 | реализуют свою часть функций высшего порядка.. 49 | -------------------------------------------------------------------------------- /src/generics/assoc_items.md: -------------------------------------------------------------------------------- 1 | # Ассоциированные элементы 2 | 3 | "Ассоциированные элементы" относятся к набору правил, 4 | касающихся элементов различных типов. Это расширение для 5 | обобщённых типажей, которое позволяет им определить новый 6 | элемент внутри себя. 7 | 8 | Каждый такой элемент называется *ассоциированным типом* 9 | и предоставляет упрощённый шаблон использования, когда 10 | `trait` является обобщённым для своего контейнера. 11 | 12 | ### Смотрите также: 13 | 14 | [RFC](https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md) 15 | -------------------------------------------------------------------------------- /src/generics/assoc_items/the_problem.md: -------------------------------------------------------------------------------- 1 | # Проблема 2 | 3 | `trait`, являющийся обобщённым для своего контейнера, есть требование к спецификации типа - пользователи `trait` *должны* специфицировать все обобщённые типы. 4 | 5 | В примере ниже, `trait` `Contains` позволяет 6 | использовать обобщённые типы `A` и `B`. 7 | Затем этот типаж реализуется для типа `Container`, в 8 | котором `A` и `B` специфицированы, как 9 | `i32`, чтобы их можно было использовать в функции 10 | `fn difference()`. 11 | 12 | Потому что `Contains` имеет обобщение, мы должны 13 | явно указать *все* обобщённые типы для 14 | `fn difference()`. На практике, мы хотим выразить 15 | `A` и `B` через *входной 16 | параметр* `C`. Как вы можете увидеть в следующем 17 | разделе, ассоциированные типы предоставляют именно эту 18 | возможность. 19 | 20 | ```rust,editable 21 | struct Container(i32, i32); 22 | 23 | // Типаж, который проверяет, сохранены ли 2 элемента в контейнере. 24 | // Также он может вернуть первое или последнее значение. 25 | trait Contains { 26 | fn contains(&self, _: &A, _: &B) -> bool; // Явно требует `A` и `B`. 27 | fn first(&self) -> i32; // Не требует явного `A` или `B`. 28 | fn last(&self) -> i32; // Не требует явного `A` или `B`. 29 | } 30 | 31 | impl Contains for Container { 32 | // Истина, если сохранённые цифры равны. 33 | fn contains(&self, number_1: &i32, number_2: &i32) -> bool { 34 | (&self.0 == number_1) && (&self.1 == number_2) 35 | } 36 | 37 | // Берём первую цифру. 38 | fn first(&self) -> i32 { self.0 } 39 | 40 | // Берём последнюю цифру. 41 | fn last(&self) -> i32 { self.1 } 42 | } 43 | 44 | // `C` содержит `A` и `B`. В свете этого, необходимость снова явно указывать `A` и 45 | // `B` огорчает. 46 | fn difference(container: &C) -> i32 where 47 | C: Contains { 48 | container.last() - container.first() 49 | } 50 | 51 | fn main() { 52 | let number_1 = 3; 53 | let number_2 = 10; 54 | 55 | let container = Container(number_1, number_2); 56 | 57 | println!("Содержатся ли в контейнере {} и {}? {}", 58 | &number_1, &number_2, 59 | container.contains(&number_1, &number_2)); 60 | println!("Первое число: {}", container.first()); 61 | println!("Последнее число: {}", container.last()); 62 | 63 | println!("Разница: {}", difference(&container)); 64 | } 65 | ``` 66 | 67 | ### Смотрите также: 68 | 69 | [`struct`](../../custom_types/structs.md) и [`trait`](../../trait.md) 70 | -------------------------------------------------------------------------------- /src/generics/bounds/testcase_empty.md: -------------------------------------------------------------------------------- 1 | # Пример: пустые ограничения 2 | 3 | Следствием того, как работают ограничения по типажу, является то, 4 | что даже если `типаж` не включает в себя какие-либо функциональные 5 | возможности, вы все равно можете использовать его в качестве ограничения. 6 | Примерами таких типажей являются `Eq` и `Ord` из стандартной библиотеки. 7 | 8 | ```rust,editable 9 | struct Cardinal; 10 | struct BlueJay; 11 | struct Turkey; 12 | 13 | trait Red {} 14 | trait Blue {} 15 | 16 | impl Red for Cardinal {} 17 | impl Blue for BlueJay {} 18 | 19 | // Эти функции действительны только для типов реализующих эти типажи. 20 | // То, что типажи пусты, не имеет значения. 21 | fn red(_: &T) -> &'static str { "красная" } 22 | fn blue(_: &T) -> &'static str { "синяя" } 23 | 24 | fn main() { 25 | let cardinal = Cardinal; 26 | let blue_jay = BlueJay; 27 | let _turkey = Turkey; 28 | 29 | // `red()` не будет работать для blue_jay, ни наоборот, 30 | // из-за ограничений по типажу. 31 | println!("Кардинал {} птица", red(&cardinal)); 32 | println!("Голубая сойка {} птица", blue(&blue_jay)); 33 | //println!("Индюк {} птица", red(&_turkey)); 34 | // ^ TODO: Попробуйте раскомментировать эту строку. 35 | } 36 | ``` 37 | 38 | ### Смотрите также: 39 | 40 | [`std::cmp::Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html), [`std::cmp::Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) и [`trait`](../../trait.md) 41 | -------------------------------------------------------------------------------- /src/generics/gen_fn.md: -------------------------------------------------------------------------------- 1 | # Функции 2 | 3 | Тот же набор правил применяется и к функциям: тип `T` становится 4 | обобщённым, когда предшествует ``. 5 | 6 | При использовании обобщённых функций, иногда требуется явно указывать тип 7 | данных параметров. Это может быть необходимо в случае, если вызываемая функция возвращает 8 | обобщённый тип или у компилятора недостаточно информации для вывода необходимого 9 | типа данных. 10 | 11 | Вызов функции с явно указанными типами данных параметров выглядит так: 12 | `fun::()`. 13 | 14 | ```rust,editable 15 | struct A; // Конкретный тип `A`. 16 | struct S(A); // Конкретный тип `S`. 17 | struct SGen(T); // Обобщённый тип `SGen`. 18 | 19 | // Все следующие функции становятся владельцем переменной, переданной в них. 20 | // После передачи, она сразу выходит из области видимости и освобождается. 21 | 22 | // Объявляем функцию `reg_fn`, которая принимает аргумент `_s` типа `S`. 23 | // Здесь отсутствует ``, поэтому это не обобщённая функция. 24 | fn reg_fn(_s: S) {} 25 | 26 | // Объявляем функцию `gen_spec_t`, которая принимает аргумент `_s` типа `SGen`. 27 | // В ней явно задан параметр типа `A`, но поскольку `A` не был указан 28 | // как параметр обобщённого типа для `gen_spec_t`, то он не является обобщённым. 29 | fn gen_spec_t(_s: SGen) {} 30 | 31 | // Объявляем функцию `gen_spec_i32`, которая принимает аргумент `_s` типа `SGen`. 32 | // В ней явно задан тип `i32`, который является определённым типом. 33 | // Поскольку `i32` не является обобщённым типом, эта функция 34 | // также не является обобщённой. 35 | fn gen_spec_i32(_s: SGen) {} 36 | 37 | // Объявляем функцию `generic`, которая принимает аргумент `_s` типа `SGen`. 38 | // Поскольку `SGen` предшествует ``, эта функция 39 | // является обобщённой над `T`. 40 | fn generic(_s: SGen) {} 41 | 42 | fn main() { 43 | // Используем не обобщённые функции. 44 | reg_fn(S(A)); // Конкретный тип. 45 | gen_spec_t(SGen(A)); // Неявно определён тип параметра `A`. 46 | gen_spec_i32(SGen(6)); // Неявно определён тип параметра `i32`. 47 | 48 | // Явно определён тип параметра `char` в `generic()`. 49 | generic::(SGen('a')); 50 | 51 | // Неявно определён параметр типа `char` в `generic()`. 52 | generic(SGen('c')); 53 | } 54 | ``` 55 | 56 | ### Смотрите также: 57 | 58 | [Функции][fn] и [структуры][structs] 59 | 60 | [fn]: fn.html 61 | [structs]: custom_types/structs.html 62 | -------------------------------------------------------------------------------- /src/generics/gen_trait.md: -------------------------------------------------------------------------------- 1 | # Типажи 2 | 3 | Конечно `типажи` тоже могут быть обобщёнными. Здесь мы определяем, тот 4 | который повторно реализует `типаж` `Drop` как обобщённый метод, чтобы 5 | удалить себя и входные данные. 6 | 7 | ```rust,editable 8 | // Некопируемые типы. 9 | struct Empty; 10 | struct Null; 11 | 12 | // Обобщённый типаж от `T`. 13 | trait DoubleDrop { 14 | // Определим метод для типа вызывающего объекта, 15 | // который принимает один дополнительный параметр `T` и ничего с ним не делает. 16 | fn double_drop(self, _: T); 17 | } 18 | 19 | // Реализация `DoubleDrop` для любого общего параметра `T` и 20 | // вызывающего объекта `U`. 21 | impl DoubleDrop for U { 22 | // Этот метод получает право владения на оба переданных аргумента и 23 | // освобождает их. 24 | fn double_drop(self, _: T) {} 25 | } 26 | 27 | fn main() { 28 | let empty = Empty; 29 | let null = Null; 30 | 31 | // Освободить `empty` и `null`. 32 | empty.double_drop(null); 33 | 34 | //empty; 35 | //null; 36 | // ^ TODO: Попробуйте раскомментировать эти строки. 37 | } 38 | ``` 39 | 40 | ### Смотрите также: 41 | 42 | [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html), [`struct`](../custom_types/structs.md) и [`trait`](../trait.md) 43 | -------------------------------------------------------------------------------- /src/generics/impl.md: -------------------------------------------------------------------------------- 1 | # Реализация 2 | 3 | Подобно функциям, реализации требуют выполнения некоторых условий, чтобы оставаться обобщёнными. 4 | 5 | ```rust 6 | struct S; // Конкретный тип `S` 7 | struct GenericVal(T); // Обобщенный тип `GenericVal` 8 | 9 | // Реализация GenericVal, где мы явно указываем типы данных параметров: 10 | impl GenericVal {} // Указываем тип `f32` 11 | impl GenericVal {} // Указываем тип `S`, который мы определили выше 12 | 13 | // `` должен указываться перед типом, чтобы оставаться обобщённым 14 | impl GenericVal {} 15 | ``` 16 | 17 | ```rust,editable 18 | struct Val { 19 | val: f64, 20 | } 21 | 22 | struct GenVal { 23 | gen_val: T, 24 | } 25 | 26 | // Реализация Val 27 | impl Val { 28 | fn value(&self) -> &f64 { 29 | &self.val 30 | } 31 | } 32 | 33 | // Реализация GenVal для обобщённого типа `T` 34 | impl GenVal { 35 | fn value(&self) -> &T { 36 | &self.gen_val 37 | } 38 | } 39 | 40 | fn main() { 41 | let x = Val { val: 3.0 }; 42 | let y = GenVal { gen_val: 3i32 }; 43 | 44 | println!("{}, {}", x.value(), y.value()); 45 | } 46 | ``` 47 | 48 | ### Смотрите также: 49 | 50 | [Функции, возвращающие ссылки](../scope/lifetime/fn.md), [`impl`](../fn/methods.md) и [`struct`](../custom_types/structs.md) 51 | -------------------------------------------------------------------------------- /src/generics/multi_bounds.md: -------------------------------------------------------------------------------- 1 | # Множественные ограничения 2 | 3 | Множественные ограничения по типажу могут быть применены с помощью `+`. 4 | Разные типы разделяются с помощью `,`. 5 | 6 | ```rust,editable 7 | use std::fmt::{Debug, Display}; 8 | 9 | fn compare_prints(t: &T) { 10 | println!("Debug: `{:?}`", t); 11 | println!("Display: `{}`", t); 12 | } 13 | 14 | fn compare_types(t: &T, u: &U) { 15 | println!("t: `{:?}", t); 16 | println!("u: `{:?}", u); 17 | } 18 | 19 | fn main() { 20 | let string = "words"; 21 | let array = [1, 2, 3]; 22 | let vec = vec![1, 2, 3]; 23 | 24 | compare_prints(&string); 25 | //compare_prints(&array); 26 | // ЗАДАНИЕ ^ Попробуйте удалить комментарий. 27 | 28 | compare_types(&array, &vec); 29 | } 30 | ``` 31 | 32 | ### Смотрите также: 33 | 34 | [`std::fmt`](../hello/print.md) и [`trait`](../trait.md) 35 | -------------------------------------------------------------------------------- /src/generics/new_types.md: -------------------------------------------------------------------------------- 1 | # New Type идиома 2 | 3 | Идиома `newtype` гарантирует во время компиляции, 4 | что программе передаётся значение правильного типа. 5 | 6 | Например, функция верификации возраста, которая проверяет 7 | возраст в годах *должна* получать значение типа 8 | `Years`. 9 | 10 | ```rust, 11 | struct Years(i64); 12 | 13 | struct Days(i64); 14 | 15 | impl Years { 16 | pub fn to_days(&self) -> Days { 17 | Days(self.0 * 365) 18 | } 19 | } 20 | 21 | 22 | impl Days { 23 | /// truncates partial years 24 | pub fn to_years(&self) -> Years { 25 | Years(self.0 / 365) 26 | } 27 | } 28 | 29 | fn old_enough(age: &Years) -> bool { 30 | age.0 >= 18 31 | } 32 | 33 | fn main() { 34 | let age = Years(5); 35 | let age_days = age.to_days(); 36 | println!("Old enough {}", old_enough(&age)); 37 | println!("Old enough {}", old_enough(&age_days.to_years())); 38 | // println!("Old enough {}", old_enough(&age_days)); 39 | } 40 | ``` 41 | 42 | Удалите комментарий с последнего `println`, чтобы увидеть, что тип 43 | должен быть `Years`. 44 | 45 | Чтобы получить из `newtype`-переменной значение 46 | базового типа, вы можете использовать кортежный синтаксис, 47 | как в примере: 48 | 49 | ```rust, 50 | struct Years(i64); 51 | 52 | fn main() { 53 | let years = Years(42); 54 | let years_as_primitive: i64 = years.0; 55 | } 56 | ``` 57 | 58 | ### Смотрите также: 59 | 60 | [`struct`](../custom_types/structs.md) 61 | -------------------------------------------------------------------------------- /src/generics/phantom.md: -------------------------------------------------------------------------------- 1 | # PhantomData-параметры 2 | 3 | Параметры фантомного типа - единственное, что не отображается во 4 | время выполнения, но проверяется статически (и только статически) 5 | во время компиляции. 6 | 7 | Типы данных могут использовать дополнительные обобщённые типы 8 | в качестве параметров-маркеров или для выполнения проверки 9 | типов во время компиляции. Эти дополнительные параметры не 10 | сохраняют значения и не имеют поведения во время выполнения. 11 | 12 | В следующем примере мы совместили [std::marker::PhantomData](https://doc.rust-lang.org/std/marker/struct.PhantomData.html) и концепцию параметров фантомных типов для создания кортежей разных типов. 13 | 14 | ```rust,editable 15 | use std::marker::PhantomData; 16 | 17 | // Фантомная кортежная структура, которая имеет обобщение `A` со скрытым параметром `B`. 18 | #[derive(PartialEq)] // Разрешаем для данного типа сравнения. 19 | struct PhantomTuple(A,PhantomData); 20 | 21 | // Фантомная структура, которая имеет обобщение `A` со скрытым параметром `B`. 22 | #[derive(PartialEq)] // Разрешаем для данного типа сравнения. 23 | struct PhantomStruct { first: A, phantom: PhantomData } 24 | 25 | // Заметьте: память выделена для обобщённого типа `A`, но не для `B`. 26 | // Следовательно, `B` не может быть использована в вычислениях. 27 | 28 | fn main() { 29 | // Здесь `f32` и `f64` - скрытые параметры. 30 | // Тип PhantomTuple объявлен с ``. 31 | let _tuple1: PhantomTuple = PhantomTuple('Q', PhantomData); 32 | // Тип PhantomTuple объявлен с ``. 33 | let _tuple2: PhantomTuple = PhantomTuple('Q', PhantomData); 34 | 35 | // Тип определён как ``. 36 | let _struct1: PhantomStruct = PhantomStruct { 37 | first: 'Q', 38 | phantom: PhantomData, 39 | }; 40 | // Тип определён как ``. 41 | let _struct2: PhantomStruct = PhantomStruct { 42 | first: 'Q', 43 | phantom: PhantomData, 44 | }; 45 | 46 | // Ошибка времени компиляции! Типы не совпадают, так что сравнение не может быть произведено: 47 | //println!("_tuple1 == _tuple2 даёт в результате: {}", 48 | // _tuple1 == _tuple2); 49 | 50 | // Ошибка времени компиляции! Типы не совпадают, так что сравнение не может быть произведено: 51 | //println!("_struct1 == _struct2 даёт в результате: {}", 52 | // _struct1 == _struct2); 53 | } 54 | ``` 55 | 56 | ### Смотрите также: 57 | 58 | [`derive`](../trait/derive.md), [`struct`](../custom_types/structs.md) и [кортежные структуры](../custom_types/structs.md) 59 | -------------------------------------------------------------------------------- /src/generics/where.md: -------------------------------------------------------------------------------- 1 | # Утверждения where 2 | 3 | Ограничение типажа также может быть выражено с помощью утверждения `where` 4 | непосредственно перед открытием `{`, а не при первом упоминании типа. 5 | Кроме того, утверждения `where` могут применять ограничения типажей к 6 | произвольным типам, а не только к параметрам типа. 7 | 8 | В некоторых случаях утверждение `where` является полезным: 9 | 10 | * При указании обобщённых типов и ограничений типажей отдельно, 11 | код становится более ясным: 12 | 13 | ```rust,ignore 14 | impl MyTrait for YourType {} 15 | 16 | // Выражение ограничений типажей через утверждение `where` 17 | impl MyTrait for YourType where 18 | A: TraitB + TraitC, 19 | D: TraitE + TraitF {} 20 | ``` 21 | 22 | * Использование утверждения `where` более выразительно, чем использование 23 | обычного синтаксиса. В этом примере `impl` не может быть непосредственно 24 | выражен без утверждения `where`: 25 | 26 | ```rust,editable 27 | use std::fmt::Debug; 28 | 29 | trait PrintInOption { 30 | fn print_in_option(self); 31 | } 32 | 33 | // Потому что в противном случае мы должны были бы выразить это как 34 | // `T: Debug` или использовать другой метод косвенного подхода, 35 | // для этого требуется утверждение `where`: 36 | impl PrintInOption for T where 37 | Option: Debug { 38 | // Мы хотим использовать `Option: Debug` как наше ограничение 39 | // типажа, потому то это то, что будет напечатано. В противном случае 40 | // использовалось бы неправильное ограничение типажа. 41 | fn print_in_option(self) { 42 | println!("{:?}", Some(self)); 43 | } 44 | } 45 | 46 | fn main() { 47 | let vec = vec![1, 2, 3]; 48 | 49 | vec.print_in_option(); 50 | } 51 | ``` 52 | 53 | ### Смотрите также: 54 | 55 | [RFC][where], [`структуры`][struct], и [`типажи`][trait] 56 | 57 | [struct]: custom_types/structs.html 58 | [trait]: trait.html 59 | [where]: https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md 60 | -------------------------------------------------------------------------------- /src/hello.md: -------------------------------------------------------------------------------- 1 | # Привет мир 2 | 3 | Это исходный код традиционной программы "Привет, мир!". 4 | 5 | ```rust,editable 6 | // Эта строка является комментарием и она будет проигнорирована компилятором 7 | // Протестировать код можно нажав на кнопку "Run" вот тут -> 8 | // так же можно использовать клавиатуру, нажав сочетание клавиш "Ctrl + Enter" 9 | 10 | // Этот код можно редактировать не стесняясь, дерзайте! 11 | // Всегда можно вернуть оригинальный код, нажав на кнопку "Reset" вот тут -> 12 | 13 | // Это главная функция. С неё начинается исполнение любой программы 14 | fn main() { 15 | // Следующий код будет исполнен в момент, когда будет запущен исполняемый файл 16 | 17 | // Напечатаем текст в консоли 18 | println!("Привет, мир!"); 19 | } 20 | ``` 21 | 22 | `println!` - это [*макрос*][macros], который отображает текст в консоли. 23 | 24 | Исполняемый файл может быть сгенерирован с помощью компилятора Rust - `rustc`. 25 | 26 | ```sh 27 | $ rustc hello.rs 28 | ``` 29 | 30 | `rustc` создаст исполняемый файл `hello`, который можно будет запустить. 31 | 32 | ```sh 33 | $ ./hello 34 | Привет, мир! 35 | ``` 36 | 37 | ### Задание 38 | 39 | Нажми кнопку 'Run', чтобы увидеть ожидаемый результат. 40 | Затем, добавь новую строку с другим макросом `println!`, чтобы вывод был таким: 41 | 42 | ```log 43 | Привет, мир! 44 | Я программирую на языке Rust! 45 | ``` 46 | 47 | [macros]: macros.html 48 | -------------------------------------------------------------------------------- /src/hello/comment.md: -------------------------------------------------------------------------------- 1 | # Комментарии 2 | 3 | Каждая программа, безусловно, нуждается в комментариях и 4 | Rust предоставляет несколько способов комментирования кода: 5 | 6 | - *Обычные комментарии*, которые игнорируются компилятором: 7 | - `// Однострочный комментарий. Который завершается в конце строки.` 8 | - `/* Блочный комментарий, который продолжается до завершающего символа. */` 9 | - *Doc комментарии*, которые будут сгенерированы в HTML[документацию](../meta/doc.md): 10 | - `/// Генерация документации для функции.` 11 | - `//! Генерация документации для модуля.` 12 | 13 | ```rust,editable 14 | fn main() { 15 | // This is an example of a line comment 16 | // There are two slashes at the beginning of the line 17 | // And nothing written inside these will be read by the compiler 18 | 19 | // println!("Hello, world!"); 20 | 21 | // Run it. See? Now try deleting the two slashes, and run it again. 22 | 23 | /* 24 | * This is another type of comment, a block comment. In general, 25 | * line comments are the recommended comment style. But 26 | * block comments are extremely useful for temporarily disabling 27 | * chunks of code. /* Block comments can be /* nested, */ */ 28 | * so it takes only a few keystrokes to comment out everything 29 | * in this main() function. /*/*/* Try it yourself! */*/*/ 30 | */ 31 | 32 | /* 33 | Note: The previous column of `*` was entirely for style. There's 34 | no actual need for it. 35 | */ 36 | 37 | // You can manipulate expressions more easily with block comments 38 | // than with line comments. Try deleting the comment delimiters 39 | // to change the result: 40 | let x = 5 + /* 90 + */ 5; 41 | println!("Is `x` 10 or 100? x = {}", x); 42 | } 43 | 44 | ``` 45 | 46 | ### Смотрите также: 47 | 48 | [Документирование библиотек](../meta/doc.md) 49 | -------------------------------------------------------------------------------- /src/macros.md: -------------------------------------------------------------------------------- 1 | # macro_rules! 2 | 3 | Rust предоставляет мощную систему макросов, которая позволяет 4 | использовать метапрограммирование. Как вы могли видеть в предыдущих главах, 5 | макросы выглядят как функции, но их имя заканчивается восклицательным знаком (`!`). 6 | Вместо вызова функции, макросы расширяются в исходный код, который впоследствии 7 | компилируется с остальной частью программы. 8 | Однако, в отличие от макросов на C и других языках, макросы Rust расширяются 9 | в абстрактные синтаксические деревья, а не в подстановку строк, 10 | поэтому Вы не получаете неожиданных ошибок приоритета операций. 11 | 12 | Макросы создаются с помощью макроса `macro_rules!` 13 | 14 | ```rust,editable 15 | // Этот простой макрос называется `say_hello`. 16 | macro_rules! say_hello { 17 | // `()` указывает, что макрос не принимает аргументов. 18 | () => ( 19 | // Макрос будет раскрываться с содержимым этого блока. 20 | println!("Hello!"); 21 | ) 22 | } 23 | 24 | fn main() { 25 | // Этот вызов будет раскрыт в код `println!("Hello");` 26 | say_hello!() 27 | } 28 | ``` 29 | 30 | Так почему же макросы полезны? 31 | 32 | 1. Не повторяйтесь. Есть много случаев, когда вам может понадобиться подобная 33 | функциональность в нескольких местах, но с различными типами. Чаще всего написание 34 | макроса - это полезный способ избежать повторения кода. (Подробнее об этом позже) 35 | 36 | 2. Предметно-ориентированные языки. Макросы позволяют определить специальный синтаксис для 37 | конкретной цели. (Подробнее об этом позже) 38 | 39 | 3. Вариативные интерфейсы. Иногда требуется определить интерфейс, который 40 | имеет переменное количество аргументов. Пример: `println!`, который может принять любое 41 | количество аргументов в зависимости от строки формата. (Подробнее об этом позже) 42 | -------------------------------------------------------------------------------- /src/macros/designators.md: -------------------------------------------------------------------------------- 1 | # Указатели 2 | 3 | Аргументы макроса имеют префикс знака доллара `$` и тип аннотируется 4 | с помощью *указателей фрагмента*: 5 | 6 | ```rust,editable 7 | macro_rules! create_function { 8 | // Этот макрос принимает аргумент идентификатора `ident` и 9 | // создаёт функцию с именем `$func_name`. 10 | // Идентификатор `ident` используют для обозначения имени переменной/функции. 11 | ($func_name:ident) => ( 12 | fn $func_name() { 13 | // Макрос `stringify!` преобразует `ident` в строку. 14 | println!("Вызвана функция {:?}()", 15 | stringify!($func_name)) 16 | } 17 | ) 18 | } 19 | 20 | // Создадим функции с именами `foo` и `bar` используя макрос, указанный выше. 21 | create_function!(foo); 22 | create_function!(bar); 23 | 24 | macro_rules! print_result { 25 | // Этот макрос принимает выражение типа `expr` и напечатает 26 | // его как строку вместе с результатом. 27 | // Указатель `expr` используют для обозначения выражений. 28 | ($expression:expr) => ( 29 | // `stringify!` преобразует выражение в строку *без изменений*. 30 | println!("{:?} = {:?}", 31 | stringify!($expression), 32 | $expression); 33 | ) 34 | } 35 | 36 | fn main() { 37 | foo(); 38 | bar(); 39 | 40 | print_result!(1u32 + 1); 41 | 42 | // Напомним, что блоки тоже являются выражениями! 43 | print_result!({ 44 | let x = 1u32; 45 | 46 | x * x + 2 * x - 1 47 | }); 48 | } 49 | ``` 50 | 51 | Это список всех указателей: 52 | 53 | - `block` 54 | - `expr` используют для обозначения выражений 55 | - `ident` используют для обозначения имени переменной/функции 56 | - `item` 57 | - `literal` используется для литеральных констант 58 | - `pat` (*образец*) 59 | - `path` 60 | - `stmt` (*единственный оператор*) 61 | - `tt` (*единственное дерево лексем*) 62 | - `ty` (*тип*) 63 | - `vis` (*спецификатор видимости*) 64 | 65 | Полный список указателей, вы можете увидеть в [Rust Reference](https://doc.rust-lang.org/reference/macros-by-example.html). 66 | -------------------------------------------------------------------------------- /src/macros/dry.md: -------------------------------------------------------------------------------- 1 | # DRY (Не повторяйся) 2 | 3 | Макросы позволяют писать DRY код, путём разделения общих частей функций 4 | и/или набор тестов. Вот пример, который реализует и тестирует операторы 5 | `+=`, `*=` и `-=` на `Vec`: 6 | 7 | ```rust,editable 8 | use std::ops::{Add, Mul, Sub}; 9 | 10 | macro_rules! assert_equal_len { 11 | // Указатель `tt` (единственное дерево лексем) используют для 12 | // операторов и лексем. 13 | ($a:ident, $b: ident, $func:ident, $op:tt) => ( 14 | assert!($a.len() == $b.len(), 15 | "{:?}: несоответствие размеров: {:?} {:?} {:?}", 16 | stringify!($func), 17 | ($a.len(),), 18 | stringify!($op), 19 | ($b.len(),)); 20 | ) 21 | } 22 | 23 | macro_rules! op { 24 | ($func:ident, $bound:ident, $op:tt, $method:ident) => ( 25 | fn $func + Copy>(xs: &mut Vec, ys: &Vec) { 26 | assert_equal_len!(xs, ys, $func, $op); 27 | 28 | for (x, y) in xs.iter_mut().zip(ys.iter()) { 29 | *x = $bound::$method(*x, *y); 30 | // *x = x.$method(*y); 31 | } 32 | } 33 | ) 34 | } 35 | 36 | // Реализуем функции `add_assign`, `mul_assign`, и `sub_assign`. 37 | op!(add_assign, Add, +=, add); 38 | op!(mul_assign, Mul, *=, mul); 39 | op!(sub_assign, Sub, -=, sub); 40 | 41 | mod test { 42 | use std::iter; 43 | macro_rules! test { 44 | ($func: ident, $x:expr, $y:expr, $z:expr) => { 45 | #[test] 46 | fn $func() { 47 | for size in 0usize..10 { 48 | let mut x: Vec<_> = iter::repeat($x).take(size).collect(); 49 | let y: Vec<_> = iter::repeat($y).take(size).collect(); 50 | let z: Vec<_> = iter::repeat($z).take(size).collect(); 51 | 52 | super::$func(&mut x, &y); 53 | 54 | assert_eq!(x, z); 55 | } 56 | } 57 | } 58 | } 59 | 60 | // Протестируем `add_assign`, `mul_assign` и `sub_assign` 61 | test!(add_assign, 1u32, 2u32, 3u32); 62 | test!(mul_assign, 2u32, 3u32, 6u32); 63 | test!(sub_assign, 3u32, 2u32, 1u32); 64 | } 65 | ``` 66 | 67 | ```bash 68 | $ rustc --test dry.rs && ./dry 69 | running 3 tests 70 | test test::mul_assign ... ok 71 | test test::add_assign ... ok 72 | test test::sub_assign ... ok 73 | 74 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured 75 | ``` 76 | -------------------------------------------------------------------------------- /src/macros/dsl.md: -------------------------------------------------------------------------------- 1 | # Domain Specific Languages (DSLs) 2 | 3 | DSL - это мини язык, встроенный в макросы Rust. Это полностью 4 | допустимый код на Rust, так как система макросов разворачивается 5 | в нормальные конструкции, но выглядит как маленький язык. Это 6 | позволяет вам определять краткий или интуитивный синтаксис для 7 | некоторой функциональности (в пределах границ). 8 | 9 | Предположим, я хочу определить небольшое API для калькулятора. 10 | Я хотел бы предоставить выражение и вывести результат в консоль. 11 | 12 | ```rust,editable 13 | macro_rules! calculate { 14 | (eval $e:expr) => {{ 15 | { 16 | let val: usize = $e; // Заставим быть переменную целым числом. 17 | println!("{} = {}", stringify!{$e}, val); 18 | } 19 | }}; 20 | } 21 | 22 | fn main() { 23 | calculate! { 24 | eval 1 + 2 // хе-хе, `eval` _не_ ключевое слово Rust! 25 | } 26 | 27 | calculate! { 28 | eval (1 + 2) * (3 / 4) 29 | } 30 | } 31 | ``` 32 | 33 | Вывод: 34 | 35 | ```txt 36 | 1 + 2 = 3 37 | (1 + 2) * (3 / 4) = 0 38 | ``` 39 | 40 | Это очень простой пример, но можно разработать и гораздо более 41 | сложные интерфейсы, такие как [`lazy_static`](https://crates.io/crates/lazy_static) 42 | или [`clap`](https://crates.io/crates/clap). 43 | 44 | Также обратите внимание на две пары скобок в макросе. Внешняя 45 | пара является частью синтаксиса `macro_rules!`, в 46 | дополнение к `()` или `[]`. 47 | -------------------------------------------------------------------------------- /src/macros/overload.md: -------------------------------------------------------------------------------- 1 | # Перегрузка 2 | 3 | Макросы могут быть перегружены, принимая различные комбинации аргументов. 4 | В этом плане, `macro_rules!` может работать аналогично блоку сопоставления (match): 5 | 6 | ```rust,editable 7 | // `test!` будет сравнивать `$left` и `$right` 8 | // по разному, в зависимости от того, как вы объявите их: 9 | macro_rules! test { 10 | // Не нужно разделять аргументы запятой. 11 | // Можно использовать любой шаблон! 12 | ($left:expr; and $right:expr) => ( 13 | println!("{:?} и {:?} это {:?}", 14 | stringify!($left), 15 | stringify!($right), 16 | $left && $right) 17 | ); 18 | // ^ каждый блок должен заканчиваться точкой с запятой. 19 | ($left:expr; or $right:expr) => ( 20 | println!("{:?} или {:?} это {:?}", 21 | stringify!($left), 22 | stringify!($right), 23 | $left || $right) 24 | ); 25 | } 26 | 27 | fn main() { 28 | test!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32); 29 | test!(true; or false); 30 | } 31 | ``` -------------------------------------------------------------------------------- /src/macros/repeat.md: -------------------------------------------------------------------------------- 1 | # Повторение 2 | 3 | Макросы могут использовать знак `+` в списке аргументов, чтобы указать, какие аргументы 4 | могут повторяться хоть один раз, или знак `*`, чтобы указать, какие аргументы могут 5 | повторяться ноль или несколько раз. 6 | 7 | В следующем примере, шаблон, окружённый `$(...),+` будет 8 | сопоставлять одно или несколько выражений, разделённых запятыми. 9 | Также обратите внимание, что точка с запятой является 10 | необязательной в последнем случае. 11 | 12 | ```rust,editable 13 | // `min!` посчитает минимальное число аргументов. 14 | macro_rules! find_min { 15 | // Простой вариант: 16 | ($x:expr) => ($x); 17 | // `$x` следует хотя бы одному `$y,` 18 | ($x:expr, $($y:expr),+) => ( 19 | // Вызовем `find_min!` на конце `$y` 20 | std::cmp::min($x, find_min!($($y),+)) 21 | ) 22 | } 23 | 24 | fn main() { 25 | println!("{}", find_min!(1u32)); 26 | println!("{}", find_min!(1u32 + 2 , 2u32)); 27 | println!("{}", find_min!(5u32, 2u32 * 3, 4u32)); 28 | } 29 | ``` -------------------------------------------------------------------------------- /src/macros/syntax.md: -------------------------------------------------------------------------------- 1 | # Синтаксис 2 | 3 | В следующем подразделе мы посмотрим как в Rust объявить 4 | макрос. Есть три основные идеи: 5 | 6 | - [Шаблоны и указатели](designators.md) 7 | - [Перегрузка](overload.md) 8 | - [Повторение](repeat.md) 9 | -------------------------------------------------------------------------------- /src/macros/variadics.md: -------------------------------------------------------------------------------- 1 | # Вариативные интерфейсы 2 | 3 | Интерфейсы с *переменным числом параметров* (вариативные интерфейсы) принимают произвольное число 4 | аргументов. Например, `println!` может принимать 5 | произвольное число аргументов, как определено в формате строки. 6 | 7 | Мы можем расширить наш макрос `calculate!` из 8 | предыдущей главы, чтобы он имел вариативный интерфейс: 9 | 10 | ```rust,editable 11 | macro_rules! calculate { 12 | // Шаблон для единичного `eval` 13 | (eval $e:expr) => {{ 14 | { 15 | let val: usize = $e; // Заставим быть переменную целым числом. 16 | println!("{} = {}", stringify!{$e}, val); 17 | } 18 | }}; 19 | 20 | // Рекурсивно декомпозируем несколько `eval` 21 | (eval $e:expr, $(eval $es:expr),+) => {{ 22 | calculate! { eval $e } 23 | calculate! { $(eval $es),+ } 24 | }}; 25 | } 26 | 27 | fn main() { 28 | calculate! { // Смотри, мама! Вариативный `calculate!`! 29 | eval 1 + 2, 30 | eval 3 + 4, 31 | eval (2 * 3) + 1 32 | } 33 | } 34 | ``` 35 | 36 | Вывод: 37 | 38 | ```txt 39 | 1 + 2 = 3 40 | 3 + 4 = 7 41 | (2 * 3) + 1 = 7 42 | ``` 43 | -------------------------------------------------------------------------------- /src/meta.md: -------------------------------------------------------------------------------- 1 | # Meta 2 | 3 | Некоторые темы не совсем соответствуют тому, как вы программируете, но предоставляют вам инструменты или инфраструктуру, которые делают лучше для всех. Эти темы включают: 4 | 5 | - Документацию: генерирование пользовательской документации с использованием `rustdoc`. 6 | - Тестирование: создание набора тестов для библиотек, чтобы быть уверенным, что ваша библиотека делает то, что должна. 7 | - Бенчмаркинг: создание бенчмарков функциональности для уверенности, что она работает быстро. 8 | -------------------------------------------------------------------------------- /src/mod.md: -------------------------------------------------------------------------------- 1 | # Модули 2 | 3 | Rust предоставляет мощную систему модулей, которая используется 4 | для иерархического разделения кода на логические единицы (модули) и 5 | управления видимостью (публичное и приватное) между ними. 6 | 7 | Модуль - это набор элементов, таких как: функции, структуры, типажи, блоки реализации (`impl`) 8 | и даже другие модули. 9 | -------------------------------------------------------------------------------- /src/mod/split.md: -------------------------------------------------------------------------------- 1 | # Иерархия файлов 2 | 3 | Модули могут быть отображены на иерархию файлов и директорий. 4 | Давайте разобьём [пример с видимостью модулей][visibility] на файлы: 5 | 6 | ```bash 7 | $ tree . 8 | . 9 | |-- my 10 | | |-- inaccessible.rs 11 | | |-- mod.rs 12 | | `-- nested.rs 13 | `-- split.rs 14 | ``` 15 | 16 | В `split.rs`: 17 | 18 | ```rust,ignore 19 | // Эта декларация найдёт файл с именем `my.rs` или `my/mod.rs` и вставит 20 | // его содержимое внутрь модуля с именем `my` в этой области видимости 21 | mod my; 22 | 23 | fn function() { 24 | println!("вызвана `function()`"); 25 | } 26 | 27 | fn main() { 28 | my::function(); 29 | 30 | function(); 31 | 32 | my::indirect_access(); 33 | 34 | my::nested::function(); 35 | } 36 | 37 | ``` 38 | 39 | В `my/mod.rs`: 40 | 41 | ```rust,ignore 42 | // Точно так же, `mod inaccessible` и `mod nested` обнаружат файлы `nested.rs` 43 | // и `inaccessible.rs`, и затем вставят их здесь в соответствующие модули 44 | 45 | mod inaccessible; 46 | pub mod nested; 47 | 48 | pub fn function() { 49 | println!("вызвана `my::function()`"); 50 | } 51 | 52 | fn private_function() { 53 | println!("вызывает `my::private_function()`"); 54 | } 55 | 56 | pub fn indirect_access() { 57 | print!("вызвана `my::indirect_access()`, которая\n> "); 58 | 59 | private_function(); 60 | } 61 | ``` 62 | 63 | В `my/nested.rs`: 64 | 65 | ```rust,ignore 66 | pub fn function() { 67 | println!("вызвана `my::nested::function()`"); 68 | } 69 | 70 | #[allow(dead_code)] 71 | fn private_function() { 72 | println!("вызвана `my::nested::private_function()`"); 73 | } 74 | ``` 75 | 76 | В `my/inaccessible.rs`: 77 | 78 | ```rust,ignore 79 | #[allow(dead_code)] 80 | pub fn public_function() { 81 | println!("вызвана `my::inaccessible::public_function()`"); 82 | } 83 | ``` 84 | 85 | Давайте проверим, что все ещё работает, как раньше: 86 | 87 | ```bash 88 | $ rustc split.rs && ./split 89 | вызвана `my::function()` 90 | вызвана `function()` 91 | вызвана `my::indirect_access()`, которая 92 | > вызвана `my::private_function()` 93 | вызвана `my::nested::function()` 94 | ``` 95 | 96 | [visibility]: mod/visibility.html 97 | -------------------------------------------------------------------------------- /src/mod/struct_visibility.md: -------------------------------------------------------------------------------- 1 | # Видимость структуры 2 | 3 | Структуры имеют дополнительный уровень видимости благодаря полями. По умолчанию 4 | видимость полей приватная, но, это можно изменить с помощью модификатора `pub`. 5 | Приватная видимость имеет значение только при обращении к структуре извне модуля, 6 | где она определена, и необходимо скрыть информацию (инкапсуляция). 7 | 8 | ```rust,editable 9 | mod my { 10 | // Публичная структура с публичным полем обобщённого типа `T` 11 | pub struct OpenBox { 12 | pub contents: T, 13 | } 14 | 15 | // Публичная структура с приватным полем обобщённого типа `T` 16 | #[allow(dead_code)] 17 | pub struct ClosedBox { 18 | contents: T, 19 | } 20 | 21 | impl ClosedBox { 22 | // Публичный конструктор 23 | pub fn new(contents: T) -> ClosedBox { 24 | ClosedBox { 25 | contents: contents, 26 | } 27 | } 28 | } 29 | } 30 | 31 | fn main() { 32 | // Публичная структура с публичным полем может быть создана, как обычно 33 | let open_box = my::OpenBox { contents: "публичную информацию" }; 34 | 35 | // а их поля доступны всем. 36 | println!("Открытая упаковка хранит: {}", open_box.contents); 37 | 38 | // Публичные структуры с приватными полями не могут быть созданы, используя имя полей 39 | // Ошибка! `ClosedBox` имеет приватные поля 40 | //let closed_box = my::ClosedBox { contents: "классифицированную информацию" }; 41 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 42 | 43 | // Однако, структуры с приватными полями могут быть созданы с помощью 44 | // публичного конструктора 45 | let _closed_box = my::ClosedBox::new("классифицированную информацию"); 46 | 47 | // нельзя получить доступ к приватным полям публичных структур. 48 | // Ошибка! Поле `contents` приватное 49 | //println!("Закрытая упаковка хранит: {}", _closed_box.contents); 50 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 51 | } 52 | ``` 53 | 54 | ### Смотрите также: 55 | 56 | [generics][generics] и [методы][methods] 57 | 58 | [generics]: generics.html 59 | [methods]: fn/methods.html -------------------------------------------------------------------------------- /src/mod/super.md: -------------------------------------------------------------------------------- 1 | # `super` и `self` 2 | 3 | Ключевые слова `super` и `self` в пути используются, 4 | чтобы устранить неоднозначность между используемыми элементами модуля. 5 | 6 | ```rust,editable 7 | fn function() { 8 | println!("вызвана `function()`"); 9 | } 10 | 11 | mod cool { 12 | pub fn function() { 13 | println!("called `cool::function()`"); 14 | } 15 | } 16 | 17 | mod my { 18 | fn function() { 19 | println!("вызвана `my::function()`"); 20 | } 21 | 22 | mod cool { 23 | pub fn function() { 24 | println!("вызвана `my::cool::function()`"); 25 | } 26 | } 27 | 28 | pub fn indirect_call() { 29 | // Давайте вызовем все функции под названием `function` в этой области видимости! 30 | print!("вызвана `my::indirect_call()`, с помощью которой\n> "); 31 | 32 | // Ключевое слово `self` ссылается на область видимости текущего модуля. 33 | // В нашем случае - модуль `my`. 34 | // Вызов `self::function()`, так же как и вызов `function()` дают одинаковый результат, 35 | // т.к они ссылаются на одну и ту же функцию. 36 | self::function(); 37 | function(); 38 | 39 | // Мы так же можем использовать ключевое слово `self`, 40 | // чтобы получить доступ к другим модулям внутри модуля `my`: 41 | self::cool::function(); 42 | 43 | // Ключевое слово `super` ссылается на родительскую область видимости (вне модуля `my`). 44 | super::function(); 45 | 46 | // Этим действием мы свяжем `cool::function` в области видимости *контейнера*. 47 | // В данном случае область видимости контейнера является самой дальней областью видимости. 48 | { 49 | use cool::function as root_function; 50 | root_function(); 51 | } 52 | } 53 | } 54 | 55 | fn main() { 56 | my::indirect_call(); 57 | } 58 | ``` -------------------------------------------------------------------------------- /src/mod/use.md: -------------------------------------------------------------------------------- 1 | # Декларация `use` 2 | 3 | Декларация `use` используется, чтобы связать полный путь с новым именем, 4 | что упрощает доступ. 5 | 6 | ```rust,editable,ignore 7 | // extern crate deeply; // обычно эта строка есть и она не закомментирована! 8 | 9 | use crate::deeply::nested::{ 10 | my_first_function, 11 | my_second_function, 12 | AndATraitType 13 | }; 14 | 15 | fn main() { 16 | my_first_function(); 17 | } 18 | ``` 19 | 20 | Вы можете использовать ключевое слово `as`, что импортировать сущности и функции под другим именем: 21 | 22 | ```rust,editable 23 | // Привязать путь `deeply::nested::function` к `other_function`. 24 | use deeply::nested::function as other_function; 25 | 26 | fn function() { 27 | println!("вызвана `function()`"); 28 | } 29 | 30 | mod deeply { 31 | pub mod nested { 32 | pub fn function() { 33 | println!("вызвана `deeply::nested::function()`") 34 | } 35 | } 36 | } 37 | 38 | fn main() { 39 | // Упрощённый доступ к `deeply::nested::function` 40 | other_function(); 41 | 42 | println!("Entering block"); 43 | { 44 | // Эквивалентно `use deeply::nested::function as function`. 45 | // `function()` затенение собой внешнюю функцию. 46 | use deeply::nested::function; 47 | function(); 48 | 49 | // у привязок `use` локальная область видимости. В данном случае 50 | // внешняя `function()` затенена только в этом блоке. 51 | println!("Leaving block"); 52 | } 53 | 54 | function(); 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /src/primitives.md: -------------------------------------------------------------------------------- 1 | # Примитивы 2 | 3 | Rust предоставляет доступ к большому количеству `примитивов`: 4 | 5 | ### Скалярные типы 6 | 7 | - знаковые целочисленные: `i8`, `i16`, `i32`, `i64` и `isize` (размер указателя) 8 | - беззнаковые целочисленные: `u8`, `u16`, `u32`, `u64` и `usize` (размер указателя) 9 | - вещественные: `f32`, `f64` 10 | - `char` скалярное значение Unicode, например: `'a'`, `'α'` и `'∞'` (4 байта каждый) 11 | - `bool`: `true` или `false` 12 | - единичный тип `()`, значение которого так же `()` 13 | 14 | Несмотря на то, что значение единичного типа является кортежем, оно не считается 15 | составным типом, потому что не содержит нескольких значений. 16 | 17 | ### Составные типы 18 | 19 | - массивы, например `[1, 2, 3]` 20 | - кортежи, например `(1, true)` 21 | 22 | Переменные всегда должны быть *аннотированы*. 23 | Числам можно указать определённый тип с помощью *суффикса*, 24 | иначе будет присвоен *тип по умолчанию*. 25 | Целочисленные значения по умолчанию `i32`, а вещественные `f64`. 26 | Стоит заметить, что Rust также умеет выводить типы из контекста. 27 | 28 | ```rust,editable,ignore,mdbook-runnable 29 | fn main() { 30 | // Переменные могут быть аннотированы. 31 | let logical: bool = true; 32 | 33 | let a_float: f64 = 1.0; // Обычная аннотация 34 | let an_integer = 5i32; // Суффиксная аннотация 35 | 36 | // Этим переменным будет присвоен тип по умолчанию. 37 | let default_float = 3.0; // `f64` 38 | let default_integer = 7; // `i32` 39 | 40 | // Тип также может быть выведен из контекста. 41 | let mut inferred_type = 12; // Тип i64 выводится из другой строки 42 | inferred_type = 4294967296i64; 43 | 44 | // Значение изменяемой переменной может быть изменено. 45 | let mut mutable = 12; // Изменяемое `i32` 46 | mutable = 21; 47 | 48 | // Ошибка! Тип переменной изменить нельзя. 49 | mutable = true; 50 | 51 | // Переменные могут быть переопределены с помощью затенения. 52 | let mutable = true; 53 | } 54 | ``` 55 | 56 | ### Смотрите также: 57 | 58 | [стандартная библиотека (`std`)](https://doc.rust-lang.org/std/), [`mut`](variable_bindings/mut.md), [вывод типов](types/inference.md) и [затенение](variable_bindings/scope.md) 59 | -------------------------------------------------------------------------------- /src/primitives/array.md: -------------------------------------------------------------------------------- 1 | # Массивы и срезы 2 | 3 | `Массив` - это коллекция объектов одинакового типа `T`, расположенных в памяти 4 | непосредственно друг за другом. Массивы создаются с помощью квадратных 5 | скобок `[]`, а их размер должен быть известен во время компиляции и является 6 | частью сигнатуры типа `[T; size]`. 7 | 8 | `Срезы` похожи на массивы, но их размер не известен в момент компиляции программы. 9 | Срезы представляют собой объекты, состоящие из указателя на данные и размер среза. 10 | Размер среза равен размеру `usize` и зависит от архитектуры процессора, например, 11 | для x86-64 он равен 64 бит. Срезы могут быть использованы для заимствования 12 | части массива и будут иметь сигнатуру типа `&[T]`. 13 | 14 | ```rust,editable,ignore,mdbook-runnable 15 | use std::mem; 16 | 17 | // Эта функция заимствует срез 18 | fn analyze_slice(slice: &[i32]) { 19 | println!("первый элемент среза: {}", slice[0]); 20 | println!("в срезе {} элементов", slice.len()); 21 | } 22 | 23 | fn main() { 24 | // Массив фиксированного размера (указывать сигнатуру типа необязательно) 25 | let xs: [i32; 5] = [1, 2, 3, 4, 5]; 26 | 27 | // Все элементы могут быть инициализированы одной и той же переменной 28 | let ys: [i32; 500] = [0; 500]; 29 | 30 | // Индекс начинается с 0 31 | println!("первый элемент массива: {}", xs[0]); 32 | println!("второй элемент массива: {}", xs[1]); 33 | 34 | // `len` возвращает длину массива 35 | println!("размер массива: {}", xs.len()); 36 | 37 | // Память для массивов выделяется в стеке 38 | println!("массив занимает {} байт", mem::size_of_val(&xs)); 39 | 40 | // Массивы могут быть автоматически заимствованы как срез 41 | println!("заимствуем весь массив, используя срез"); 42 | analyze_slice(&xs); 43 | 44 | // Срезы могут указывать на часть массива 45 | println!("заимствуем часть массива как срез"); 46 | analyze_slice(&ys[1 .. 4]); 47 | 48 | // Выход за границу массива заставит компилятор паниковать. 49 | // Не надо так. 50 | println!("{}", xs[5]); 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /src/primitives/literals.md: -------------------------------------------------------------------------------- 1 | # Литералы и операторы 2 | 3 | Целочисленное `1`, вещественное `1.2`, символ `'a'`, строка `"abc"`, логическое `true` 4 | и единичный тип `()` могут быть выражены с помощью литералов. 5 | 6 | Целочисленные значения так же могут быть выражены с помощью шестнадцатеричного, 7 | восьмеричного или двоичного обозначения используя соответствующие префиксы: `0x`, `0o` или `0b`. 8 | 9 | Для улучшения читаемости числовых литералов можно использовать подчёркивания, например 10 | `1_000` тоже самое, что и `1000`, и `0.000_001` равно `0.000001`. 11 | 12 | Нам необходимо указать компилятору какой тип для литерала мы используем. 13 | Сейчас мы используем суффикс `u32`, чтобы указать, что литерал - беззнаковое целое 14 | число 32-х бит и суффикс `i32` - знаковое целое 32-х битное число. 15 | 16 | Доступные операторы и их приоритет [в Rust][rust op-prec] такой же как и в других 17 | [C-подобных языках][op-prec]. 18 | 19 | ```rust,editable 20 | fn main() { 21 | // Целочисленное сложение 22 | println!("1 + 2 = {}", 1u32 + 2); 23 | 24 | // Целочисленное вычитание 25 | println!("1 - 2 = {}", 1i32 - 2); 26 | // ЗАДАНИЕ ^ Попробуйте изменить `1i32` на `1u32` 27 | // чтобы убедится насколько важен тип данных 28 | 29 | // Булева логика 30 | println!("true И false будет {}", true && false); 31 | println!("true ИЛИ false будет {}", true || false); 32 | println!("НЕ true будет {}", !true); 33 | 34 | // Побитовые операции 35 | println!("0011 И 0101 будет {:04b}", 0b0011u32 & 0b0101); 36 | println!("0011 ИЛИ 0101 будет {:04b}", 0b0011u32 | 0b0101); 37 | println!("0011 исключающее ИЛИ 0101 будет {:04b}", 0b0011u32 ^ 0b0101); 38 | println!("1 << 5 будет {}", 1u32 << 5); 39 | println!("0x80 >> 2 будет 0x{:x}", 0x80u32 >> 2); 40 | 41 | // Использование подчёркивания для улучшения читаемости! 42 | println!("Один миллион записан как {}", 1_000_000u32); 43 | } 44 | ``` 45 | 46 | [rust op-prec]: https://doc.rust-lang.org/reference/expressions.html#expression-precedence 47 | [op-prec]: https://en.wikipedia.org/wiki/Operator_precedence#Programming_languages 48 | -------------------------------------------------------------------------------- /src/scope.md: -------------------------------------------------------------------------------- 1 | # Правила области видимости 2 | 3 | Области видимости играют важную роль во владении, заимствовании 4 | и времени жизни. То есть, они указывают компилятору, когда 5 | заимствования действительны, когда ресурсы могут быть освобождены, 6 | и когда переменные создаются или уничтожаются. -------------------------------------------------------------------------------- /src/scope/borrow.md: -------------------------------------------------------------------------------- 1 | # Заимствование 2 | 3 | Большую часть времени мы хотим обращаться к данным без получения владения над 4 | ними. Для этого Rust предоставляет механизм *заимствования* Вместо передачи 5 | объектов по значению (`T`), объекты могут быть переданы по ссылке (`&T`). 6 | 7 | Компилятор статически гарантирует, что ссылки *всегда* указывают на допустимые 8 | объекты посредством проверки заимствований. К примеру, исходный объект не может 9 | быть уничтожен, пока существуют ссылки на него. 10 | 11 | ```rust,editable,ignore,mdbook-runnable 12 | // Эта функция берёт во владение упаковку и уничтожает её 13 | fn eat_box_i32(boxed_i32: Box) { 14 | println!("Уничтожаем упаковку в которой хранится {}", boxed_i32); 15 | } 16 | 17 | // Эта функция заимствует i32 18 | fn borrow_i32(borrowed_i32: &i32) { 19 | println!("Это число равно: {}", borrowed_i32); 20 | } 21 | 22 | fn main() { 23 | // Создаём упакованное i32, и i32 на стеке 24 | let boxed_i32 = Box::new(5_i32); 25 | let stacked_i32 = 6_i32; 26 | 27 | // Заимствуем содержимое упаковки. При этом мы не владеем ресурсом. 28 | // Содержимое может быть заимствовано снова. 29 | borrow_i32(&boxed_i32); 30 | borrow_i32(&stacked_i32); 31 | 32 | { 33 | // Получаем ссылку на данные, которые хранятся внутри упаковки 34 | let _ref_to_i32: &i32 = &boxed_i32; 35 | 36 | // Ошибка! 37 | // Нельзя уничтожать упаковку `boxed_i32` пока данные внутри заимствованы. 38 | eat_box_i32(boxed_i32); 39 | // ИСПРАВЬТЕ ^ Закомментируйте эту строку 40 | 41 | // `_ref_to_i32` покидает область видимости и больше не является заимствованным ресурсом. 42 | } 43 | 44 | // `boxed_i32` теперь может получить владение над `eat_box` и быть уничтожено 45 | eat_box_i32(boxed_i32); 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /src/scope/borrow/alias.md: -------------------------------------------------------------------------------- 1 | # Алиасинг 2 | 3 | Данные могут быть заимствованы без возможности изменения любое количество раз, но пока такое заимствование существует, оригинальные данные не могут быть заимствованы с возможностью изменения. С другой стороны, 4 | одновременно может быть только *одно* изменяемое 5 | заимствование. Исходные данные могут быть снова заимствованы 6 | только *после* того, как изменяемая ссылка выйдет из 7 | области видимости. 8 | 9 | ```rust,editable 10 | struct Point { x: i32, y: i32, z: i32 } 11 | 12 | fn main() { 13 | let mut point = Point { x: 0, y: 0, z: 0 }; 14 | 15 | { 16 | let borrowed_point = &point; 17 | let another_borrow = &point; 18 | 19 | // Данные могут быть доступны через ссылки и владельца этих данных 20 | println!("Точка имеет координаты: ({}, {}, {})", 21 | borrowed_point.x, another_borrow.y, point.z); 22 | 23 | // Ошбика! Нельзя изменяемо заимствовать `point`, так как она уже 24 | // неизменяемо заимствована. 25 | //let mutable_borrow = &mut point; 26 | // TODO ^ Попробуйте раскомментировать эту строку 27 | 28 | // Неизменяемые ссылки вышли из области видимости 29 | } 30 | 31 | { 32 | let mutable_borrow = &mut point; 33 | 34 | // Меняем при помощи изменяемой ссылки 35 | mutable_borrow.x = 5; 36 | mutable_borrow.y = 2; 37 | mutable_borrow.z = 1; 38 | 39 | // Ошибка! Нельзя неизменяемо заимствовать `point` так как она уже 40 | // заимствована изменяемо. 41 | //let y = &point.y; 42 | // TODO ^ Попробуйте раскомментировать эту строку 43 | 44 | // Ошибка! Нельзя вывести на экран потому что `println!` берёт неизменяемую ссылку. 45 | //println!("Point Z coordinate is {}", point.z); 46 | // TODO ^ Попробуйте раскомментировать эту строку 47 | 48 | // Ok! Изменяемая ссылка может быть передана `println!` как неизменяемая 49 | println!("Точка имеет координаты: ({}, {}, {})", 50 | mutable_borrow.x, mutable_borrow.y, mutable_borrow.z); 51 | 52 | // Изменяемая ссылка вышла из области видимости 53 | } 54 | 55 | // Неизменяемая ссылка на `point` снова разрешена 56 | let borrowed_point = &point; 57 | println!("Точка имеет координаты: ({}, {}, {})", 58 | borrowed_point.x, borrowed_point.y, borrowed_point.z); 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /src/scope/borrow/freeze.md: -------------------------------------------------------------------------------- 1 | # Замораживание 2 | 3 | Когда данные заимствуются, они заодно и *замораживаются*. *Замороженные* данные 4 | не могут быть изменены до тех пор, пока все ссылки не выйдут за область видимости: 5 | 6 | ```rust,editable,ignore,mdbook-runnable 7 | fn main() { 8 | let mut _mutable_integer = 7i32; 9 | 10 | { 11 | // Заимствуем `_mutable_integer` 12 | let large_integer = &_mutable_integer; 13 | 14 | // Ошибка! `_mutable_integer` заморожена в этой области видимости 15 | _mutable_integer = 50; 16 | // ИСПРАВЬТЕ ^ Закомментируйте эту строку 17 | 18 | println!("Неизменяемое заимствование числа {}", large_integer); 19 | 20 | // `large_integer` выходит из области видимости 21 | } 22 | 23 | // Ок! `_mutable_integer` не заморожена в этой области видимости 24 | _mutable_integer = 3; 25 | } 26 | ``` 27 | -------------------------------------------------------------------------------- /src/scope/borrow/mut.md: -------------------------------------------------------------------------------- 1 | # Изменяемость 2 | 3 | Изменяемые данные могут быть заимствованы с возможностью 4 | изменения при помощи `&mut T`. Это называется 5 | *изменяемая ссылка* и даёт заимствующему 6 | возможность чтения и записи. В отличие от неё, 7 | `&T` заимствует данные через неизменяемую 8 | ссылку и заимствующий может читать данные, но не может 9 | модифицировать их: 10 | 11 | ```rust,editable,ignore,mdbook-runnable 12 | #[allow(dead_code)] 13 | #[derive(Clone, Copy)] 14 | struct Book { 15 | // `&'static str` - это ссылка на строку, расположенную в неизменяемой памяти 16 | author: &'static str, 17 | title: &'static str, 18 | year: u32, 19 | } 20 | 21 | // Эта функция получает ссылку на книгу 22 | fn borrow_book(book: &Book) { 23 | println!("Я неизменяемо заимствовала {} - {} издания", book.title, book.year); 24 | } 25 | 26 | // Эта функция получает изменяемую ссылку на книгу и устанавливает поле `year` в 2014 27 | fn new_edition(book: &mut Book) { 28 | book.year = 2014; 29 | println!("Я изменяемо заимствовала {} - {} издания", book.title, book.year); 30 | } 31 | 32 | fn main() { 33 | // Создаём неизменяемую книгу в переменной `immutabook` 34 | let immutabook = Book { 35 | // строковый литерал имеет тип `&'static str` 36 | author: "Douglas Hofstadter", 37 | title: "Gödel, Escher, Bach", 38 | year: 1979, 39 | }; 40 | 41 | // Создаём изменяемую копию `immutabook` и называем её `mutabook` 42 | let mut mutabook = immutabook; 43 | 44 | // Неизменяемое заимствование неизменяемого объекта 45 | borrow_book(&immutabook); 46 | 47 | // Неизменяемое заимствование изменяемого объекта 48 | borrow_book(&mutabook); 49 | 50 | // Заимствование изеняемого объекта как изменяемого 51 | new_edition(&mut mutabook); 52 | 53 | // Ошибка! Нельзя заимствовать неизменяемый объект как изменяемый 54 | new_edition(&mut immutabook); 55 | // ИСПРАВЬТЕ ^ Добавьте комментарий для этой строки 56 | } 57 | ``` 58 | 59 | ### Смотрите также: 60 | 61 | [`static`](../lifetime/static_lifetime.md) 62 | -------------------------------------------------------------------------------- /src/scope/borrow/ref.md: -------------------------------------------------------------------------------- 1 | # `ref` паттерн 2 | 3 | Когда мы используем сопоставление с образцом или 4 | деструктурируем при помощи `let`, можно 5 | использовать ключевое слово `ref` для получения 6 | ссылки на поле структуры или кортежа. Пример ниже показывает 7 | несколько случаев, когда это может быть полезно: 8 | 9 | ```rust,editable 10 | #[derive(Clone, Copy)] 11 | struct Point { x: i32, y: i32 } 12 | 13 | fn main() { 14 | let c = 'Q'; 15 | 16 | // Заимствование с `ref` по левую сторону от присваивания, эквивалетно 17 | // заимствованию с `&` по правую сторону. 18 | let ref ref_c1 = c; 19 | let ref_c2 = &c; 20 | 21 | println!("ref_c1 равно ref_c2: {}", *ref_c1 == *ref_c2); 22 | 23 | let point = Point { x: 0, y: 0 }; 24 | 25 | // `ref` также может использоваться при деструктуризации структур. 26 | let _copy_of_x = { 27 | // `ref_to_x` - ссылка на поле `x` в `point`. 28 | let Point { x: ref ref_to_x, y: _ } = point; 29 | 30 | // Возвращаем копию поля `x` из `point`. 31 | *ref_to_x 32 | }; 33 | 34 | // Изменяемая копия `point` 35 | let mut mutable_point = point; 36 | 37 | { 38 | // `ref` может использоваться вместе с `mut` для получения изменяемой ссылки. 39 | let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point; 40 | 41 | // Изменяем поле `y` переменной `mutable_point` через изменяемую ссылку. 42 | *mut_ref_to_y = 1; 43 | } 44 | 45 | println!("point ({}, {})", point.x, point.y); 46 | println!("mutable_point ({}, {})", mutable_point.x, mutable_point.y); 47 | 48 | // Изменяемый кортеж с указателем 49 | let mut mutable_tuple = (Box::new(5u32), 3u32); 50 | 51 | { 52 | // Деструктурируем `mutable_tuple` чтобы изменить значение `last`. 53 | let (_, ref mut last) = mutable_tuple; 54 | *last = 2u32; 55 | } 56 | 57 | println!("tuple {:?}", mutable_tuple); 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /src/scope/lifetime.md: -------------------------------------------------------------------------------- 1 | # Времена жизни 2 | 3 | *Время жизни* - это конструкция, которую компилятор (или более конкретно, 4 | его анализатор заимствований) использует, чтобы убедиться, что все 5 | заимствования действительны. В частности время жизни переменной 6 | начинается с момента её создания и заканчивается когда она уничтожается. 7 | Времена жизни и области видимости упоминаются часто вместе, но 8 | они не совпадают. 9 | 10 | Возьмём, например, случай когда мы заимствуем переменную через `&`. 11 | Срок действия заимствования определяется местом его объявления. 12 | В результате, заимствование действительно до тех пор, 13 | пока оно не закончится или пока кредитор не будет уничтожен. Однако, 14 | область заимствования определяется местом использования ссылки. 15 | 16 | В следующем примере и в остальной части этого раздела мы увидим, как 17 | времена жизни связаны с областями видимости, а также как они различаются. 18 | 19 | ```rust,editable 20 | // Времена жизни аннотированы линиями, обозначающими 21 | // создание и уничтожение каждой переменной. 22 | // `i` имеет самое длинное время жизни, так как его область охватывает 23 | // полностью оба заимствования `borrow1` и `borrow2`. 24 | // Продолжительность заимствования `borrow1` по сравнению с 25 | // заимствованием `borrow2` не имеет значения, так как они не пересекаются. 26 | fn main() { 27 | let i = 3; // Lifetime for `i` starts. ────────────────┐ 28 | // │ 29 | { // │ 30 | let borrow1 = &i; // `borrow1` lifetime starts. ──┐│ 31 | // ││ 32 | println!("borrow1: {}", borrow1); // ││ 33 | } // `borrow1 ends. ──────────────────────────────────┘│ 34 | // │ 35 | // │ 36 | { // │ 37 | let borrow2 = &i; // `borrow2` lifetime starts. ──┐│ 38 | // ││ 39 | println!("borrow2: {}", borrow2); // ││ 40 | } // `borrow2` ends. ─────────────────────────────────┘│ 41 | // │ 42 | } // Lifetime ends. ─────────────────────────────────────┘ 43 | ``` 44 | 45 | Обратите внимание, что для меток времени жизни не назначаются имена или типы. 46 | Это ограничивает то, как время жизни будет использоваться, как мы увидим далее. 47 | -------------------------------------------------------------------------------- /src/scope/lifetime/elision.md: -------------------------------------------------------------------------------- 1 | # Сокрытие 2 | 3 | Некоторые шаблоны времён жизни достаточно общие и поэтому 4 | анализатор заимствований может позволить вам опустить их чтобы 5 | ускорить написание кода и увеличить его читаемость. 6 | Это известно как сокрытие времён жизни. Сокрытие появилось в Rust, 7 | исключительно из-за того, что они применяются к общим шаблонам. 8 | 9 | Следующий код показывает несколько примеров сокрытия. Для более полного описания сокрытия, обратитесь к главе про [a0}сокрытие времён жизни в TRPL. 10 | 11 | ```rust,editable 12 | // По существу, `elided_input` и `annotated_input` имеют одинаковую сигнатуру 13 | // потому что время жизни `elided_input` выводится компилятором: 14 | fn elided_input(x: &i32) { 15 | println!("`elided_input`: {}", x); 16 | } 17 | 18 | fn annotated_input<'a>(x: &'a i32) { 19 | println!("`annotated_input`: {}", x); 20 | } 21 | 22 | // Аналогично, `elided_pass` и `annotated_pass` имеют идентичные сигнатуры 23 | // потому что время жизни неявно добавлено к `elided_pass`: 24 | fn elided_pass(x: &i32) -> &i32 { x } 25 | 26 | fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x } 27 | 28 | fn main() { 29 | let x = 3; 30 | 31 | elided_input(&x); 32 | annotated_input(&x); 33 | 34 | println!("`elided_pass`: {}", elided_pass(&x)); 35 | println!("`annotated_pass`: {}", annotated_pass(&x)); 36 | } 37 | ``` 38 | 39 | ### Смотрите также: 40 | 41 | [сокрытие](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-elision) 42 | -------------------------------------------------------------------------------- /src/scope/lifetime/fn.md: -------------------------------------------------------------------------------- 1 | # Функции 2 | 3 | Сигнатуры функции с указанием времени жизни имеют некоторые ограничения: 4 | 5 | - любая ссылка *должна* иметь аннотированное время жизни 6 | - любая возвращаемая ссылка *должна* иметь то же время жизни, что входящая ссылкаили `static`. 7 | 8 | Кроме того, обратите внимание, что возврат ссылок из функции, которая не имеет ссылок во входных аргументах, запрещен, если он 9 | приведет к возвращению ссылок на недопустимые данные. В следующем примере показаны 10 | некоторые действительные формы функции со временем жизни: 11 | 12 | ```rust,editable 13 | // Одна входная ссылка со временем жизни `'a`, которая 14 | // будет жить как минимум до конца функции. 15 | fn print_one<'a>(x: &'a i32) { 16 | println!("`print_one`: x is {}", x); 17 | } 18 | 19 | // Использование времени жизни также возможно с изменяемыми ссылками. 20 | fn add_one<'a>(x: &'a mut i32) { 21 | *x += 1; 22 | } 23 | 24 | // Несколько элементов с различными временами жизни. В этом случае 25 | // было бы хорошо, чтобы у обоих ссылок было одно время жизни `'a`, 26 | // в более сложных случаях может потребоваться различное время жизни. 27 | fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) { 28 | println!("`print_multi`: x is {}, y is {}", x, y); 29 | } 30 | 31 | // Возврат переданных на вход ссылок допустим. 32 | // Однако должен быть указано правильное время жизни. 33 | fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x } 34 | 35 | //fn invalid_output<'a>() -> &'a String { &String::from("foo") } 36 | // Код написанный выше является недопустимым: время жизни `'a` 37 | // должно жить после выхода из функции. 38 | // Здесь, `&String::from("foo")` создает ссылку на `String` 39 | // Данные будут удалены после выхода из области видимости, и 40 | // будет возвращена ссылка на недопустимые данные. 41 | 42 | fn main() { 43 | let x = 7; 44 | let y = 9; 45 | 46 | print_one(&x); 47 | print_multi(&x, &y); 48 | 49 | let z = pass_x(&x, &y); 50 | print_one(z); 51 | 52 | let mut t = 3; 53 | add_one(&mut t); 54 | print_one(&t); 55 | } 56 | ``` 57 | 58 | ### Смотрите также: 59 | 60 | [функции](fn.md) 61 | -------------------------------------------------------------------------------- /src/scope/lifetime/lifetime_bounds.md: -------------------------------------------------------------------------------- 1 | # Ограничения 2 | 3 | Так же, как и обобщённые типы, времена жизни (обобщённое само по себе) могут быть ограничены. 4 | Для них знак `:` имеет немного другое значение, 5 | но знак `+` такое же. Прочитайте следующую заметку: 6 | 7 | 1. `T: 'a`: *Все* ссылки в `T` должны пережить время жизни `'a`. 8 | 2. `T: Trait + 'a`: Тип `T` должен реализовать типаж `Trait` и *все* ссылкив `T` должны пережить `'a`. 9 | 10 | Пример ниже демонстрирует синтаксис в действии и использует его после ключевого слова `where`: 11 | 12 | ```rust,editable 13 | use std::fmt::Debug; // Типаж с ограничениями. 14 | 15 | #[derive(Debug)] 16 | struct Ref<'a, T: 'a>(&'a T); 17 | // `Ref` содержит ссылки на обобщённый тип `T` который имеет 18 | // неизвестное время жизни `'a`. `T` ограничен так, что любые 19 | // *ссылки* в `T` должны пережить `'a`. 20 | // Кроме того, время жизни `Ref` не может превышать `'a`. 21 | 22 | // Обобщённая функция, которая показывает использование типажа `Debug`. 23 | fn print(t: T) where 24 | T: Debug { 25 | println!("`print`: t это {:?}", t); 26 | } 27 | 28 | // Здесь приводится ссылка на `T`, где `T` реализует 29 | // `Debug` и все *ссылки* в `T` переживают `'a`. 30 | // К тому же, `'a` должен пережить функцию. 31 | fn print_ref<'a, T>(t: &'a T) where 32 | T: Debug + 'a { 33 | println!("`print_ref`: t это {:?}", t); 34 | } 35 | 36 | fn main() { 37 | let x = 7; 38 | let ref_x = Ref(&x); 39 | 40 | print_ref(&ref_x); 41 | print(ref_x); 42 | } 43 | ``` 44 | 45 | ### Смотрите также: 46 | 47 | [Обобщения](../../generics.md), [ограничения в обобщениях](../../generics/bounds.md) и 48 | [множественные ограничения в обобщениях](../../generics/multi_bounds.md) 49 | -------------------------------------------------------------------------------- /src/scope/lifetime/lifetime_coercion.md: -------------------------------------------------------------------------------- 1 | # Приведение (coercion) 2 | 3 | Длинное время жизни может быть приведено к короткому, 4 | благодаря чему всё работает нормально внутри области 5 | видимости, хотя кажется, что не должно. Это достигается за счёт того что компилятор Rust выполняет приведение времён жизни и за счёт 6 | объявления разницы между ними разницы: 7 | 8 | ```rust,editable 9 | // Здесь Rust выводит наиболее короткое время жизни. 10 | // Затем обе ссылки приводятся к этому времени жизни. 11 | fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 { 12 | first * second 13 | } 14 | 15 | // `<'a: 'b, 'b>` читается как "время жизни `'a` не меньше, чем время жизни `'b`". 16 | // Здесь мы получаем `&'a i32` и в результате приведения возвращаем `&'b i32`. 17 | fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 { 18 | first 19 | } 20 | 21 | fn main() { 22 | let first = 2; // Более длинное время жизни 23 | 24 | { 25 | let second = 3; // Более короткое время жизни 26 | 27 | println!("Произведение равно {}", multiply(&first, &second)); 28 | println!("{} первое", choose_first(&first, &second)); 29 | }; 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /src/scope/lifetime/methods.md: -------------------------------------------------------------------------------- 1 | # Методы 2 | 3 | Методы аннотируются аналогично функциям: 4 | 5 | ```rust,editable 6 | struct Owner(i32); 7 | 8 | impl Owner { 9 | // Время жизни аннотируется как в отдельной функции. 10 | fn add_one<'a>(&'a mut self) { 11 | self.0 += 1; 12 | } 13 | fn print<'a>(&'a self) { 14 | println!("`print`: {}", self.0); 15 | } 16 | } 17 | 18 | fn main() { 19 | let mut owner = Owner(18); 20 | 21 | owner.add_one(); 22 | owner.print(); 23 | } 24 | ``` 25 | 26 | ### Смотрите также: 27 | 28 | [`Методы`][methods] 29 | 30 | [methods]: fn/methods.html -------------------------------------------------------------------------------- /src/scope/lifetime/static_lifetime.md: -------------------------------------------------------------------------------- 1 | # Static 2 | 3 | `'static` - наибольшее возможное время жизни и 4 | длится в течение всей жизни работающей программы. 5 | `'static` может быть приведено к более короткому 6 | времени жизни. Есть два способа сделать переменную со 7 | временем жизни `'static` и в результате обоих 8 | способов, переменная хранится в неизменяемой памяти бинарного 9 | файла: 10 | 11 | - Создание константы с ключевым словом `static`. 12 | - Создание *строкового* литерала, имеющего тип `&'static str`. 13 | 14 | Рассмотрим следующий пример, который показывает оба метода: 15 | 16 | ```rust,editable 17 | // Создадим константу со временем жизни `'static`. 18 | static NUM: i32 = 18; 19 | 20 | // Вернём ссылку на `NUM`, у которой собственное время жизни `'static` 21 | // приводится ко времени жизни аргумента. 22 | fn coerce_static<'a>(_: &'a i32) -> &'a i32 { 23 | &NUM 24 | } 25 | 26 | fn main() { 27 | { 28 | // Создадим *строковый* литерал и выведем его: 29 | let static_string = "Я в неизменяемой памяти"; 30 | println!("static_string: {}", static_string); 31 | 32 | // Когда `static_string` выходит из области видимости, ссылка 33 | // на неё больше не может быть использована, но данные остаются в бинарном файле. 34 | } 35 | 36 | { 37 | // Создадим число для использования в `coerce_static`: 38 | let lifetime_num = 9; 39 | 40 | // Приведём `NUM` ко времени жизни `lifetime_num`: 41 | let coerced_static = coerce_static(&lifetime_num); 42 | 43 | println!("coerced_static: {}", coerced_static); 44 | } 45 | 46 | println!("NUM: {} остаётся доступным!", NUM); 47 | } 48 | ``` 49 | 50 | ### Смотрите также: 51 | 52 | [`'static` константы](../../custom_types/constants.md) 53 | -------------------------------------------------------------------------------- /src/scope/lifetime/struct.md: -------------------------------------------------------------------------------- 1 | # Структуры 2 | 3 | Аннотирование времени жизни в структурах аналогично функциям: 4 | 5 | ```rust,editable 6 | // Тип `Borrowed`, в котором находится ссылка на `i32`. 7 | // Ссылка на `i32` должна пережить `Borrowed`. 8 | #[derive(Debug)] 9 | struct Borrowed<'a>(&'a i32); 10 | 11 | // Аналогично, обе ссылки расположенные здесь, должны пережить эту структуру. 12 | #[derive(Debug)] 13 | struct NamedBorrowed<'a> { 14 | x: &'a i32, 15 | y: &'a i32, 16 | } 17 | 18 | // Перечисление, которое указывает на `i32` или на ссылку. 19 | #[derive(Debug)] 20 | enum Either<'a> { 21 | Num(i32), 22 | Ref(&'a i32), 23 | } 24 | 25 | fn main() { 26 | let x = 18; 27 | let y = 15; 28 | 29 | let single = Borrowed(&x); 30 | let double = NamedBorrowed { x: &x, y: &y }; 31 | let reference = Either::Ref(&x); 32 | let number = Either::Num(y); 33 | 34 | println!("x заимствован в {:?}", single); 35 | println!("x и y заимствованы в {:?}", double); 36 | println!("x заимствован в {:?}", reference); 37 | println!("y *не* заимствован в {:?}", number); 38 | } 39 | ``` 40 | 41 | ### Смотрите также: 42 | 43 | [`Структуры`][structs] 44 | 45 | 46 | [structs]: custom_types/structs.html 47 | -------------------------------------------------------------------------------- /src/scope/lifetime/trait.md: -------------------------------------------------------------------------------- 1 | # Типажи 2 | 3 | Аннотирование времён жизни для методов типажей в основном 4 | похоже на аннотирование в функциях. Обратите внимание, что 5 | `impl` также может иметь аннотацию времени жизни. 6 | 7 | ```rust,editable 8 | // Структура с аннотированным временем жизни. 9 | #[derive(Debug)] 10 | struct Borrowed<'a> { 11 | x: &'a i32, 12 | } 13 | 14 | // Аннотированное время жизни для реализации. 15 | impl<'a> Default for Borrowed<'a> { 16 | fn default() -> Self { 17 | Self { 18 | x: &10, 19 | } 20 | } 21 | } 22 | 23 | fn main() { 24 | let b: Borrowed = Default::default(); 25 | println!("b равно {:?}", b); 26 | } 27 | ``` 28 | 29 | ### Смотрите также: 30 | 31 | [`trait`](../../trait.md) 32 | -------------------------------------------------------------------------------- /src/scope/move.md: -------------------------------------------------------------------------------- 1 | # Владение и перемещение 2 | 3 | Поскольку переменные ответственны за освобождение своих ресурсов, 4 | **ресурсы могут иметь лишь одного владельца**. Это ограничение предотвращает 5 | возможность высвобождения ресурсов более одно раза. Обратите внимание, 6 | что не все переменные владеют своим ресурсом (например, [ссылки][references]). 7 | 8 | При присваивании (`let x = y`) или при передаче функции аргумента по значению (`foo(x)`), 9 | *владение* ресурсами передаётся. В языке Rust это называется *перемещением.* 10 | 11 | После перемещения ресурсов, переменная, владевшая ресурсами ранее, не может быть 12 | использована. Это предотвращает создание висячих указателей. 13 | 14 | ```rust,editable 15 | // Эта функция берёт во владение память, выделенную в куче 16 | fn destroy_box(c: Box) { 17 | println!("Уничтожаем упаковку, в которой хранится {}", c); 18 | 19 | // `c` уничтожится, а память будет освобождена 20 | } 21 | 22 | fn main() { 23 | // Целое число выделенное в стеке 24 | let x = 5u32; 25 | 26 | // *Копируем* `x` в `y`. В данном случае нет ресурсов для перемещения 27 | let y = x; 28 | 29 | // Оба значения можно использовать независимо 30 | println!("x равен {}, а y равен {}", x, y); 31 | 32 | // `a` - указатель на целое число, выделенное в куче 33 | let a = Box::new(5i32); 34 | 35 | println!("a содержит: {}", a); 36 | 37 | // *Перемещаем* `a` в `b` 38 | let b = a; 39 | // Адрес указателя `a` копируется (но не данные) в `b`. 40 | // Оба указателя указывают на одни и те же данные в куче, но 41 | // `b` теперь владеет ими. 42 | 43 | // Ошибка! `a` больше не может получить доступ к данным, потому что 44 | // больше не владеет данными в куче. 45 | //println!("a содержит: {}", a); 46 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 47 | 48 | // Эта функция берет во владение память, выделенную в куче, которой ранее владела `b` 49 | destroy_box(b); 50 | 51 | // Поскольку в данный момент память в куче уже освобождена, это действие 52 | // приведёт к разыменованию освобождённой памяти, но это запрещено компилятором 53 | // Ошибка! Причина та же, что и в прошлый раз 54 | //println!("b содержит: {}", b); 55 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 56 | } 57 | ``` 58 | 59 | [references]: flow_control/match/destructuring/destructure_pointers.html 60 | -------------------------------------------------------------------------------- /src/scope/move/mut.md: -------------------------------------------------------------------------------- 1 | # Изменяемость 2 | 3 | Изменяемость данных может быть изменена при передаче владения. 4 | 5 | ```rust,editable 6 | fn main() { 7 | let immutable_box = Box::new(5u32); 8 | 9 | println!("immutable_box содержит в себе {}", immutable_box); 10 | 11 | // Ошибка изменяемости 12 | //*immutable_box = 4; 13 | 14 | // *Переместить* упаковку, изменив её владение (и изменяемость) 15 | let mut mutable_box = immutable_box; 16 | 17 | println!("mutable_box содержит в себе {}", mutable_box); 18 | 19 | // Изменяем данные внутри упаковки 20 | *mutable_box = 4; 21 | 22 | println!("mutable_box now содержит в себе {}", mutable_box); 23 | } 24 | ``` -------------------------------------------------------------------------------- /src/std.md: -------------------------------------------------------------------------------- 1 | # Типы стандартной библиотеки 2 | 3 | Стандартная библиотека (`std`) предоставляет множество пользовательских типов, которые значительно 4 | расширяют `примитивы`. Некоторые из них: 5 | 6 | - расширяемую строку `String`s: `"hello world"` 7 | - динамический массив: `[1, 2, 3]` 8 | - опциональные типы: `Option` 9 | - типы для обработки ошибок: `Result` 10 | - указатели на объекты в куче: `Box` 11 | 12 | ### Смотрите также: 13 | 14 | [Примитивы](primitives.md) и [`std`](https://doc.rust-lang.org/std/) 15 | -------------------------------------------------------------------------------- /src/std/box.md: -------------------------------------------------------------------------------- 1 | # `Box`, стек и куча 2 | 3 | Все значения в Rust по умолчанию аллоцируются на стеке. Значения могут быть *упакованы* 4 | (аллоцированы в куче) при помощи создания `Box`. `Box` - это умный указатель на аллоцированное в куче значение типа `T`. Когда `Box` покидает область видимости, вызывается его деструктор, который уничтожает внутренний объект, и занятая им память в куче освобождается. 5 | 6 | Упакованные значения могут быть разыменованы с помощью операции `*`. 7 | Эта операция убирает один уровень косвенности. 8 | 9 | ```rust,editable 10 | use std::mem; 11 | 12 | #[allow(dead_code)] 13 | #[derive(Debug, Clone, Copy)] 14 | struct Point { 15 | x: f64, 16 | y: f64, 17 | } 18 | 19 | #[allow(dead_code)] 20 | struct Rectangle { 21 | p1: Point, 22 | p2: Point, 23 | } 24 | 25 | fn origin() -> Point { 26 | Point { x: 0.0, y: 0.0 } 27 | } 28 | 29 | fn boxed_origin() -> Box { 30 | // Аллоцировать эту точку в куче и вернуть указатель на неё 31 | Box::new(Point { x: 0.0, y: 0.0 }) 32 | } 33 | 34 | fn main() { 35 | // (все аннотации типов избыточны) 36 | // Переменные, аллоцированные на стеке 37 | let point: Point = origin(); 38 | let rectangle: Rectangle = Rectangle { 39 | p1: origin(), 40 | p2: Point { x: 3.0, y: 4.0 } 41 | }; 42 | 43 | // Прямоугольник, аллоцированный в куче 44 | let boxed_rectangle: Box = Box::new(Rectangle { 45 | p1: origin(), 46 | p2: origin() 47 | }); 48 | 49 | // Результат функции может быть упакован 50 | let boxed_point: Box = Box::new(origin()); 51 | 52 | // Двойная косвенность 53 | let box_in_a_box: Box> = Box::new(boxed_origin()); 54 | 55 | println!("Точка занимает {} байт на стеке", 56 | mem::size_of_val(&point)); 57 | println!("Прямоугольник занимает {} байт на стеке", 58 | mem::size_of_val(&rectangle)); 59 | 60 | // box size == pointer size 61 | println!("Упакованная точка занимает {} байт на стеке", 62 | mem::size_of_val(&boxed_point)); 63 | println!("Упакованный прямоугольник занимает {} байт на стеке", 64 | mem::size_of_val(&boxed_rectangle)); 65 | println!("Упакованная 'упаковка' занимает {} байт на стеке", 66 | mem::size_of_val(&box_in_a_box)); 67 | 68 | // Копируем данные из `boxed_point` в `unboxed_point` 69 | let unboxed_point: Point = *boxed_point; 70 | println!("Распакованная точка занимает {} байт на стеке", 71 | mem::size_of_val(&unboxed_point)); 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /src/std/hash.md: -------------------------------------------------------------------------------- 1 | # HashMap 2 | 3 | В то время как вектора сохраняют значения с числовыми индексами, 4 | `HashMap` сохраняют значения по ключу. Ключи 5 | `HashMap` могут иметь логический, числовой, строковый 6 | или любой другой тип данных, который реализует типажи 7 | `Eq` и `Hash`. Подробнее об этом в 8 | следующей главе. 9 | 10 | Как и вектора, `HashMap` расширяемые, но они также 11 | могут и сжать себя, когда у них появляется избыточное пространство. 12 | Вы можете создать хэш-карту с определённой размерностью при 13 | помощи `HashMap::with_capacity(uint)` или использовать 14 | `HashMap::new()` для получения хэш-карты с 15 | размерностью по умолчанию (рекомендуется). 16 | 17 | ```rust,editable 18 | use std::collections::HashMap; 19 | 20 | fn call(number: &str) -> &str { 21 | match number { 22 | "798-1364" => "Абонент выключен или находится вне зоны действия сети. 23 | Пожалуйста, позвоните позднее.", 24 | "645-7689" => "Здравствуйте, это Mr. Awesome's Pizza. Меня зовут Фред. 25 | Что я могу сделать для вас?", 26 | _ => "Привет! Кто это опять?" 27 | } 28 | } 29 | 30 | fn main() { 31 | let mut contacts = HashMap::new(); 32 | 33 | contacts.insert("Даниель", "798-1364"); 34 | contacts.insert("Эшли", "645-7689"); 35 | contacts.insert("Кейти", "435-8291"); 36 | contacts.insert("Роберт", "956-1745"); 37 | 38 | // Возьмём ссылку и вернём `Option<&V>` 39 | match contacts.get(&"Даниель") { 40 | Some(&number) => println!("Звоним Даниелю: {}", call(number)), 41 | _ => println!("У нас нет номера Даниеля."), 42 | } 43 | 44 | // `HashMap::insert()` вернёт `None`, если мы добавляем 45 | // новое значение, иначе - `Some(value)` 46 | contacts.insert("Даниель", "164-6743"); 47 | 48 | match contacts.get(&"Эшли") { 49 | Some(&number) => println!("Звоним Эшли: {}", call(number)), 50 | _ => println!("У нас нет номера Эшли."), 51 | } 52 | 53 | contacts.remove(&"Эшли"); 54 | 55 | // `HashMap::iter()` возвращает итератор, который в произвольном 56 | // порядке отдаёт пары `(&'a key, &'a value)`. 57 | for (contact, &number) in contacts.iter() { 58 | println!("Звоним {}: {}", contact, call(number)); 59 | } 60 | } 61 | ``` 62 | 63 | Для большей информации о том, как работает хеширование и хэш-карты (который иногда называются хэш-таблицами), вы можете обратиться к [Wikipedia](https://en.wikipedia.org/wiki/Hash_table). 64 | -------------------------------------------------------------------------------- /src/std/option.md: -------------------------------------------------------------------------------- 1 | # `Option` 2 | 3 | Иногда желательно перехватить ошибку в какой-либо части программы 4 | вместо вызова паники с помощью макроса `panic!`. Это можно сделать 5 | с помощью перечисления `Option`. 6 | 7 | Перечисление `Option` имеет два варианта: 8 | 9 | * `None`, указывающий о наличии ошибки или отсутствия значений 10 | * `Some(value)`, кортежная структура, обёртка для `значения` типа `T`. 11 | 12 | ```rust,editable,ignore,mdbook-runnable 13 | // Целочисленное деление, которое не вызывает `panic!` 14 | fn checked_division(dividend: i32, divisor: i32) -> Option { 15 | if divisor == 0 { 16 | // В случае ошибки возвращаем `None` 17 | None 18 | } else { 19 | // Результат деления возвращаем в варианте `Some` 20 | Some(dividend / divisor) 21 | } 22 | } 23 | 24 | // Эта функция обрабатывает деление, которое может выполнится с ошибкой 25 | fn try_division(dividend: i32, divisor: i32) { 26 | // Значение типа `Option` могут быть сопоставлены по шаблону 27 | match checked_division(dividend, divisor) { 28 | None => println!("{} / {} вызвало ошибку!", dividend, divisor), 29 | Some(quotient) => { 30 | println!("{} / {} = {}", dividend, divisor, quotient) 31 | }, 32 | } 33 | } 34 | 35 | fn main() { 36 | try_division(4, 2); 37 | try_division(1, 0); 38 | 39 | // Привязка `None` к переменной должна быть аннотированной по типу 40 | let none: Option = None; 41 | let _equivalent_none = None::; 42 | 43 | let optional_float = Some(0f32); 44 | 45 | // Распаковка варианта `Some` будет извлекать данные, которые в нем находятся. 46 | println!("{:?} распаковывается в {:?}", optional_float, optional_float.unwrap()); 47 | 48 | // Распаковка варианта `None` вызовет `panic!` 49 | println!("{:?} распаковывается в {:?}", none, none.unwrap()); 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /src/std/panic.md: -------------------------------------------------------------------------------- 1 | # `panic!` 2 | 3 | Макрос `panic!` используется для генерации паники и раскрутки стека. 4 | Во время раскрутки стека, среда выполнения возьмёт на себя всю ответственность по 5 | освобождению ресурсов, которыми *владеет* текущий поток, вызывая деструкторы 6 | всех объектов. 7 | 8 | Так как в данном случае мы имеем дело с однопоточной программой, `panic!` заставит 9 | программу вывести сообщение с ошибкой и завершится. 10 | 11 | ```rust,editable,ignore,mdbook-runnable 12 | // Реализуем свою версию целочисленного деления (/) 13 | fn division(dividend: i32, divisor: i32) -> i32 { 14 | if divisor == 0 { 15 | // Деление на ноль вызывает панику 16 | panic!("Деление на ноль!"); 17 | } else { 18 | dividend / divisor 19 | } 20 | } 21 | 22 | // Основной поток `main` 23 | fn main() { 24 | // Целочисленное значение, выделенное в куче 25 | let _x = Box::new(0i32); 26 | 27 | // Это операция вызовет панику в основном потоке 28 | division(3, 0); 29 | 30 | println!("Эта часть кода не будет достигнута"); 31 | 32 | // `_x` должен быть уничтожен в этой точке 33 | } 34 | ``` 35 | 36 | Давайте убедимся, что `panic!` не приводит к утечки памяти. 37 | 38 | ```bash 39 | $ rustc panic.rs && valgrind ./panic 40 | ==4401== Memcheck, a memory error detector 41 | ==4401== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 42 | ==4401== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info 43 | ==4401== Command: ./panic 44 | ==4401== 45 | thread '

' panicked at 'division by zero', panic.rs:5 46 | ==4401== 47 | ==4401== HEAP SUMMARY: 48 | ==4401== in use at exit: 0 bytes in 0 blocks 49 | ==4401== total heap usage: 18 allocs, 18 frees, 1,648 bytes allocated 50 | ==4401== 51 | ==4401== All heap blocks were freed -- no leaks are possible 52 | ==4401== 53 | ==4401== For counts of detected and suppressed errors, rerun with: -v 54 | ==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 55 | ``` 56 | -------------------------------------------------------------------------------- /src/std/rc.md: -------------------------------------------------------------------------------- 1 | # `Rc` 2 | 3 | Когда необходимо множественное владение, может использоваться 4 | `Rc` (счётчик ссылок). `Rc` отслеживает 5 | количество ссылок, означающих количество владельцев значения, 6 | сохранённого в `Rc`. 7 | 8 | Количество ссылок на `Rc` увеличивается на 1 каждый 9 | раз, когда `Rc` клонируется, и уменьшается на 1, когда 10 | один из клонов выходит из области видимости и удаляется. Когда 11 | количество ссылок на `Rc` становится равным нулю, 12 | т.е. владельцев больше нет, и `Rc`, и значение 13 | удаляются. 14 | 15 | При клонировании `Rc` никогда не делается глубокая 16 | копия. Клонирование лишь создаёт другой указатель на обёрнутое 17 | значение и увеличивает счётчик. 18 | 19 | ```rust,editable 20 | use std::rc::Rc; 21 | 22 | fn main() { 23 | let rc_examples = "Пример с Rc".to_string(); 24 | { 25 | println!("--- Создана rc_a ---"); 26 | 27 | let rc_a: Rc = Rc::new(rc_examples); 28 | println!("Количество ссылок на rc_a: {}", Rc::strong_count(&rc_a)); 29 | 30 | { 31 | println!("--- rc_a клонировано в rc_b ---"); 32 | 33 | let rc_b: Rc = Rc::clone(&rc_a); 34 | println!("Количество ссылок на rc_b: {}", Rc::strong_count(&rc_b)); 35 | println!("Количество ссылок на rc_a: {}", Rc::strong_count(&rc_a)); 36 | 37 | // Два `Rc` равны, если равны их внутренние значения 38 | println!("rc_a и rc_b равны: {}", rc_a.eq(&rc_b)); 39 | 40 | // Мы можем напрямую использовать методы внутреннего значения 41 | println!("Размер значения внутри rc_a: {}", rc_a.len()); 42 | println!("Значение rc_b: {}", rc_b); 43 | 44 | println!("--- rc_b удаляется ---"); 45 | } 46 | 47 | println!("Количество ссылок на rc_a: {}", Rc::strong_count(&rc_a)); 48 | 49 | println!("--- rc_a удаляется ---"); 50 | } 51 | 52 | // Ошибка! `rc_examples` уже перемещена в `rc_a` 53 | // И когда `rc_a` удалилась, `rc_examples` удалилась вместе с ней 54 | // println!("rc_examples: {}", rc_examples); 55 | // TODO ^ Попробуйте удалить комментарий эту строку 56 | } 57 | ``` 58 | 59 | ### Смотрите также: 60 | 61 | [std::rc](https://doc.rust-lang.org/std/rc/index.html) и [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html). 62 | -------------------------------------------------------------------------------- /src/std/result.md: -------------------------------------------------------------------------------- 1 | # `Result` 2 | 3 | Раньше мы видели, что в качестве возвращаемого значения из 4 | функции, которая может завершиться с ошибкой, можно использовать 5 | перечисление `Option`, в котором `None` 6 | будет обозначать неудачу. Однако иногда важно понять 7 | *почему* операция потерпела неудачу. Для этого у нас есть 8 | перечисление `Result`. 9 | 10 | Перечисление `Result` имеет два варианта: 11 | 12 | - `Ok(value)`, который обозначает, что операция успешно завершилась, и оборачивает значение (`value`), возвращаемое операцией (`value` имеет тип `T`). 13 | - `Err(why)`, который показывает, что операция потерпела неудачу, оборачивает значение ошибки (причину, `why`), которое (надеемся) описывает причину неудачи. `why` имеет тип `E`. 14 | 15 | ```rust,editable,ignore,mdbook-runnable 16 | mod checked { 17 | // Математические "ошибки", которые мы хотим отлавливать 18 | #[derive(Debug)] 19 | pub enum MathError { 20 | DivisionByZero, 21 | NonPositiveLogarithm, 22 | NegativeSquareRoot, 23 | } 24 | 25 | pub type MathResult = Result; 26 | 27 | pub fn div(x: f64, y: f64) -> MathResult { 28 | if y == 0.0 { 29 | // При таком значение операция потерпит неудачу. 30 | // Вместо этого давайте вернём ошибку, обёрнутую в `Err` 31 | Err(MathError::DivisionByZero) 32 | } else { 33 | // Эта операция возможна, так что вернём результат, обёрнутый в `Ok` 34 | Ok(x / y) 35 | } 36 | } 37 | 38 | pub fn sqrt(x: f64) -> MathResult { 39 | if x < 0.0 { 40 | Err(MathError::NegativeSquareRoot) 41 | } else { 42 | Ok(x.sqrt()) 43 | } 44 | } 45 | 46 | pub fn ln(x: f64) -> MathResult { 47 | if x <= 0.0 { 48 | Err(MathError::NonPositiveLogarithm) 49 | } else { 50 | Ok(x.ln()) 51 | } 52 | } 53 | } 54 | 55 | // `op(x, y)` === `sqrt(ln(x / y))` 56 | fn op(x: f64, y: f64) -> f64 { 57 | // Это трёхуровневая пирамида из `match`! 58 | match checked::div(x, y) { 59 | Err(why) => panic!("{:?}", why), 60 | Ok(ratio) => match checked::ln(ratio) { 61 | Err(why) => panic!("{:?}", why), 62 | Ok(ln) => match checked::sqrt(ln) { 63 | Err(why) => panic!("{:?}", why), 64 | Ok(sqrt) => sqrt, 65 | }, 66 | }, 67 | } 68 | } 69 | 70 | fn main() { 71 | // Потерпит ли это неудачу? 72 | println!("{}", op(1.0, 10.0)); 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /src/std/result/question_mark.md: -------------------------------------------------------------------------------- 1 | # `?` 2 | 3 | Разбор цепочки результатов с использованием `match` может стать 4 | довольно неопрятной, к счастью, с помощью оператора 5 | `?` можно сделать разбор снова красивым. 6 | `?` используется в конце выражения, возвращающего 7 | `Result` и эквивалентен выражению `match`, в котором 8 | ветка `Err(err)` разворачивается в 9 | `Err(From::from(err))`, а ветка `Ok(ok)` во 10 | внутреннее значение (`ok`). 11 | 12 | ```rust,editable,ignore,mdbook-runnable 13 | mod checked { 14 | #[derive(Debug)] 15 | enum MathError { 16 | DivisionByZero, 17 | NonPositiveLogarithm, 18 | NegativeSquareRoot, 19 | } 20 | 21 | type MathResult = Result; 22 | 23 | fn div(x: f64, y: f64) -> MathResult { 24 | if y == 0.0 { 25 | Err(MathError::DivisionByZero) 26 | } else { 27 | Ok(x / y) 28 | } 29 | } 30 | 31 | fn sqrt(x: f64) -> MathResult { 32 | if x < 0.0 { 33 | Err(MathError::NegativeSquareRoot) 34 | } else { 35 | Ok(x.sqrt()) 36 | } 37 | } 38 | 39 | fn ln(x: f64) -> MathResult { 40 | if x <= 0.0 { 41 | Err(MathError::NonPositiveLogarithm) 42 | } else { 43 | Ok(x.ln()) 44 | } 45 | } 46 | 47 | // Промежуточная функция 48 | fn op_(x: f64, y: f64) -> MathResult { 49 | // Если `div` "упадёт", тогда будет "возвращено" `DivisionByZero` 50 | let ratio = div(x, y)?; 51 | 52 | // если `ln` "упадёт", тогда будет "возвращено" `NonPositiveLogarithm` 53 | let ln = ln(ratio)?; 54 | 55 | sqrt(ln) 56 | } 57 | 58 | pub fn op(x: f64, y: f64) { 59 | match op_(x, y) { 60 | Err(why) => panic!(match why { 61 | MathError::NonPositiveLogarithm 62 | => "логарифм не положительного числа", 63 | MathError::DivisionByZero 64 | => "деление на ноль", 65 | MathError::NegativeSquareRoot 66 | => "квадратный корень от отрицательного числа", 67 | }), 68 | Ok(value) => println!("{}", value), 69 | } 70 | } 71 | } 72 | 73 | fn main() { 74 | checked::op(1.0, 10.0); 75 | } 76 | ``` 77 | 78 | Обязательно посмотрите [документацию](https://doc.rust-lang.org/std/result/index.html), так как есть много 79 | методов для работы с `Result`. 80 | -------------------------------------------------------------------------------- /src/std/vec.md: -------------------------------------------------------------------------------- 1 | # Вектора 2 | 3 | Вектора - это массивы изменяемого размера. Их размер, как и у 4 | срезов, не известен во время компиляции, но он может в любое 5 | время расширяться. Вектора представляются при помощи 3 6 | параметров: 7 | 8 | - указатель на данные 9 | - длина 10 | - вместимость 11 | 12 | Вместимость показывает сколько памяти зарезервировано для 13 | вектора. Вектор может расти до тех пор, пока его длина меньше 14 | вместимости. Если при следующей вставке порог может быть 15 | превышен, под вектор выделяется больше памяти и данные переносятся в новый вектор. 16 | 17 | ```rust,editable,ignore,mdbook-runnable 18 | fn main() { 19 | // Итераторы могут быть собраны в вектора 20 | let collected_iterator: Vec = (0..10).collect(); 21 | println!("(0..10) собраны в: {:?}", collected_iterator); 22 | 23 | // Макрос `vec!` может быть использован для инициализации вектора 24 | let mut xs = vec![1i32, 2, 3]; 25 | println!("Исходный вектор: {:?}", xs); 26 | 27 | // Вставка нового элемента в конец вектора 28 | println!("Добавим 4 в конец вектора"); 29 | xs.push(4); 30 | println!("Вектор: {:?}", xs); 31 | 32 | // Ошибка! Неизменяемые вектора не могут увеличиваться 33 | collected_iterator.push(0); 34 | // ИСПРАВЬТЕ ^ Закомментируйте эту строку 35 | 36 | // Метод `len` отдаёт количество элементом, сохранённых в векторе 37 | println!("Длина вектора: {}", xs.len()); 38 | 39 | // Индексация выполняется при помощи квадратных скобок (индексация начинается с 0) 40 | println!("Второй элемент: {}", xs[1]); 41 | 42 | // `pop` удаляет последний элемент из вектора и возвращает его 43 | println!("Последний элемент: {:?}", xs.pop()); 44 | 45 | // Выход за пределы индексации вызывает панику 46 | println!("Четвёртый элемент: {}", xs[3]); 47 | // ИСПРАВЬТЕ ^ Закомментируйте эту строку 48 | 49 | // По векторами легко итерироваться 50 | println!("Содержимое `xs`:"); 51 | for x in xs.iter() { 52 | println!("> {}", x); 53 | } 54 | 55 | // Также можно итерироваться по вектору с получением индекса элемента 56 | // (который будет содержаться в отдельной переменной `i`) 57 | for (i, x) in xs.iter().enumerate() { 58 | println!("{}-ый элемент имеет значение {}", i, x); 59 | } 60 | 61 | // Благодаря `iter_mut`, у изменяемых векторов можно менять значения 62 | // во время итерирования 63 | for x in xs.iter_mut() { 64 | *x *= 3; 65 | } 66 | println!("Обновлённый вектор: {:?}", xs); 67 | } 68 | ``` 69 | 70 | Подробную информацию о методах объекта Vec 71 | можно почитать в разделе модуля std::vec 72 | -------------------------------------------------------------------------------- /src/std_misc.md: -------------------------------------------------------------------------------- 1 | # Разное в стандартной библиотеке 2 | 3 | Многие другие типы предоставляются стандартной библиотекой 4 | для вспомогательных целей, например: 5 | 6 | - Потоки 7 | - Каналы 8 | - Операции файлового ввода/вывода 9 | 10 | Они расширяют возможности, которые предоставляют [примитивы](primitives.html). 11 | 12 | ### Смотрите также: 13 | 14 | [примитивы](primitives.html) и [стандартная библиотека](https://doc.rust-lang.org/std/) 15 | -------------------------------------------------------------------------------- /src/std_misc/arg.md: -------------------------------------------------------------------------------- 1 | # Аргументы программы 2 | 3 | ## Стандартная библиотека 4 | 5 | Аргументы командной строки могут быть доступны при помощи 6 | `std::env::args`, который возвращает итератор, который 7 | выдаёт `String` для каждого аргумента: 8 | 9 | ```rust,editable 10 | use std::env; 11 | 12 | fn main() { 13 | let args: Vec = env::args().collect(); 14 | 15 | // Первый аргумент - путь, используемый для вызова программы. 16 | println!("Мой путь {}.", args[0]); 17 | 18 | // Оставшиеся аргументы - переданные в командной строке параметры. 19 | // Вызов программы выглядит так: 20 | // $ ./args arg1 arg2 21 | println!("У меня {:?} аргумента: {:?}.", args.len() - 1, &args[1..]); 22 | } 23 | ``` 24 | 25 | ```shell 26 | $ ./args 1 2 3 27 | Мой путь ./args. 28 | У меня 3 аргумента: ["1", "2", "3"]. 29 | ``` 30 | 31 | ## Крейты 32 | 33 | В качестве альтернативы, существует несколько крейтов, которые 34 | предоставляют дополнительную функциональность при создании 35 | приложений командной сроки. [Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/cli/arguments.html) показывает 36 | лучшие практики, как использовать один из самых популярных 37 | крейтов для аргументов командной строки, `clap`. 38 | -------------------------------------------------------------------------------- /src/std_misc/channels.md: -------------------------------------------------------------------------------- 1 | # Каналы 2 | 3 | Rust предоставляет асинхронные каналы (`channel`) для 4 | взаимодействия между потоками. Каналы обеспечивают 5 | однонаправленную передачу информации между двумя конечными 6 | точками: отправителем (`Sender`) и получателем 7 | (`Receiver`). 8 | 9 | ```rust,editable 10 | use std::sync::mpsc::{Sender, Receiver}; 11 | use std::sync::mpsc; 12 | use std::thread; 13 | 14 | static NTHREADS: i32 = 3; 15 | 16 | fn main() { 17 | // Каналы имеют две конечные точки: Sender` и `Receiver`, 18 | // где `T` - тип передаваемового сообщения. 19 | // (аннотации типов избыточны) 20 | let (tx, rx): (Sender, Receiver) = mpsc::channel(); 21 | let mut children = Vec::new(); 22 | 23 | for id in 0..NTHREADS { 24 | // Отправитель может быть скопирован 25 | let thread_tx = tx.clone(); 26 | 27 | // Каждый поток отправит через канал его id 28 | let child = thread::spawn(move || { 29 | // Поток забирает владение `thread_tx` 30 | // Каждый поток добавляет своё сообщение в очередь канала 31 | thread_tx.send(id).unwrap(); 32 | 33 | // Отправка - не блокирующая операция, поток незамедлительно 34 | // продолжит работу после отправки сообщения 35 | println!("поток {} завершён", id); 36 | }); 37 | 38 | children.push(child); 39 | } 40 | 41 | // Здесь все сообщения собираются 42 | let mut ids = Vec::with_capacity(NTHREADS as usize); 43 | for _ in 0..NTHREADS { 44 | // Метод `recv` "достаёт" сообщения из канала 45 | // `recv` блокирует текущий поток, если доступных сообщений нет 46 | ids.push(rx.recv()); 47 | } 48 | 49 | // Ожидаем, когда потоки завершат всю оставшуюся работу 50 | for child in children { 51 | child.join().expect("Упс! Дочерний поток паникует"); 52 | } 53 | 54 | // Посмотрите порядок, с которым сообщения были отправлeны 55 | println!("{:?}", ids); 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /src/std_misc/ffi.md: -------------------------------------------------------------------------------- 1 | # Foreign Function Interface 2 | 3 | Rust предоставляет интерфейс внешних функций (Foreign Function 4 | Interface, FFI) к библиотекам, написанным на языке С. Внешние 5 | функции должны быть объявлены внутри блока `extern` 6 | и аннотированы при помощи атрибута `#[link]`, который 7 | содержит имя внешней библиотеки. 8 | 9 | ```rust,ignore 10 | use std::fmt; 11 | 12 | // Этот extern-блок подключает библиотеку libm 13 | #[link(name = "m")] 14 | extern { 15 | // Это внешняя функция, которая считает квадратный корень 16 | // комплексного числа одинарной точности 17 | fn csqrtf(z: Complex) -> Complex; 18 | 19 | fn ccosf(z: Complex) -> Complex; 20 | } 21 | 22 | // Так как вызовы внешних функций считаются unsafe, 23 | // принято писать над ними обёртки. 24 | fn cos(z: Complex) -> Complex { 25 | unsafe { ccosf(z) } 26 | } 27 | 28 | fn main() { 29 | // z = -1 + 0i 30 | let z = Complex { re: -1., im: 0. }; 31 | 32 | // вызов внешней функции - unsafe операция 33 | let z_sqrt = unsafe { csqrtf(z) }; 34 | 35 | println!("квадратный корень от {:?} равен {:?}", z, z_sqrt); 36 | 37 | // вызов безопасного API в котором находится unsafe операция 38 | println!("cos({:?}) = {:?}", z, cos(z)); 39 | } 40 | 41 | // Минимальная реализация комплексного числа одинарной точности 42 | #[repr(C)] 43 | #[derive(Clone, Copy)] 44 | struct Complex { 45 | re: f32, 46 | im: f32, 47 | } 48 | 49 | impl fmt::Debug for Complex { 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | if self.im < 0. { 52 | write!(f, "{}-{}i", self.re, -self.im) 53 | } else { 54 | write!(f, "{}+{}i", self.re, self.im) 55 | } 56 | } 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /src/std_misc/file.md: -------------------------------------------------------------------------------- 1 | # Файловый ввод-вывод 2 | 3 | Структура `File` представляет открытый файл (она является обёрткой над файловым дескриптором) и даёт возможность чтения/записи этого файла. 4 | 5 | Из-за того, что многие вещи могут пойти не так в процессе файлового 6 | ввода-вывода, все методы `File` возвращают тип 7 | `io::Result`, который является псевдонимом для 8 | `Result`. 9 | 10 | Это делает *явными* ошибки всех операций ввода-вывода. 11 | Благодаря этому, программист может увидеть все пути отказов и 12 | обрабатывать их упреждающей форме. 13 | -------------------------------------------------------------------------------- /src/std_misc/file/create.md: -------------------------------------------------------------------------------- 1 | # `create` 2 | 3 | Статический метод `create` открывает файл в режиме 4 | только для записи. Если файл уже существует, то его содержимое 5 | уничтожится, в противном же случае, создастся новый файл. 6 | 7 | ```rust,ignore 8 | static LOREM_IPSUM: &str = 9 | "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 10 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 11 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 12 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 13 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 14 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 15 | "; 16 | 17 | use std::error::Error; 18 | use std::fs::File; 19 | use std::io::prelude::*; 20 | use std::path::Path; 21 | 22 | fn main() { 23 | let path = Path::new("out/lorem_ipsum.txt"); 24 | let display = path.display(); 25 | 26 | // Откроем файл в режиме для записи. Возвращается `io::Result` 27 | let mut file = match File::create(&path) { 28 | Err(why) => panic!("невозможно создать {}: {}", display, why.description()), 29 | Ok(file) => file, 30 | }; 31 | 32 | // Запишем строку `LOREM_IPSUM` в `file`. Возвращается `io::Result<()>` 33 | match file.write_all(LOREM_IPSUM.as_bytes()) { 34 | Err(why) => panic!("невозможно записать в {}: {}", display, why.description()), 35 | Ok(_) => println!("успешно записано в {}", display), 36 | } 37 | } 38 | ``` 39 | 40 | Вот расширенный ожидаемый результат: 41 | 42 | ```shell 43 | $ mkdir out 44 | $ rustc create.rs && ./create 45 | успешно записано в out/lorem_ipsum.txt 46 | $ cat out/lorem_ipsum.txt 47 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 48 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 49 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 50 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 51 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 52 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 53 | ``` 54 | 55 | (Как и в предыдущем примере, предлагаем вам протестировать этот 56 | код с различными вариантами отказа.) 57 | 58 | Существует структура [`OpenOptions`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html), которая 59 | может использоваться для настройки того, как файл будет открыт. 60 | -------------------------------------------------------------------------------- /src/std_misc/file/open.md: -------------------------------------------------------------------------------- 1 | # `open` 2 | 3 | Статический метод `open` может использоваться для открытия файла в режиме только для чтения. 4 | 5 | Структура `File` владеет ресурсом, файловым 6 | дескриптором, и заботится о том, чтобы он был закрыт, когда 7 | структура удаляется из памяти. 8 | 9 | ```rust,editable,ignore 10 | use std::error::Error; 11 | use std::fs::File; 12 | use std::io::prelude::*; 13 | use std::path::Path; 14 | 15 | fn main() { 16 | // Создадим "путь" к нужному файлу 17 | let path = Path::new("hello.txt"); 18 | let display = path.display(); 19 | 20 | // Откроем "путь" в режиме "только чтение". Возвращается `io::Result` 21 | let mut file = match File::open(&path) { 22 | // Метод `description` у `io::Error` возвращает строку, 23 | // которая описывает ошибку 24 | Err(why) => panic!("невозможно открыть {}: {}", display, 25 | why.description()), 26 | Ok(file) => file, 27 | }; 28 | 29 | // Читаем содержимое файла в строку. Метод возвращает `io::Result` 30 | let mut s = String::new(); 31 | match file.read_to_string(&mut s) { 32 | Err(why) => panic!("невозможно прочесть {}: {}", display, 33 | why.description()), 34 | Ok(_) => print!("{} содержит:\n{}", display, s), 35 | } 36 | 37 | // `file` выходит из области видимости и файл "hello.txt" закрывается 38 | } 39 | 40 | ``` 41 | 42 | Вот ожидаемый результат: 43 | 44 | ```shell 45 | $ echo "Hello World!" > hello.txt 46 | $ rustc open.rs && ./open 47 | hello.txt содержит: 48 | Hello World! 49 | ``` 50 | 51 | (Рекомендуем протестировать предыдущий пример при различных 52 | условиях сбоев: файл `hello.txt` не существует или 53 | `hello.txt` не читаемый и другое) 54 | -------------------------------------------------------------------------------- /src/std_misc/file/read_lines.md: -------------------------------------------------------------------------------- 1 | # `read_lines` 2 | 3 | Метод `lines()` возвращает итератор, проходящий через 4 | все строки файла. 5 | 6 | `File::open` работает с чем-то, что реализует типаж `AsRef`. Поэтому `read_lines()` будет ожидать это же. 7 | 8 | ```rust,no_run 9 | use std::fs::File; 10 | use std::io::{self, BufRead}; 11 | use std::path::Path; 12 | 13 | fn main() { 14 | // Файл `hosts` должен существовать в текущей директории 15 | if let Ok(lines) = read_lines("./hosts") { 16 | // Получает итератор, который возвращает Option 17 | for line in lines { 18 | if let Ok(ip) = line { 19 | println!("{}", ip); 20 | } 21 | } 22 | } 23 | } 24 | 25 | // Для обработки ошибок, возвращаемое значение оборачивается в Result 26 | // Возвращаем `Iterator` для построчного чтения файла. 27 | fn read_lines

(filename: P) -> io::Result>> 28 | where P: AsRef, { 29 | let file = File::open(filename)?; 30 | Ok(io::BufReader::new(file).lines()) 31 | } 32 | ``` 33 | 34 | Запуск этой программы просто выводит эти строки на экран по 35 | отдельности. 36 | 37 | ```shell 38 | $ echo -e "127.0.0.1\n192.168.0.1\n" > hosts 39 | $ rustc read_lines.rs && ./read_lines 40 | 127.0.0.1 41 | 192.168.0.1 42 | ``` 43 | 44 | Такой подход более эффективен, чем создание `String` в памяти, особенно при работе с большими файлами. 45 | -------------------------------------------------------------------------------- /src/std_misc/path.md: -------------------------------------------------------------------------------- 1 | # Path 2 | 3 | Структура `Path` представляет пути к файлу в файловой 4 | системе. Есть два вида `Path`: `posix::Path`, 5 | для UNIX - подобных систем, и `windows::Path`, для 6 | Windows. В прелюдии экспортируется соответствующий 7 | платформозависимый вариант `Path`. 8 | 9 | `Path` может быть создан из `OsStr`, и 10 | предоставляет некоторые методы для получения информации о 11 | файле или директории, на которые он указывает. 12 | 13 | Обратите внимание, что внутренне представление 14 | `Path` *не является* UTF-8 строкой, но вместо 15 | этого хранит вектор байт (`Vec`). 16 | Следовательно, преобразование `Path` в 17 | `&str` *не* бесплатно и может закончиться 18 | неудачей (возвращается `Option`). 19 | 20 | ```rust,editable 21 | use std::path::Path; 22 | 23 | fn main() { 24 | // Создаём `Path` из `&'static str` 25 | let path = Path::new("."); 26 | 27 | // Метод `display` возвращает показываемую структуру 28 | let _display = path.display(); 29 | 30 | // `join` соединяет `path` с байтовым контейнером, используя ОС-специфичный 31 | // разделитель, и возвращает новый путь 32 | let new_path = path.join("a").join("b"); 33 | 34 | // Конвертируем путь в строковый срез 35 | match new_path.to_str() { 36 | None => panic!("новый путь не является действительной UTF-8 последовательностью"), 37 | Some(s) => println!("новый путь {}", s), 38 | } 39 | } 40 | 41 | ``` 42 | 43 | Не забудьте проверить остальные методы `Path` 44 | (`posix::Path` или `windows::Path`) и 45 | структуры `Metadata`. 46 | 47 | ### Смотрите также: 48 | 49 | [OsStr](https://doc.rust-lang.org/std/ffi/struct.OsStr.html) и [Metadata](https://doc.rust-lang.org/std/fs/struct.Metadata.html). 50 | -------------------------------------------------------------------------------- /src/std_misc/process.md: -------------------------------------------------------------------------------- 1 | # Дочерние процессы 2 | 3 | Структура `process::Output` представляет результат завершённого дочернего процесса, 4 | и структура `process::Command` - это строитель процесса. 5 | 6 | ```rust,editable,ignore 7 | use std::process::Command; 8 | 9 | fn main() { 10 | let output = Command::new("rustc") 11 | .arg("--version") 12 | .output().unwrap_or_else(|e| { 13 | panic!("Ошибка выполнения процесса {}", e) 14 | }); 15 | 16 | if output.status.success() { 17 | let s = String::from_utf8_lossy(&output.stdout); 18 | 19 | print!("rustc завершился успешно и вывел в stdout:\n{}", s); 20 | } else { 21 | let s = String::from_utf8_lossy(&output.stderr); 22 | 23 | print!("rustc завершился с ошибкой и вывел в stderr:\n{}", s); 24 | } 25 | } 26 | ``` 27 | 28 | (Рекомендуется попробовать предыдущий пример с неправильным флагом обращения к `rustc`) 29 | -------------------------------------------------------------------------------- /src/std_misc/process/pipe.md: -------------------------------------------------------------------------------- 1 | # Pipes 2 | 3 | Структура `std::Child` представляет собой запущенный 4 | дочерний процесс и предоставляет дескрипторы `stdin`, 5 | `stdout` и `stderr` для взаимодействия с 6 | этим процессом через каналы (pipes). 7 | 8 | ```rust,ignore 9 | use std::error::Error; 10 | use std::io::prelude::*; 11 | use std::process::{Command, Stdio}; 12 | 13 | static PANGRAM: &'static str = 14 | "the quick brown fox jumped over the lazy dog\n"; 15 | 16 | fn main() { 17 | // Создадим команду `wc` 18 | let process = match Command::new("wc") 19 | .stdin(Stdio::piped()) 20 | .stdout(Stdio::piped()) 21 | .spawn() { 22 | Err(why) => panic!("не удалось создать wc: {}", why.description()), 23 | Ok(process) => process, 24 | }; 25 | 26 | // Запишем строку в `stdin` созданной команды. 27 | // 28 | // `stdin` имеет тип `Option`, но так как мы знаем, что экземпляр должен быть только один, 29 | // мы можем напрямую вызвать `unwrap`. 30 | match process.stdin.unwrap().write_all(PANGRAM.as_bytes()) { 31 | Err(why) => panic!("не удалось записать в stdin команды wc: {}", 32 | why.description()), 33 | Ok(_) => println!("пангамма отправлена"), 34 | } 35 | 36 | // Так как `stdin` не существует после вышележащих вызовов, он разрушается 37 | // и канал закрывается. 38 | // 39 | // Это очень важно, иначе `wc` не начал бы обработку только что 40 | // отправленных данных. 41 | 42 | // Поле `stdout` имеет тип `Option` и может быть извлечено. 43 | let mut s = String::new(); 44 | match process.stdout.unwrap().read_to_string(&mut s) { 45 | Err(why) => panic!("невозможно прочесть stdout команды wc: {}", 46 | why.description()), 47 | Ok(_) => print!("wc ответил:\n{}", s), 48 | } 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /src/std_misc/process/wait.md: -------------------------------------------------------------------------------- 1 | # Ожидание 2 | 3 | Если вы хотите дождаться завершения `process::Child`, вы должны вызвать `Child::wait`, который вернёт `process::ExitStatus`. 4 | 5 | ```rust,ignore 6 | use std::process::Command; 7 | 8 | fn main() { 9 | let mut child = Command::new("sleep").arg("5").spawn().unwrap(); 10 | let _result = child.wait().unwrap(); 11 | 12 | println!("достигнут конец функции main"); 13 | } 14 | ``` 15 | 16 | ```bash 17 | $ rustc wait.rs && ./wait 18 | # `wait` продолжает работать в течение 5 секунд, пока команда `sleep 5` не завершится 19 | достигнут конец функции main 20 | ``` 21 | -------------------------------------------------------------------------------- /src/std_misc/threads.md: -------------------------------------------------------------------------------- 1 | # Потоки 2 | 3 | Rust предоставляет механизм для создания собственных потоков операционной системы через функцию `spawn`. Аргументом этой функции является замыкание, которое принимает владение захваченным ею окружением. 4 | 5 | ```rust,editable 6 | use std::thread; 7 | 8 | static NTHREADS: i32 = 10; 9 | 10 | // Это главный поток `main` 11 | fn main() { 12 | // Создаём вектор дочерних потоков. 13 | let mut children = vec![]; 14 | 15 | for i in 0..NTHREADS { 16 | // Создаём очередной поток 17 | children.push(thread::spawn(move || { 18 | println!("этот поток номер {}", i); 19 | })); 20 | } 21 | 22 | for child in children { 23 | // Ждём пока поток завершится и вернёт результат. 24 | let _ = child.join(); 25 | } 26 | } 27 | ``` 28 | 29 | Эти потоки будут запланированы ОС. 30 | -------------------------------------------------------------------------------- /src/testing.md: -------------------------------------------------------------------------------- 1 | # Тестирование 2 | 3 | Rust - это язык программирования, который очень заботится о корректности и 4 | включает в себя поддержку написания тестов программного обеспечения в самом языке. 5 | 6 | Тестирование поставляется в трёх стилях: 7 | 8 | - [Модульное](testing/unit_testing.md) тестирование. 9 | - [Тестирование кода из примеров документации](testing/doc_testing.md). 10 | - [Интеграционное тестирование](testing/integration_testing.md). 11 | 12 | Также Rust поддерживает указание дополнительных зависимостей для тестов: 13 | 14 | - [Dev-dependencies](testing/dev_dependencies.md) 15 | 16 | ## Смотрите также: 17 | 18 | - [Глава о тестировании](https://doc.rust-lang.org/book/ch11-00-testing.html) в "The Rust Programming Language" 19 | - [Описание API](https://rust-lang-nursery.github.io/api-guidelines/documentation.html) для тестирования примеров из документации. 20 | -------------------------------------------------------------------------------- /src/testing/dev_dependencies.md: -------------------------------------------------------------------------------- 1 | # `dev-dependencies` 2 | 3 | Иногда возникает необходимость иметь зависимости только для тестов (примеры, бенчмарки). Такие зависимости добавляются в `Cargo.toml` в секцию 4 | `[dev-dependencies]`. Эти зависимости не распространяются как зависимости на другие пакеты, которые зависят от этого пакета. 5 | 6 | Одним из таких примеров является пакет расширяющий стандартный макрос `assert!`. Файл `Cargo.toml`: 7 | 8 | ```ignore 9 | # при стандартной сборке проекта данная зависимость не будет использоваться. 10 | [dev-dependencies] 11 | pretty_assertions = "0.4.0" 12 | ``` 13 | 14 | Файл `src/lib.rs`: 15 | 16 | ```rust,ignore 17 | // внешний пакет используется только для тестирования 18 | #[cfg(test)] 19 | #[macro_use] 20 | extern crate pretty_assertions; 21 | 22 | pub fn add(a: i32, b: i32) -> i32 { 23 | a + b 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use super::*; 29 | 30 | #[test] 31 | fn test_add() { 32 | assert_eq!(add(2, 3), 5); 33 | } 34 | } 35 | ``` 36 | 37 | ## Смотрите также: 38 | 39 | Документация [Cargo](http://doc.crates.io/specifying-dependencies.html) по указанию зависимостей. 40 | -------------------------------------------------------------------------------- /src/testing/integration_testing.md: -------------------------------------------------------------------------------- 1 | # Интеграционное тестирование 2 | 3 | [Модульные тесты](unit_testing.md) тестируют по одному модулю изолированно: они малы 4 | и могут проверить не публичный код. Интеграционные тесты являются внешними для вашего пакета и используют 5 | только его открытый интерфейс, таким же образом, как и любой другой код. Их цель в том, чтобы проверить, что многие части вашей библиотеки работают корректно вместе. 6 | 7 | Cargo ищет интеграционные тесты в каталоге `tests` после каталога `src`. 8 | 9 | Файл `src/lib.rs`: 10 | 11 | ```rust,ignore 12 | // Предположим, что наш пакет называется `adder`, для теста он будет внешним кодом. 13 | pub fn add(a: i32, b: i32) -> i32 { 14 | a + b 15 | } 16 | ``` 17 | 18 | Файл с тестом: `tests/integration_test.rs`: 19 | 20 | ```rust,ignore 21 | // мы тестируем extern crate, как и любой другой код. 22 | extern crate adder; 23 | 24 | #[test] 25 | fn test_add() { 26 | assert_eq!(adder::add(3, 2), 5); 27 | } 28 | ``` 29 | 30 | Запустить тесты можно командой `cargo test`: 31 | 32 | ```shell 33 | $ cargo test 34 | running 0 tests 35 | 36 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 37 | 38 | Running target/debug/deps/integration_test-bcd60824f5fbfe19 39 | 40 | running 1 test 41 | test test_add ... ok 42 | 43 | test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 44 | 45 | Doc-tests adder 46 | 47 | running 0 tests 48 | 49 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 50 | ``` 51 | 52 | Каждый файл с исходным кодом в директории `tests` компилируется в отдельный пакет. 53 | Один из путей использовать некоторый общий код между интеграционными тестами - создать модуль с публичными функциями и импортировать их в тестах. 54 | 55 | Файл `tests/common.rs`: 56 | 57 | ```rust,ignore 58 | pub fn setup() { 59 | // некоторый код для настройки, создание необходимых файлов/каталогов, запуск серверов. 60 | } 61 | ``` 62 | 63 | Файл с тестом: `tests/integration_test.rs` 64 | 65 | ```rust,ignore 66 | // мы тестируем extern crate, как и любой другой код. 67 | extern crate adder; 68 | 69 | // импорт общего модуля. 70 | mod common; 71 | 72 | #[test] 73 | fn test_add() { 74 | // использование общего кода. 75 | common::setup(); 76 | assert_eq!(adder::add(3, 2), 5); 77 | } 78 | ``` 79 | 80 | Модули с общим кодом следуют обычным правилам [модулей](../mod.md). Общий модуль можно создать как `tests/common/mod.rs`. 81 | -------------------------------------------------------------------------------- /src/trait.md: -------------------------------------------------------------------------------- 1 | # Типажи 2 | 3 | `Типаж (trait)` - это набор методов, определённых для неизвестного типа: 4 | `Self`. Они могут получать доступ к другим методам, 5 | которые были объявлены в том же типаже. 6 | 7 | Типажи могут быть реализованы для любых типов данных. В примере ниже, 8 | мы определили группу методов `Animal`. Типаж `Animal` реализован для типа данных 9 | `Sheep`, что позволяет использовать методы из `Animal` внутри `Sheep`. 10 | 11 | ```rust,editable 12 | struct Sheep { naked: bool, name: &'static str } 13 | 14 | trait Animal { 15 | // Сигнатура статического метода, `Self` ссылается на реализующий тип. 16 | fn new(name: &'static str) -> Self; 17 | 18 | // Сигнатура метода экземпляра; они возвращают строки. 19 | fn name(&self) -> &'static str; 20 | fn noise(&self) -> &'static str; 21 | 22 | // Типаж может содержать определение метода по умолчанию 23 | fn talk(&self) { 24 | println!("{} говорит {}", self.name(), self.noise()); 25 | } 26 | } 27 | 28 | impl Sheep { 29 | fn is_naked(&self) -> bool { 30 | self.naked 31 | } 32 | 33 | fn shear(&mut self) { 34 | if self.is_naked() { 35 | // Методы типа могут использовать методы типажа, реализованного для этого типа. 36 | println!("{} уже без волос...", self.name()); 37 | } else { 38 | println!("{} подстригается!", self.name); 39 | 40 | self.naked = true; 41 | } 42 | } 43 | } 44 | 45 | // Реализуем типаж `Animal` для `Sheep`. 46 | impl Animal for Sheep { 47 | // `Self` реализующий тип: `Sheep`. 48 | fn new(name: &'static str) -> Sheep { 49 | Sheep { name: name, naked: false } 50 | } 51 | 52 | fn name(&self) -> &'static str { 53 | self.name 54 | } 55 | 56 | fn noise(&self) -> &'static str { 57 | if self.is_naked() { 58 | "baaaaah?" 59 | } else { 60 | "baaaaah!" 61 | } 62 | } 63 | 64 | // Методы по умолчанию могут быть переопределены. 65 | fn talk(&self) { 66 | // Например, мы добавили немного спокойного миросозерцания... 67 | println!("{} делает паузу... {}", self.name, self.noise()); 68 | } 69 | } 70 | 71 | fn main() { 72 | // Аннотация типа в данном случае необходима. 73 | let mut dolly: Sheep = Animal::new("Dolly"); 74 | // ЗАДАНИЕ ^ Попробуйте убрать аннотацию типа 75 | 76 | dolly.talk(); 77 | dolly.shear(); 78 | dolly.talk(); 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /src/trait/clone.md: -------------------------------------------------------------------------------- 1 | # Типаж Clone 2 | 3 | При работе с ресурсами, стандартным поведением является передача их (ресурсов) 4 | в ходе выполнения или вызов функции. Однако, иногда нам нужно 5 | также объявить копию ресурса. 6 | 7 | Типаж [`Clone`][clone] помогает нам сделать именно это. Чаще всего, мы можем 8 | использовать метод `.clone()` объявленный типажом `Clone`. 9 | 10 | ```rust,editable 11 | // Единичная структура без ресурсов 12 | #[derive(Debug, Clone, Copy)] 13 | struct Nil; 14 | 15 | // Кортежная структура с ресурсами, которая реализует типаж `Clone` 16 | #[derive(Clone, Debug)] 17 | struct Pair(Box, Box); 18 | 19 | fn main() { 20 | // Объявим экземпляр `Nil` 21 | let nil = Nil; 22 | // Скопируем `Nil`, который не имеет ресурсов для перемещения 23 | let copied_nil = nil; 24 | 25 | // Оба `Nil`s могут быть использованы независимо 26 | println!("оригинал: {:?}", nil); 27 | println!("копия: {:?}", copied_nil); 28 | 29 | // Объявим экземпляр `Pair` 30 | let pair = Pair(Box::new(1), Box::new(2)); 31 | println!("оригинал: {:?}", pair); 32 | 33 | // Скопируем `pair` в `moved_pair`, перенаправляя ресурсы 34 | let moved_pair = pair; 35 | println!("копия: {:?}", moved_pair); 36 | 37 | // Ошибка! `pair` потеряла свои ресурсы 38 | //println!("оригинал: {:?}", pair); 39 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 40 | 41 | // Скопируем `moved_pair` в `cloned_pair` (включая ресурсы) 42 | let cloned_pair = moved_pair.clone(); 43 | // Сбросим оригинальную пару используя std::mem::drop 44 | drop(moved_pair); 45 | 46 | // Ошибка! `moved_pair` была сброшена 47 | //println!("копия: {:?}", moved_pair); 48 | // ЗАДАНИЕ ^ Попробуйте раскомментировать эту строку 49 | 50 | // Полученный результат из .clone() все ещё можно использовать! 51 | println!("клон: {:?}", cloned_pair); 52 | } 53 | ``` 54 | 55 | [clone]: https://doc.rust-lang.org/std/clone/trait.Clone.html -------------------------------------------------------------------------------- /src/trait/drop.md: -------------------------------------------------------------------------------- 1 | # Типаж Drop 2 | 3 | Типаж [`Drop`][Drop] имеет только один метод: `drop`, который вызывается автоматически, 4 | когда объект выходит из области видимости. Основное применение типажа `Drop` 5 | заключается в том, чтобы освободить ресурсы, которыми владеет экземпляр реализации. 6 | 7 | `Box`, `Vec`, `String`, `File`, и `Process` - это некоторые примеры типов, которые 8 | реализуют типаж `Drop` для освобождения ресурсов. Типаж `Drop` также может быть 9 | реализован вручную для любых индивидуальных типов данных. 10 | 11 | В следующем примере мы добавим вывод в консоль к функции `drop`, чтобы было видно, 12 | когда она вызывается. 13 | 14 | ```rust,editable 15 | struct Droppable { 16 | name: &'static str, 17 | } 18 | 19 | // Это простая реализация `drop`, которая добавляет вывод в консоль. 20 | impl Drop for Droppable { 21 | fn drop(&mut self) { 22 | println!("> Сбросили {}", self.name); 23 | } 24 | } 25 | 26 | fn main() { 27 | let _a = Droppable { name: "a" }; 28 | 29 | // блок А 30 | { 31 | let _b = Droppable { name: "b" }; 32 | 33 | // блок Б 34 | { 35 | let _c = Droppable { name: "c" }; 36 | let _d = Droppable { name: "d" }; 37 | 38 | println!("Выходим из блока Б"); 39 | } 40 | println!("Вышли из блока Б"); 41 | 42 | println!("Выходим из блока А"); 43 | } 44 | println!("Вышли из блока А"); 45 | 46 | // Переменную можно сбросить вручную с помощью функции `drop`. 47 | drop(_a); 48 | // ЗАДАНИЕ ^ Попробуйте закомментировать эту строку 49 | 50 | println!("Конец главной функции."); 51 | 52 | // *Нельзя* сбросить `_a` снова, потому что переменная уже 53 | // (вручную) сброшена. 54 | } 55 | ``` 56 | 57 | [Drop]: https://doc.rust-lang.org/std/ops/trait.Drop.html -------------------------------------------------------------------------------- /src/trait/ops.md: -------------------------------------------------------------------------------- 1 | # Перегрузка операторов 2 | 3 | В Rust, множество операторов могут быть перегружены с помощью типажей. То есть, некоторые 4 | операторы могут использоваться для выполнения различных задач на основе вводимых аргументов. 5 | Это возможно, потому что операторы являются синтаксическим сахаром для вызова методов. Например, 6 | оператор `+` в `a + b` вызывает метод `add` (как в `a.add(b)`). 7 | Метод `add` является частью типажа `Add`. 8 | Следовательно, оператор `+` могут использовать все, кто реализуют типаж `Add`. 9 | 10 | Список типажей, таких как `Add`, которые перегружают операторы, доступен [здесь](https://doc.rust-lang.org/core/ops/). 11 | 12 | ```rust,editable 13 | use std::ops; 14 | 15 | struct Foo; 16 | struct Bar; 17 | 18 | #[derive(Debug)] 19 | struct FooBar; 20 | 21 | #[derive(Debug)] 22 | struct BarFoo; 23 | 24 | // Типаж `std::ops::Add` используется для указания функциональности `+`. 25 | // Здесь мы объявим `Add` - типаж сложения, со вторым 26 | // операндом типа `Bar`. 27 | // Следующий блок реализует операцию: Foo + Bar = FooBar 28 | impl ops::Add for Foo { 29 | type Output = FooBar; 30 | 31 | fn add(self, _rhs: Bar) -> FooBar { 32 | println!("> Вызвали Foo.add(Bar)"); 33 | 34 | FooBar 35 | } 36 | } 37 | 38 | // Если мы поменяем местами типы, то получим реализацию некоммутативного сложения. 39 | // Здесь мы объявим `Add` - типаж сложения, со вторым 40 | // операндом типа `Foo`. 41 | // Этот блок реализует операцию: Bar + Foo = BarFoo 42 | impl ops::Add for Bar { 43 | type Output = BarFoo; 44 | 45 | fn add(self, _rhs: Foo) -> BarFoo { 46 | println!("> Вызвали Bar.add(Foo)"); 47 | 48 | BarFoo 49 | } 50 | } 51 | 52 | fn main() { 53 | println!("Foo + Bar = {:?}", Foo + Bar); 54 | println!("Bar + Foo = {:?}", Bar + Foo); 55 | } 56 | ``` 57 | 58 | ### Смотрите также: 59 | 60 | [Add](https://doc.rust-lang.org/core/ops/trait.Add.html), [Syntax Index](https://doc.rust-lang.org/book/second-edition/appendix-02-operators.html) 61 | -------------------------------------------------------------------------------- /src/types.md: -------------------------------------------------------------------------------- 1 | # Типы 2 | 3 | Rust предоставляет несколько механизмов изменения или определения примитивных 4 | и пользовательских типов: 5 | 6 | - [Приведение](types/cast.html) между примитивными типами 7 | - Указание желаемого типа при помощи [литералов](types/literals.md) 8 | - Использование [вывода типов](types/inference.html) 9 | - [Псевдонимы](types/alias.html) типов 10 | -------------------------------------------------------------------------------- /src/types/alias.md: -------------------------------------------------------------------------------- 1 | # Псевдонимы 2 | 3 | Оператор type используется, чтобы задать новое имя существующему типу. 4 | Имя типа должно быть в стиле `CamelCase`, иначе компилятор выдаст предупреждение. 5 | Исключением являются примитивные типы: `usize`, `f32` и другие. 6 | 7 | ```rust,editable 8 | // `NanoSecond` это новое имя для `u64`. 9 | type NanoSecond = u64; 10 | type Inch = u64; 11 | 12 | // Используйте этот атрибут, чтобы не выводить предупреждение 13 | // о именах не в стиле CamelCase 14 | #[allow(non_camel_case_types)] 15 | type u64_t = u64; 16 | // ЗАДАНИЕ ^ Попробуйте удалить атрибут 17 | 18 | fn main() { 19 | // `NanoSecond` = `Inch` = `u64_t` = `u64`. 20 | let nanoseconds: NanoSecond = 5 as u64_t; 21 | let inches: Inch = 2 as u64_t; 22 | 23 | // Обратите внимание, что псевдонимы *не предоставляют* никакой 24 | // дополнительной безопасности типов, так как *не являются* новыми типами 25 | println!("{} nanoseconds + {} inches = {} unit?", 26 | nanoseconds, 27 | inches, 28 | nanoseconds + inches); 29 | } 30 | ``` 31 | 32 | Основное применение псевдонимов — сокращение размера кода: например, тип `IoResult` является 33 | псевдонимом типа Result. 34 | 35 | ### Смотрите также: 36 | 37 | [Атрибуты](attribute.html) -------------------------------------------------------------------------------- /src/types/cast.md: -------------------------------------------------------------------------------- 1 | # Приведение типов 2 | 3 | Rust не предусматривает неявного преобразования типов (принудительное) между примитивными типами. 4 | Но, явное преобразование типов (casting) можно выполнить используя ключевое слово `as`. 5 | 6 | Правила, используемые для преобразование внутренних типов, такие же, как в языке C, 7 | за исключением тех случаев, когда преобразование типов в языке C 8 | вызывает неопределённое поведение. 9 | Поведение всех приведений между встроенными типами чётко определено в Rust. 10 | 11 | ```rust,editable,ignore,mdbook-runnable 12 | // Убрать все предупреждения 13 | // которые вызываются переполнением при преобразование типов. 14 | #![allow(overflowing_literals)] 15 | 16 | fn main() { 17 | let decimal = 65.4321_f32; 18 | 19 | // Ошибка ! Нет неявного преобразования 20 | let integer: u8 = decimal; 21 | // ИСПРАВЬТЕ ^ Закомментируйте данную строку 22 | 23 | // Явное преобразование 24 | let integer = decimal as u8; 25 | let character = integer as char; 26 | 27 | println!("Casting: {} -> {} -> {}", decimal, integer, character); 28 | 29 | // Когда преобразовывается любое значение в беззнаковый тип T 30 | // std::T::MAX + 1 добавляется или вычитается до тех пор, пока значение 31 | // не будет помещаться в новый тип. 32 | 33 | // 1000 поместится в u16 34 | println!("1000 as a u16 is: {}", 1000 as u16); 35 | 36 | // 1000 - 256 - 256 - 256 = 232 37 | // Подробнее. Первые 8 младших битов (LSB) сохраняются, 38 | // а старшие биты (MSB) будут усечены. 39 | println!("1000 as a u8 is : {}", 1000 as u8); 40 | // -1 + 256 = 255 41 | println!(" -1 as a u8 is : {}", (-1i8) as u8); 42 | 43 | // Для положительных чисел результатом будет остаток от деления 44 | println!("1000 mod 256 is : {}", 1000 % 256); 45 | 46 | // Когда значение преобразовывается в знаковый тип, 47 | // побитовый результат будет таким же, как и 48 | // первое преобразование к соответствующему типу без знака. Если старший бит этого значения 49 | // равен 1, то это значение отрицательное. 50 | 51 | // За исключением случая, когда значение умещается в тип. 52 | println!(" 128 as a i16 is: {}", 128 as i16); 53 | // 128 as u8 -> 128, дополнительный код которого в 8 битах: 54 | println!(" 128 as a i8 is : {}", 128 as i8); 55 | 56 | // повторяем примеры 57 | // 1000 as u8 -> 232 58 | println!("1000 as a i8 is : {}", 1000 as i8); 59 | // и дополнительный код 232 - это -24 60 | println!(" 232 as a i8 is : {}", 232 as i8); 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /src/types/inference.md: -------------------------------------------------------------------------------- 1 | # Вывод типов 2 | 3 | Движок вывода типов весьма умён. Он делает куда больше, 4 | чем просто смотрит на тип [r-value][rvalue] при инициализации. 5 | Он также смотрит, как используется значение после инициализации, чтобы 6 | вывести его тип. Вот расширенный пример вывода типов: 7 | 8 | ```rust,editable 9 | fn main() { 10 | // Благодаря выведению типов компилятор знает, `elem` имеет тип - u8. 11 | let elem = 5u8; 12 | 13 | // Создадим пустой вектор (расширяемый массив). 14 | let mut vec = Vec::new(); 15 | // В данном месте компилятор не знает точный тип `vec`, он лишь знает, 16 | // что это вектор чего-то там (`Vec<_>`). 17 | 18 | // Добавляем `elem` в вектор. 19 | vec.push(elem); 20 | // Ага! Теперь компилятор знает, что `vec` - это вектор, который хранит в себе тип `u8` 21 | // (`Vec`) 22 | // ЗАДАНИЕ ^ Попробуйте закомментировать строку `vec.push(elem)` 23 | 24 | println!("{:?}", vec); 25 | } 26 | ``` 27 | 28 | Не потребовалось никакой аннотации типов переменных, компилятор счастлив, как и программист! 29 | 30 | [rvalue]: https://en.wikipedia.org/wiki/Value_%28computer_science%29#lrvalue 31 | -------------------------------------------------------------------------------- /src/types/literals.md: -------------------------------------------------------------------------------- 1 | # Литералы 2 | 3 | Числовые литералы могут быть обозначены добавлением типа в качестве суффикса. Например, 4 | чтобы указать, что литерал `42` должен иметь тип `i32`, необходимо написать `42i32`. 5 | 6 | Тип литералов без суффикса будет зависеть от того, как они используются. Если нет никаких 7 | ограничений, то компилятор будет использовать `i32` для целочисленных литералов, а `f64` для 8 | литералов с плавающей точкой. 9 | 10 | ```rust,editable 11 | fn main() { 12 | // Литералы с суффиксами. Их тип известен при инициализации. 13 | let x = 1u8; 14 | let y = 2u32; 15 | let z = 3f32; 16 | 17 | // Литералы без суффиксов. Их тип будет зависеть от того, как их используют. 18 | let i = 1; 19 | let f = 1.0; 20 | 21 | // `size_of_val` возвращает размер переменной в байтах 22 | println!("size of `x` in bytes: {}", std::mem::size_of_val(&x)); 23 | println!("size of `y` in bytes: {}", std::mem::size_of_val(&y)); 24 | println!("size of `z` in bytes: {}", std::mem::size_of_val(&z)); 25 | println!("size of `i` in bytes: {}", std::mem::size_of_val(&i)); 26 | println!("size of `f` in bytes: {}", std::mem::size_of_val(&f)); 27 | } 28 | ``` 29 | 30 | В предыдущем коде используются некоторые вещи, которые не были объяснены ранее. 31 | Вот краткое объяснение для нетерпеливых читателей: 32 | 33 | * `fun(&foo)` используется для передаче аргумента в функцию *по ссылке*, вместо 34 | передачи по значению (`fun(foo)`). Подробнее см. [заимствование][borrow] или соответствующую 35 | [главу в книге](http://rurust.github.io/rust_book_ru/src/references-and-borrowing.html). 36 | * `std::mem::size_of_val` является функцией, но вызывается с указанием *полного пути*. 37 | Код можно разделить на логические единицы, называемые *модулями*. В данном случае, 38 | функция определена в модуле `mem`, а модуль `mem` определён в *контейнере* `std`. 39 | Подробнее см. [модули][mod] и [контейнеры][crate], 40 | а так же соответствующую 41 | [главу в книге](http://rurust.github.io/rust_book_ru/src/crates-and-modules.html) 42 | 43 | [borrow]: scope/borrow.html 44 | [mod]: mod.html 45 | [crate]: crates.html 46 | -------------------------------------------------------------------------------- /src/unsafe.md: -------------------------------------------------------------------------------- 1 | # Небезопасные операции 2 | 3 | В качестве введения в этот раздел процитируем официальную документацию, 4 | "нужно стараться минимизировать количество небезопасного кода в кодовой базе." Имея это в виду, давайте начнём! Небезопасные аннотации в Rust используются для обхода блокировок 5 | защиты, устанавливаемых компилятором; в частности, существует четыре основных варианта использования небезопасного кода: 6 | 7 | - разыменование сырых указателей 8 | - вызов функций или методов, которые являются `unsafe` (включая вызов функции через FFI см. [предыдущую главу](std_misc/ffi.md) книги) 9 | - доступ или изменение статических изменяемых переменных 10 | - реализация небезопасных типажей 11 | 12 | ### Сырые указатели 13 | 14 | Сырые указатели `*` и ссылки `&T` имеют схожую функциональность, но ссылки 15 | всегда безопасны, потому что они гарантированно указывают на достоверные данные за счёт механизма проверки заимствований. Разыменование же сырого указателя можно выполнить только через небезопасный блок. 16 | 17 | ```rust,editable 18 | fn main() { 19 | let raw_p: *const u32 = &10; 20 | 21 | unsafe { 22 | assert!(*raw_p == 10); 23 | } 24 | } 25 | ``` 26 | 27 | ### Вызов небезопасных функций 28 | 29 | Некоторые функции могут быть объявлены как `unsafe`, то есть за корректность этого кода несёт ответственность программист, написавший его, вместо компилятора. Пример - 30 | это метод [`std::slice::from_raw_parts`](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html), который создаст срез из указателя на первый элемент и длины. 31 | 32 | ```rust,editable 33 | use std::slice; 34 | 35 | fn main() { 36 | let some_vector = vec![1, 2, 3, 4]; 37 | 38 | let pointer = some_vector.as_ptr(); 39 | let length = some_vector.len(); 40 | 41 | unsafe { 42 | let my_slice: &[u32] = slice::from_raw_parts(pointer, length); 43 | 44 | assert_eq!(some_vector.as_slice(), my_slice); 45 | } 46 | } 47 | ``` 48 | 49 | Для `slice::from_raw_parts` одно из предположений, которое *должно* быть поддержано, 50 | что переданный указатель указывает на допустимую память и что в памяти лежит значение правильного типа. Если эти инварианты не поддерживаются, то поведение программы не определено, и неизвестно, что произойдёт. 51 | -------------------------------------------------------------------------------- /src/variable_bindings.md: -------------------------------------------------------------------------------- 1 | # Связывание переменных 2 | 3 | Rust предоставляет безопасность типов с помощью статической типизации. 4 | Тип переменной может быть указан при объявление связи с переменной. 5 | Тем не менее, в большинстве случаев, компилятор сможет определить тип переменной из контекста. 6 | 7 | Значения (как и литералы) могут быть привязаны к переменным, используя оператор `let`. 8 | 9 | ```rust,editable 10 | fn main() { 11 | let an_integer = 1u32; 12 | let a_boolean = true; 13 | let unit = (); 14 | 15 | // скопировать значение `an_integer` в `copied_integer` 16 | let copied_integer = an_integer; 17 | 18 | println!("Целое: {:?}", copied_integer); 19 | println!("Логическое: {:?}", a_boolean); 20 | println!("Встречайте единичное значение: {:?}", unit); 21 | 22 | // Компилятор предупреждает о неиспользуемых переменных; эти предупреждения можно 23 | // отключить используя подчёркивание перед именем переменной 24 | let _unused_variable = 3u32; 25 | let noisy_unused_variable = 2u32; 26 | // ИСПРАВЬТЕ ^ Добавьте подчёркивание 27 | } 28 | ``` -------------------------------------------------------------------------------- /src/variable_bindings/declare.md: -------------------------------------------------------------------------------- 1 | # Предварительное объявление 2 | 3 | Можно сначала объявить связь с переменной, а инициализировать её позже. 4 | Однако, такая форма используется редко, 5 | так как может привести к использованию неинициализированных переменных. 6 | 7 | ```rust,editable,ignore,mdbook-runnable 8 | fn main() { 9 | // Объявляем связь с переменной 10 | let a_binding; 11 | 12 | { 13 | let x = 2; 14 | 15 | // Инициализируем связь 16 | a_binding = x * x; 17 | } 18 | 19 | println!("связь а: {}", a_binding); 20 | 21 | let another_binding; 22 | 23 | // Ошибка! Использование неинициализированной связи с переменной 24 | println!("другая связь: {}", another_binding); 25 | // ИСПРАВЬТЕ ^ Закомментируйте строку 26 | 27 | another_binding = 1; 28 | 29 | println!("другая связь: {}", another_binding); 30 | } 31 | ``` 32 | 33 | Компилятор запрещает использование неинициализированных переменных, 34 | так как это привело бы к неопределённому поведению. 35 | -------------------------------------------------------------------------------- /src/variable_bindings/mut.md: -------------------------------------------------------------------------------- 1 | # Изменяемость 2 | 3 | По умолчанию связывание переменных является неизменяемым, 4 | но с помощью модификатора `mut` можно разрешить изменения. 5 | 6 | ```rust,editable,ignore,mdbook-runnable 7 | fn main() { 8 | let _immutable_binding = 1; 9 | let mut mutable_binding = 1; 10 | 11 | println!("Перед изменением: {}", mutable_binding); 12 | 13 | // Ok 14 | mutable_binding += 1; 15 | 16 | println!("После изменения: {}", mutable_binding); 17 | 18 | // Ошибка! 19 | _immutable_binding += 1; 20 | // ИСПРАВЬТЕ ^ Закомментируйте эту строку 21 | } 22 | ``` 23 | 24 | Компилятор будет выводить подробные сообщения об ошибках, связанных с изменяемостью. 25 | -------------------------------------------------------------------------------- /src/variable_bindings/scope.md: -------------------------------------------------------------------------------- 1 | # Область видимости и затенение 2 | 3 | Связывание переменных имеет локальную область видимости, и живут эти переменные в *блоке*. 4 | Блок — набор инструкций, заключённый между фигурными скобками `{}`. 5 | Кроме того, допускается [затенение переменных](https://en.wikipedia.org/wiki/Variable_shadowing). 6 | 7 | ```rust,editable,ignore,mdbook-runnable 8 | fn main() { 9 | // Эта переменная живёт в функции main 10 | let long_lived_binding = 1; 11 | 12 | // Это блок, он имеет меньшую область видимости, чем функция main 13 | { 14 | // Эта переменная существует только в этом блоке 15 | let short_lived_binding = 2; 16 | 17 | println!("inner short: {}", short_lived_binding); 18 | 19 | // Эта переменная *затеняет* собой внешнюю 20 | let long_lived_binding = 5_f32; 21 | 22 | println!("inner long: {}", long_lived_binding); 23 | } 24 | // Конец блока 25 | 26 | // Ошибка! `short_lived_binding` нет в этой области видимости 27 | println!("outer short: {}", short_lived_binding); 28 | // ИСПРАВЬТЕ ^ Закомментируйте строку 29 | 30 | println!("outer long: {}", long_lived_binding); 31 | 32 | // Это связывание так же *скрывает* собой предыдущие 33 | let long_lived_binding = 'a'; 34 | 35 | println!("outer long: {}", long_lived_binding); 36 | } 37 | ``` 38 | --------------------------------------------------------------------------------