Перейти к содержанию

Инструменты

Posted on:8 февраля 2023 г. at 13:01

Example Dynamic OG Image link

Анатомия проекта

Базовая компоновка проекта, а также общие файлы и папки, используемые 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

Деревья модулей и импорт:

Деревья модулей

Исходные файлы модулей работают следующим образом:

Пространства имен

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 как функцию.

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 плагинов можно найти здесь.

Кросс-компиляция

[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!(). Список не является исчерпывающим.