«Делайте все, что в ваших силах, пока не узнаете лучше. Тогда, когда ты знаешь лучше, делай лучше ».
__ Майя Анжелу

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 RustGitbook). В этом посте я просто попытался резюмировать,

▸ Модули ▸ Ящики ▸ Рабочие области ▸ Стандартные модули и прелюдии



🐣 Я 🇱🇰 веб-разработчик, живущий в 🇻🇳. Так что я не являюсь носителем английского языка и просто изучаю Rust. Если вы обнаружили какую-либо ошибку или что-то нужно изменить, даже орфографическую или грамматическую ошибку, пожалуйста, дайте мне знать. Спасибо.

Хорошо, увидимся в следующем посте :)

«Никогда не поздно стать тем, кем ты мог бы быть».
__ Джордж Элиот

ДАЛЕЕ ▸ Умный компилятор ▸ Паника ▸ Вариант и результат ▸ Распаковать и ожидать ▸ Ошибка и отсутствие распространения ▸ Комбинаторы ▸ Пользовательские типы ошибок