«Делайте все, что в ваших силах, пока не узнаете лучше. Тогда, когда ты знаешь лучше, делай лучше ».
__ Майя Анжелу
RUST: НАЧАТЬ!
В предыдущих постах мы больше обсуждали теоретические части языка программирования Rust.
ОСНОВЫ RUST
« Не стоит знать язык, который не влияет на ваше представление о программировании . medium.com»
▸ Установка и Hello World ▸ Груз и ящики ▸ Привязки переменных, константы и статика ▸ Комментарии ▸ Функции ▸ Примитивные типы данных ▸ Операторы
▸ Потоки управления
▸ Векторы ▸ Структуры ▸ Перечисления ▸ Обобщения ▸ Имплсы и черты
▸ Право собственности ▸ Заимствования ▸ Срок службы и пожизненное исключение
Но сегодня мы собираемся подробнее обсудить практические аспекты написания программ на Rust.
Также теперь вы можете читать тот же контент через learning-rust.github.io 👈. Я думаю, что он более структурирован и позволяет увидеть общую картину.
Организация кода
Когда единичный блок кода становится больше, он должен быть разбит на более мелкие части и должен быть организован надлежащим образом. Rust поддерживает разные уровни организации кода.
- Функции
- Модули
могут быть сопоставлены с
▸ встроенным модулем
▸ файлом
▸ иерархией каталогов - Ящики
могут быть сопоставлены с файлом
▸ lib.rs в том же исполняемом контейнере
▸ Ящик зависимостей, указанный в Cargo.toml
- Зависимость может быть указана из
▸ пути
▸ репозитория git
▸ crates.io - Рабочие области
Помогает управлять несколькими ящиками как одним проектом.
Давайте обсудим по порядку.
💡 Чтобы упростить примеры, мы используем простую функцию, которая печатает
“Hello, world!”
. Но что касается написания тестируемых кодов, всегда старайтесь вернутьString
из функции и распечатать его при вызове, вместо этого печатая строку внутри функции.
01. Функции
Функции - это первая линия организации в любой программе.
Мы можем добавить модульные тесты в тот же файл.
💭 Атрибут - это общий метаданный произвольной формы, который интерпретируется в соответствии с именем, соглашением, языком и версией компилятора.
02. Модули
▸ Встроенные модули / модули в одном файле
Связанный код и данные группируются в модуль и хранятся в одном файле.
Модули также могут быть вложенными.
Частные функции могут вызываться из того же модуля или из дочернего модуля.
💡 Ключевое слово
self
используется для ссылки на тот же модуль, а ключевое словоsuper
используется для ссылки на родительский модуль. Также ключевое словоsuper
можно использовать для доступа к корневым функциям изнутри модуля.
🔎 При написании тестов рекомендуется писать тесты внутри тестового модуля, поскольку они компилируются только при запуске тестов.
▸ Модули - в другом файле в том же каталоге
Если мы обернем содержимое файла объявлением mod
, он будет действовать как вложенный модуль.
▸ Модули - В другом файле в другом каталоге
mod.rs
в корне модуля каталога находится точка входа в модуль каталога. Все остальные файлы в этом корне каталога действуют как подмодули модуля каталога.
Опять же, если мы обернем содержимое файла объявлением mod
, он будет действовать как вложенный модуль.
Другие файлы в модуле каталога действуют как подмодули для mod.rs
⭐️ Если вам нужно получить доступ к элементу phrases/greetings.rs
извне модуля, вы должны импортировать модуль greetings
как общедоступный модуль.
🔎 Невозможно импортировать дочерние файловые модули модулей каталогов в
main.rs
, поэтому вы не можете использоватьmod phrases::greetings;
изmain.rs
. Но есть способ импортировать модульphrases::greetings::hello()
вphrases
путем повторного экспортаhello
в модульphrases
. Таким образом, вы можете называть его напрямую какphrases::hello()
.
Это позволяет вам представить внешний интерфейс, который не может напрямую соответствовать вашей внутренней кодовой организации. Если это все еще непонятно, не волнуйтесь; Мы обсудим использование use
в следующем разделе этого сообщения.
03. Ящики
💭 Ящики немного похожи на пакеты на некоторых других языках. Ящики составляются индивидуально. Если в ящике есть дочерние файловые модули, эти файлы будут объединены с файлом ящика и скомпилированы как единое целое.
💭 Ящик может создать исполняемый файл / двоичный файл или библиотеку.src/main.rs
- это корень ящика / точка входа для двоичного ящика, аsrc/lib.rs
- это точка входа для ящика библиотеки.
▸ Файл lib.rs в том же исполняемом ящике
💡 При написании бинарных ящиков мы можем переместить основные функции в src/lib.rs
и использовать его как библиотеку из src/main.rs
. Этот шаблон довольно часто встречается в ящиках с исполняемыми файлами.
💯 Как я упоминал ранее, здесь мы используем простейшие примеры, чтобы упростить учебные материалы. Но именно так нам нужно написать
greetings/src/lib.rs
, чтобы сделать код более тестируемым.
📖 При импорте ящика, в названии которого есть тире like-this, который не является допустимым идентификатором Rust, он будет преобразован путем замены тире на подчеркивания, поэтому вы должны написать
extern crate like_this;
lib.rs
может связываться с несколькими файлами.
▸ Ящик зависимостей, указанный в Cargo.toml
Когда код в lib.rs
файле становится больше, мы можем переместить их в отдельный ящик библиотеки и использовать его как зависимость от основного ящика. Как мы упоминали ранее, зависимость может быть указана из пути к папке, репозитория git или crates.io.
» Давайте посмотрим, как создать вложенный ящик и использовать его в качестве зависимости, используя путь к папке,
» Если вы хотите использовать ящик библиотеки в нескольких проектах, один из способов - переместить код контейнера в репозиторий git и использовать его в качестве зависимости при необходимости.
» Другой способ - загрузить его в crates.io и использовать в качестве зависимости при необходимости.
🚧 Во-первых, давайте создадим простой ящик Hello world и загрузим его на crates.io.
💭 //! комментарии к документации используются для написания документации на уровне ящиков и модулей. В других местах мы должны использовать /// вне блока. А при загрузке ящика на crates.io cargo генерирует документацию из этих комментариев к документам и размещает ее на docs.rs.
💡 Мы должны добавить поля описание и лицензия в Cargo.toml, иначе мы получим error: api errors: missing or empty metadata fields: description, license. Please see http://doc.crates.io/manifest.html
Чтобы загрузить это в crates.io,
01. Мы должны создать учетную запись на crates.io, чтобы получить токен API
02. Затем запустите cargo login <token>
с этим токеном API и cargo publish
📖 Это более подробно описано в Cargo Docs.
- Для приобретения токена API вам понадобится аккаунт на crates.io. Для этого посетите домашнюю страницу и войдите в систему через учетную запись GitHub (на данный момент требуется). После этого перейдите на страницу Настройки учетной записи и выполните указанную команду
cargo login
.
Пр.cargo login abcdefghijklmnopqrstuvwxyz012345
- Следующий шаг - упаковать ваш ящик в формат, который можно загрузить на crates.io. Для этого мы будем использовать подкоманду
cargo package
.
- Теперь ее можно загрузить в crates.io с помощью командыcargo publish
.
- Если вы хотите пропустить шагcargo package
,cargo publish
подкоманда автоматически упакует локальный ящик, если копия еще не найдена.
Наш ящик называется test_crate_hello_world
. Так что его можно найти на
📦 https: // crates.io/crates/ test_crate_hello_world
📑 https: // docs.rs/ test_crate_hello_world
💯 crates.io также поддерживает файлы readme. Чтобы включить его, мы должны добавить поле readme в Cargo.toml. Пример:
readme="README.md"
🏗️ Хорошо, давайте посмотрим, как мы можем использовать это из другого ящика.
По умолчанию Cargo проверяет зависимости на crates.io. Поэтому нам нужно добавить только имя контейнера и строку версии в Cargo.toml, а затем запустить cargo build
, чтобы получить зависимости и скомпилировать их.
04. Рабочие места
Когда база кода становится больше, вам, возможно, придется работать над несколькими ящиками в одном проекте. Rust поддерживает это через Workspaces. Вы можете создавать, запускать тесты или создавать документы для всех ящиков одновременно, выполняя cargo
команды из корня проекта.
⭐️ При одновременной работе с несколькими ящиками повышается вероятность наличия общих зависимостей от ящиков. Чтобы предотвратить загрузку и компиляцию одной и той же зависимости несколько раз, Rust использует общий каталог сборки в корне проекта, одновременно выполняя cargo build
из корня проекта.
🔎 папка с исходным кодом rust-lang / rust - хороший пример рабочей области.
использовать
▸ В основном ключевое слово use
используется для привязки полного пути элемента к новому имени. Таким образом, пользователь не хочет каждый раз повторять полный путь.
▸ Еще одно распространенное использование use
- импорт элементов в область действия. Помните, что это также немного похоже на создание псевдонима и его использование вместо использования полного пути.
💡 По умолчанию в объявлениях
use
используются абсолютные пути, начиная с корня ящика. Но в объявленияхself
иsuper
этот путь указывается относительно текущего модуля.
Таким же образом ключевое слово use
используется для импорта элементов из других ящиков, включая std
, Стандартная библиотека Rust.
Нам не нужно использовать
extern crate std;
при использованииstd
библиотеки. Подробнее об этом мы поговорим в разделе «Стандартная библиотека».
💡 use
операторы импортируют только то, что мы указали в области видимости, вместо импорта всех элементов модуля или ящика. Так повышается эффективность программы.
▸ Другой особый случай - pub use
. При создании модуля вы можете экспортировать вещи из другого модуля в свой модуль. Так что после этого к ним можно будет получить доступ прямо из вашего модуля. Это называется реэкспорт.
Этот шаблон довольно распространен в больших библиотеках. Это помогает скрыть сложность внутренней модульной структуры библиотеки от пользователей, потому что пользователям не нужно знать / следить за всей картой каталогов элементов библиотеки во время работы с ними.
Стандартная библиотека, примитивы и прелюдии Rust
⭐️ В Rust языковые элементы реализуются не только с помощью крейта std
библиотеки, но также и с помощью компилятора. Примеры,
- Примитивы: определяются компилятором, а методы реализуются
std
библиотекой непосредственно на примитивах. - Стандартные макросы: определяется как компилятором, так и
std
.
Библиотека std
разделена на модули в соответствии с основными областями, которые охватываются каждым из них.
⭐️ В то время как примитивы реализуются компилятором, стандартная библиотека реализует наиболее полезные методы непосредственно на примитивных типах. Но некоторые редко используемые языковые элементы некоторых примитивов хранятся в соответствующих
std
модулях. Вот почему вы можете видетьchar
,str
и целочисленные типы как в примитивах, так и вstd
модулях.
🔎 Изучив Исходный код Rust, вы увидите, что
src
каталог является рабочей областью. Несмотря на то, что у него много библиотечных ящиков, изучив корневой файл Cargo.toml, вы можете легко определить, что основными ящиками являютсяrustc
(компилятор) иlibstd
(std
). Вlibstd/lib.rs
модули std были реэкспортированы черезpub use
, а исходное расположение большинства модулей std -src/libcore
.
Немного важных std
модулей,
»std::io
- Основные функции ввода / вывода
» std::fs
- Функции, специфичные для файловой системы
»std::path
- Межплатформенный путь специальные функции
»std::env
- Функции, связанные со средой процесса
» std::mem
- Функции, связанные с памятью
»std::net
- Связь TCP / UDP
» std::os
- Специальные функции ОС
»std::thread
- Специальные возможности потоков < br /> »std::collections
- Основные Типы коллекций
💯 Дополнительные сведения см. в Документации стандартной библиотеки Rust.
⭐️ Несмотря на то, что Rust std
содержит много модулей, по умолчанию он не загружает каждую часть библиотеки std в каждой программе Rust. Вместо этого он загружает только наименьший список вещей, необходимых почти для каждой программы на Rust. Это так называемые прелюдии. Они только импортируют,
Прелюдии были явно импортированы на
libstd/lib.rs
, и весь список можно увидеть наlibstd/prelude/v1.rs
.
⭐️ Технически Rust вставляет
▸ extern crate std;
: в корень каждого ящика
▸ use std::prelude::v1::*;
: в каждый модуль
Таким образом, вам не нужно импортировать их каждый раз.
Концепция прелюдий довольно распространена в библиотеках Rust. Даже некоторые модули внутри std
ящика (например, 85_) и многие библиотеки (например, дизель) имеют свои собственные 86_ модули.
⭐️ Но прелюдии используются для создания единого места для импорта всех важных компонентов, которые необходимы при использовании библиотеки. Они не загружаются автоматически, если вы не импортировали их вручную. Только std::prelude
автоматически импортируется во все программы на Rust.
🚧 Пример того, как мы можем организовать код при использовании структур и трейтов внутри модулей, будет добавлен через несколько дней.
Хорошо, давайте остановим здесь четвертый пост из серии Learning Rust (и Gitbook). В этом посте я просто попытался резюмировать,
▸ Модули ▸ Ящики ▸ Рабочие области ▸ Стандартные модули и прелюдии
🐣 Я 🇱🇰 веб-разработчик, живущий в 🇻🇳. Так что я не являюсь носителем английского языка и просто изучаю Rust. Если вы обнаружили какую-либо ошибку или что-то нужно изменить, даже орфографическую или грамматическую ошибку, пожалуйста, дайте мне знать. Спасибо.
Хорошо, увидимся в следующем посте :)
«Никогда не поздно стать тем, кем ты мог бы быть».
__ Джордж Элиот
ДАЛЕЕ ▸ Умный компилятор ▸ Паника ▸ Вариант и результат ▸ Распаковать и ожидать ▸ Ошибка и отсутствие распространения ▸ Комбинаторы ▸ Пользовательские типы ошибок