Анатомия проекта
Базовая компоновка проекта, а также общие файлы и папки, используемые cargo.
Директория/Файл | Комментарий |
---|---|
📁 .cargo/ | Проектно-локальная конфигурация cargo, может содержать config.toml. |
📁 benches/ | Бенчмарки для вашего крейта, проходящие через cargo bench, требуют по умолчанию ночной версии rust. |
📁 examples/ | Примеры того, как использовать ваш крейт. |
- my_example.rs | Отдельные примеры запускаются как cargo run —example my_example. |
📁 src/ | Фактический исходный код для вашего проекта. |
- main.rs | Точка входа по умолчанию для приложений, это то, что использует cargo run. |
- lib.rs | Точка входа по умолчанию для библиотек. Здесь начинается поиск my_crate::f(). |
📁 src/bin/ | Место для дополнительных двоичных файлов, даже в библиотечных проектах. |
- extra.rs | Дополнительный двоичный файл, запускается cargo run —bin extra. |
📁 tests/ | Интеграционные тесты находятся эдесь, запускаясь через cargo test. Модульные тесты часто остаются в каталоге src/. |
.rustfmt.toml | В случае, если вы хотите настроить, как работает cargo fmt. |
.clippy.toml | Специальная конфигурация для определенных clippy lints, используемых через cargo clippy. |
build.rs | Предварительный скрипт, полезный при компиляции C/FFI, … |
Cargo.toml | Основной манифест проекта. Определяет зависимости, артефакты. |
Cargo.lock | Сведения о зависимостях для сборки, содержащиеся в git для приложений, а не для libs. |
rust-toolchain.toml | Определение инструментов (канал, компоненты, путей) для этого проекта. |
Минимальные примеры для различных точек входа могут выглядеть следующим образом:
Приложение
// src/main.rs (Точка входа приложения по умолчанию)
fn main() {
println!("Ржавый код!");
}
Библиотеки
// src/lib.rs (Точка входа в библиотеку по умолчанию)
pub fn f() {} // Является общедоступным элементом в корне, поэтому он доступен извне.
mod m {
pub fn g() {} // Нет общедоступного пути ('m' не общедоступен) от root, поэтому 'g'.
} // Крейт недоступен с внешней стороны ящика.
Модульные тесты
// src/my_module.rs (любой файл вашего проекта)
fn f() -> u32 { 0 }
#[cfg(test)]
mod test {
use super::f; // Необходимо импортировать элементы из родительского модуля.
// Имеет доступ к непубличным членам.
#[test]
fn ff() {
assert_eq!(f(), 0);
}
}
Интеграционные тесты
// tests/sample.rs (пример интеграционного теста)
#[test]
fn my_sample() {
assert_eq!(my_crate::f(), 123); // Интеграционные тесты (и бенчмарки) «зависят» от crate, как 3-я сторона.
} // Следовательно, они видят только общедоступные элементы.
Тесты производительности
// benches/sample.rs (пример теста производительности)
#![feature(test)]
extern crate test; // Обычно это не нужно в новых версиях rust
use test::{black_box, Bencher};
#[bench]
fn my_algo(b: &mut Bencher) {
b.iter(|| black_box(my_crate::f())); // 'black_box' предотвращает оптимизацию 'f'.
}
Построение скриптов
// build.rs (Пример сценария предварительной сборки)
fn main() {
// Вам нужно полагаться на env.vars, '#[cfg(...)] ' предназначен для хоста.
let target_os = env::var("CARGO_CFG_TARGET_OS");
}
Макросы
// src/lib.rs (Точка входа по умолчанию для макросов)
extern crate proc_macro; // Видимо нужно импортировать именно так.
use proc_macro::TokenStream;
#[proc_macro_attribute] // Крейты теперь могут использовать '#[my_attribute]'
pub fn my_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
// Cargo.toml
[package]
name = "my_crate"
version = "0.1.0"
[lib]
proc-macro = true
Деревья модулей и импорт:
Деревья модулей
Исходные файлы модулей работают следующим образом:
- Дерево модулей должно быть явно определено, не является неявно построенным из дерева файловой системы.
- Корень дерева модуля равен библиотеке, приложению, … точка входа (например, lib.rs). Фактически определения модулей работают следующим образом:
mod m {}
определяет модуль в файле, в то время какmod m;
прочитает m.rs или m/mod.rs.- Путь к .rs на основе вложенности, например,
mod a { mod b { mod c; }}}
либо a/b/c.rs, либо a/b/c/mod.rs. - Файлы не идут из корня дерева модулей через какой-нибудь
mod m;
, не будет затронут компилятором!
Пространства имен
Rust имеет три типа пространств имен:
Типы пространств имен | Функции пространства имен | Макросы пространства имен |
---|---|---|
mod X {} | fn X() {} | macro_rules! X { … } |
X (crate) | const X: u8 = 1; | |
trait X {} | static X: u8 = 1; | |
enum X {} | ||
union X {} | ||
struct X {} |
struct X;
- Вычисляется в типах и в функциях, определяет тип X
как константу.
struct X();
- Вычисляется в типах и в функциях, определяет тип X
как функцию.
- В любой заданной области, например в модуле, может существовать только один элемент на пространство имен, например:
—
enum X {}
иfn X() {}
могут сосуществовать. —struct X;
иconst X
не может сосуществовать. - С использованием
my_mod::X;
все элементы, называемые ‘X’, будут импортированы. Из-за соглашений об именах (например, fn и mod написаны строчными буквами) и здравого смысла (большинство разработчиков просто не называют все вещи X) вам не придется беспокоиться об этих видах в большинстве случаев. Однако они могут быть фактором при проектировании макросов.
Cargo
Команды и инструменты, которые полезно знать.
Команда | Комментарий |
---|---|
cargo init | Создаёт новый проект. |
cargo build | Компилирует проект в режиме отладки (—release для оптимизации). |
cargo check | Проверит, будет ли проект компилироваться (намного быстрее компиляции). |
cargo test | Выполнит тесты проекта. |
cargo doc --open | Локальное создание документации по коду и зависимостям. |
cargo run | Запустит проект, если создан двоичный файл (main.rs). |
cargo run --bin b | Запустит двоичный файл b. Унифицирует объекты с другими зависимостями (может сбить с толку). |
cargo run -p w | Запустит main в рабочей области w. Относится больше к функциям. |
cargo … --timings | Покажет какие крейты заставили вашу сборку собираться так много времени. |
cargo tree | Показать график зависимостей. |
cargo +{nightly, stable} … | Используйте данную цепочку инструментов для команд, например, для инструментов «ночной сборки rust». |
cargo +nightly … | Некоторые команды ночнjq сборки (заменит … командой дальше). |
rustc -- -Zunpretty=expanded | Показывает расширяемые макросы. |
rustup doc | Откроывает оффлайн Rust документацию (в т.ч. книги). |
cargo build означает, что вы можете написать cargo build, либо просто cargo b, —release можно заменить на -r.
Дополнительные компоненты rust устанавливаются утилитой rustup, добавляет необходимый инструмент.
Инструмент | Комментарий |
---|---|
cargo clippy | Дополнительный линтер перехватывающий распространенные злоупотребления API и унидиоматический код. |
cargo fmt | Автоматический форматировщик кода (компонент rustup добавляет rustfmt). |
Большое количество дополнительных cargo плагинов можно найти здесь.
Кросс-компиляция
- Проверка компилируемого объекта поддерживается разными платформами.
- Установка через
rustup target install X
. - Установите собственные нативные инструменты (зависит от ОС). Получить от поставщика (Google, Apple, …), может быть доступно не на всех платформах (например, нет инструментов iOS в Windows). Некоторые инструменты требуют дополнительных шагов сборки (например, make-standalone-toolchain.sh Android).
- Обновите ~/.cargo/config.toml следующим образом:
[target.aarch64-linux-android]
linker = "[PATH_TO_TOOLCHAIN]/aarch64-linux-android/bin/aarch64-linux-android-clang"
или
[target.aarch64-linux-android]
linker = "C:/[PATH_TO_TOOLCHAIN]/prebuilt/windows-x86_64/bin/aarch64-linux-android21-clang.cmd"
- Установите переменные среды (необязательно, подождите, пока компилятор пожалуется перед настройкой):
set CC=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd
set CXX=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd
set AR=C:\[PATH_TO_TOOLCHAIN]\prebuilt\windows-x86_64\bin\aarch64-linux-android-ar.exe
…
Установите ли вы их, зависит от того, как компилятор жалуется, не обязательно все необходимы.
Некоторые платформы могут быть чрезвычайно чувствительными к тому, как указаны пути (например, \
против /
).
Компиляция приложения: cargo build —target=X
Директивы инструментов
Специальные токены, встроенные в исходный код, используются инструментарием или препроцессорной обработки.
Макросы
Внутри декларативного макроса на примере macro_rules! осуществляется:
Внутри макросов | Комментарий |
---|---|
$x:ty | Захват макросов (типы ниже). |
- $x:item | Элемент, например, функция, структура, модуль и т.д. |
- $x:block | Блок {} операторов или выражений, например { let x = 5; } |
- $x:stmt | Оператор, например let x = 1 + 1; , String::new() ; или vec![] ; |
- $x:expr | Выражение, например x , 1 + 1 , String::new() или vec![] |
- $x:pat | Шаблон, например Some(t) , (17, 'a') или _ . |
- $x:ty | Тип, например String , usize или Vec<u8> . |
- $x:ident | Идентификатор, например в let x = 0 ; идентификатор ‘x’. |
- $x:path | Путь (например, foo , ::std::mem::replace , transmute::<_, int> ). |
- $x:literal | Литерал, например 3 , "foo" , b"bar" и т.д. |
- $x:lifetime | Время жизни, например 'a , 'static и т.д.). |
- $x:meta | Мета-элемент, вещи которые входят внутрь #[...] и #! [...] атрибутов. |
- $x:vis | Модификатор видимости pub, pub(crate) и т.д. |
- $x:tt | Одно дерево токенов, см. здесь для получения более подробной информации. |
$crate | Специальная гигиеническая переменная, crate где определены макросы. |
Документирование
Внутри документа комментируют:
Комментарии в коде | Комментарии |
---|---|
```…``` | Выполните doc test (doc-код, выполняемый в cargo test). |
```X,Y …``` | То же самое, но включает в себя дополнительные конфигурации, ‘X, Y’ являются … |
- rust | Сделать явным тест, что написано на Rust, подразумевается инструментом Rust. |
- - | Скомпилировать для теста. Запустите для теста. Потерпите неудачу, если будет паника. Поведение по умолчанию. |
- should_panic | Скомпилировать для теста. Запустите для теста. Исполнение должно паниковать. Если нет, то завершение будет неуспешным. |
- no_run | Скомпилировать для теста. Будет ошибка, если код не может быть скомпилирован, код не выполнится. |
- compile_fail | Скомпилировать для теста, но это завершится неудачей, даже если сам код будет скомпилирован. |
- ignore | Не компилировать. Не запускайть. Вместо этого отдайте предпочтение варианту выше. |
- edition2018 | Выполнение кода как в Rust 18, если значение по умолчанию — 15. |
# | Скрыть строку из документации (''' # use x::hidden; '''). |
['S'] | Создайте ссылку на структуру, перечисление, трейт, функцию, … S. |
['S'](crate::S) | Также можно использовать пути в виде ссылок markdown. |
#![globals]
Атрибуты, влияющие на весь трейт или приложение:
Команда компилятору | Уровень | Комментарий |
---|---|---|
#![no_std] | C | Не импортирует (автоматически) std; вместо этого использует ядро. |
#![no_implicit_prelude] | CM | Не добавляет прелюдию, нужно вручную импортировать None, Vec, … |
#![no_main] | C | Не включайть main() в приложения. |
#![feature(a, b, c)] | C | Не полагайтесь на функции, которые могут никогда не стабилизироваться. |
#![windows_subsystem = "x"] | C | В Windows, создайть консоль или приложение для Windows. |
#![crate_name = "x"] | C | Указать текущее название крейта, например, при неиспользовании cargo. |
#![crate_type = "bin"] | C | Указать текущий тип крейта (bin, lib, dylib, cdylib, …). |
#![recursion_limit = "123"] | C | Установить предел рекурсии времени компиляции для deref, макросов, … |
#![type_length_limit = "456"] | C | Ограничивает максимальное количество подстановок типов. |
#[panic_handler] | F | Сделайть обработчик паники приложения fn f(&PanicInfo) -> ! . |
#[global_allocator] | S | Сделать статический элемент impl. Глобальный распределитель GlobalAlloc. |
C - означает на уровне крейта (обычно указывается как #![ my_attr] в файле верхнего уровня). M - модуля. F - функции. S - статических.
#[code]
Атрибуты, в первую очередь управляющие эмитируемым кодом:
Команда компилятору | Уровень | Комментарий |
---|---|---|
#[non_exhaustive] | T | Перспективная структура или перечисление, намек на то, что он может изменится в будущем. |
#[path = "x.rs"] | M | Получить модуль из нестандартного файла. |
#[inline] | F | Предлагает компилятору инлайн функцию. |
#[inline(always)] | F | Предлагает компилятору инлайн функцию, или ещё что. |
#[inline(never)] | F | Инструктирует компилятор не использовать инлайн (внешние) функции. |
#[cold] | F | Намек на то, что функция, вероятно, не будет вызываться. |
#[target_feature(enable="x")] | F | Включите функцию CPU (например, avx2) для кода unsafe функции. |
#[track_caller] | F | Позволяет функции находить вызывающего абонента для улучшения сообщений паники. |
#[repr(X)] | T | Использовать другое представление вместо используемого по умолчанию: |
- #[repr(C)] | T | Использовать C-совместимый (FFI), предсказуемый (transmute) макет. |
- #[repr(C, u8)] | enum | Присвоить перечислению дискриминант указанного типа. |
- #[repr(transparent)] | T | Присвоить одноэлементному типу тот же макет, что и содержащемуся полю. |
- #[repr(packed(1))] | T | Более низкое выравнивание структуры и содержащихся полей, умеренно склонное к UB. |
- #[repr(align(8))] | T | Выравнивание структуры по заданному значению, например, для типов SIMD. |
#[no_mangle] | * | Использует имя элемента непосредственно в качестве имени символа, не искажает. |
#[no_link] | X | Не связывает extern crate, когда нужны только макросы. |
#[link(name="x", kind="y")] | X | Собственный lib для связи при поиске символа. |
#[link_name = "foo"] | F | Имя символа для поиска разрешения внешней функции. |
#[link_section = ".sample"] | FS | Имя раздела объектного файла, в который должен быть помещен элемент. |
#[export_name = "foo"] | FS | Экспортирует функцию или статический объект под другим именем. |
#[used] | S | Не оптимизирует статическую переменную, несмотря на то, что она выглядит неиспользуемой. |
M - модуля. F - функции. S - статических. T - типа. X - означает что-то особенное. * - означает практически любой предмет.
#[quality]
Команда компилятору | Уровень | Комментарий |
---|---|---|
#[allow(X)] | * | Проинструктируйте rustc / clippy для … игнорировать класс X с возможными проблемами. |
#[warn(X)] $^1$ | * | … создает предупреждение, хорошо смешивается с clippy линтами. |
#[deny(X)] $^1$ | * | … неудачная компиляция |
#[forbid(X)] $^1$ | * | … выполнить компиляцию с ошибкой и предотвратить последующие переопределения разрешений. |
#[deprecated = "msg"] | * | Сообщить пользователям, что вы допустили ошибку в конструкции. |
#[must_use = "msg"] | FTX | Определяет, что возвращаемое значение проверки компилятора обрабатывается вызывающим абонентом. |
#[test] | F | Помечает функцию как тест, выполняемой cargo test. |
#[ignore = "msg"] | F | Компилирует, но пока не выполняет некоторые #[test] . |
#[should_panic] | F | Тест должен panic!(), чтобы действительно прошел. |
#[bench] | F | Маркировать функцию на стенде/в качестве эталона для cargo bench. |
$^1$ Есть некоторые споры, какой из них является лучшим для обеспечения высокого качества крейтов. Активно поддерживаемые крейты для нескольких разработчиков, вероятно, выигрывают от более агрессивного отрицания или запрета линта; менее регулярно обновляемые, вероятно больше от консервативного использования warn (так как будущие обновления компилятора или clippy могут внезапно сломать, в этом случае работающий код будет с незначительными проблемами).
Тесты | Уровень | Комментарий |
---|---|---|
#[test] | F | Помечает функцию как тест, выполняемый cargo test . |
#[ignore = "msg"] | F | Компилирует, но пока не выполняет #[test]. |
#[should_panic] | F | Тест должен паниковать чтобы быть успешным. |
#[bench] | F | Маркировать функцию в bench/`` в качестве эталона для cargo bench`. |
Форматирование | Уровень | Комментарий |
---|---|---|
#[rustfmt::skip] | * | Предотвращает очистку элементов cargo fmt . |
#![rustfmt::skip::macros(x)] | CM | … от очистки макроса x. |
#![rustfmt::skip::attributes(x)] | CM | … от очистки атрибута x. |
Документация | Уровень | Комментарий |
---|---|---|
#[doc = "Explanation"] | * | То же самое, что добавить комментарий /// doc. |
#[doc(alias = "other")] | * | Установить другое имя, которое пользователи могут искать в документах. |
#[doc(hidden)] | * | Запретить отображение элемента в документах. |
#![doc(html_favicon_url = "")] | C | Задает иконку для документов. |
#![doc(html_logo_url = "")] | C | Логотип, используемый в документах. |
#![doc(html_playground_url = "")] | C | Генерирует кнопки Run и использует данную службу. |
#![doc(html_root_url = "")] | C | Базовый URL-адрес для ссылок на внешние крейты. |
#![doc(html_no_source)] | C | Запрещает включение источника в документы. |
C - означает на уровне крейта (обычно указывается как #![ my_attr] в файле верхнего уровня). M - на уровне модуля. F - на уровне функции. T - на уровне типа. X - означает что-то особенное. * - означает практически любой предмет.
#[macros]
Макросы | Уровень | Комментарий |
---|---|---|
#[macro_export] | ! | Экспорт macro_rules! публичность(pub) на уровне модуля. |
#[macro_use] | MX | Макросы сохраняются в прошлых модулях или импортируются из внешнего модуля. |
#[proc_macro] | F | Помечает функцию как функциональный процедурный макрос, вызываемый например как m!(). |
#[proc_macro_derive(Foo)] | F | Пометить функцию как производный макрос, который может #[derive(Foo)] |
#[proc_macro_attribute] | F | Пометить функцию как макрос атрибута, который может понимать #[x] |
#[derive(X)] | T | Пусть какой-нибудь макрос proc обеспечит impl трейта X. |
! - макрос. M - на уровнемодуля. F - на уровнефункции. X - означает что-то особенное.
#[cfg]
Атрибуты, управляющие условной компиляцией:
Атрибуты конфигурации | Уровень | Комментарий |
---|---|---|
#[cfg(X)] | * | Включить элемент, если конфигурация X сохранена. |
#[cfg(all(X, Y, Z))] | * | Включить элемент, если все параметры сохраняются. |
#[cfg(any(X, Y, Z))] | * | Включить элемент, если хотя бы один параметр сохраняется. |
#[cfg(not(X))] | * | Включить элемент, если X не сохраняется. |
#[cfg_attr(X, foo = "msg")] | * | Применить #[foo = «msg»] , если сохраняется конфигурация X. |
Следует отметить, что опции обычно можно задавать несколько раз, т.е. одна и та же команда может использоваться с несколькими значениями. Можно ожидать, что #[cfg (target_feature = «avx»)] и #[cfg (target_feature = «avx2»)] будут истинными одновременно.
Известные варианты | Уровень | Комментарий |
---|---|---|
#[cfg(target_arch = "x86_64")] | * | Модуль архитектуры CPU компилируется для. |
#[cfg(target_feature = "avx")] | * | Доступен ли конкретный класс инструкций. |
#[cfg(target_os = "macos")] | * | Операционная система, в которой будет запущен код. |
#[cfg(target_family = "unix")] | * | Семейство операционных систем принадлежит. |
#[cfg(target_env = "msvc")] | * | Как библиотеки DLL и функции взаимодействуют в ОС. |
#[cfg(target_endian = "little")] | * | Основная причина отказа. |
#[cfg(target_pointer_width = "64")] | * | Сколько битов имеют указатели, usize и команды CPU. |
#[cfg(target_vendor = "apple")] | * | Автор ОС. |
#[cfg(debug_assertions)] | * | Будут ли debug_assert!() и его “друзья” паниковать. |
#[cfg(panic = "unwind")] | * | Произойдет ли раскручивание стека или прерывание на панике. |
#[cfg(proc_macro)] | * | Крейт скомпилирован как proc макрос. |
#[cfg(test)] | * | Скомпилированно ли cargo test. |
#[cfg(feature = "serde")] | * | Когда ваш крейт был скомпилирован с функцией serde. |
* - означает практически любой предмет.
build.rs
Переменные среды и выходные данные, связанные со сценарием перед построением.
Среда ввода | Комментарий |
---|---|
CARGO_FEATURE_X | Набор переменной среды для каждого активированного компонента x. |
-CARGO_FEATURE_SERDE | Если функция serde была включена. |
-CARGO_FEATURE_SOME_FEATURE | Если включено в некоторые функции, будет преобразовано в _. |
CARGO_CFG_X | Изменяет конфигурацию, разрешения преобразует в _. |
-CARGO_CFG_TARGET_OS=macos | Если target_os были установлены в macos. |
-CARGO_CFG_TARGET_FEATURE=avx,avx2 | Если target_feature были установлены avx и avx2. |
OUT_DIR | Где должны быть размещены выходные данные. |
TARGET | Цель компилируется. |
HOST | Запуск этого сценария сборки. |
PROFILE | Может быть для отладки или запуска. |
Доступно в build.rs через env::var()?. Список не является исчерпывающим.
Выходная строка | Комментарий |
---|---|
cargo:rerun-if-changed=PATH | Снова запустите этот build.rs, если PATH изменился. |
cargo:rerun-if-env-changed=VAR | Снова запустите этот build.rs, если переменные среды изменились. |
cargo:rustc-link-lib=[KIND=]NAME | Связывайте вашу библиотеку, как будто с помощью опции -l. |
cargo:rustc-link-search=[KIND=]PATH | Поиск пути для нативной библиотеки, как будто с помощью опции -L. |
cargo:rustc-flags=FLAGS | Добавляет специальные флаги в компилятор. |
cargo:rustc-cfg=KEY[="VALUE"] | Опция cfg, которая будет использоваться в последующей компиляции. |
cargo:rustc-env=VAR=VALUE | Значение доступно через env!() в крейте во время компиляции. |
cargo:rustc-cdylib-link-arg=FLAG | При построении cdylib передаёт флаг компоновщика. |
cargo:warning=MESSAGE | Выдает предупреждение компилятора. |
Выводится из build.rs через println!(). Список не является исчерпывающим.