Build Tools

Rust Tools

External links:

rustup component add rustfmt 
rustup component add rust-src
git clone https://github.com/AstroNvim/AstroNvim ~/.config/nvim
nvim +PackerSync

# After install use commands:
:LspInstall rust -> rust-analyzer
:TSInstall rust
  • Neovide GUI upgrade on Astro Vim
git clone https://github.com/neovide/neovide
cd neovide
cargo build --release
  • EVCXR or iRUST REPL
cargo install evcxr_repl
cargo install irust

VSCode/Zed Extensions for Rust

  • (VSCode-only) CodeLLDB
  • (VSCode-only) Error Lens - показывает ошибки кода сразу в коде
  • (VSCode-only) indent-rainbow - подсвечивает цветом отступы разной глубины
  • (VSCode + Zed) Dependi (бывш crates) - сообщает, если пакеты устарели

VSCode Settings

(For CodeLLDB) Allow breakpoints everywhere: "debug.allowBreakpointsEverywhere": true cargo check: поменять check на clippy: "rust-analyzer.check.command": "clippy"

Rust Prelude

Rust has a Prelude - a set of libraries included in every project. See current libs included in Prelude

User input

std::io::stdin library is used to get user input from standard input stream. Not included in Prelude:

use std:io

let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to load");

.expect handles Err variant of read_line function, crashing the program with the defined error message.

Subsections of Build Tools

Bacon

Links:

Bacon

Bacon - CLI-утилита запуска команд сборки или тестов по триггеру.

Установка

cargo install --locked bacon
bacon test # run from project folder 

Настройка

Запуск команды инициализации:

bacon --init

Приводит к появлению в папке проекта файла bacon.toml с разными вариантами команд, и можно дописать свою секцию, например:

[jobs.check-examples]
command = ["cargo", "run", "--example", "iterators"]
watch = ["examples"]
need_stdout = true

command - команда и параметры watch - отслеживаемая папка need_stdout - stdout вывод кода показывать к терминале Далее запуск секции:

bacon check-examples

Интерактивный перезапуск компиляции

This will compile+build the code in examples folder, file “variables.rs”. Very convenient to try test different stuff. For live development do:

bacon run -- -q # сборка и запуск текущего проект
bacon run -- -q --example <файл> # сборка и запуск файла в папке examples

bacon test # запуск unit-тестов (например, определённых для lib.rs)

-q - убрать вывод деталей компиляции

(deprecated) Cargo Watch - интерактивный перезапуск компиляции

This will compile+build the code in examples folder, file “variables.rs”. Very convenient to try test different stuff. For live development do:

cargo watch -q -c -x 'run -q --example variables'

-x - rerun upon code change -c - clear terminal each time -q - quiet mode ❗Проект cargo watch заморожен, более не развивается.

Cargo

Link: Cargo profiles - https://doc.rust-lang.org/cargo/reference/profiles.html Mold linker - https://github.com/rui314/mold Условная компиляция - https://doc.rust-lang.org/reference/conditional-compilation.html

Cargo

Use Cargo for managing projects.

cargo new test_project // create project with binary file src/main.rs
// OR
cargo new test_project --lib // create project with library file src/lib.rs 

cd test_project

Source code is in src folder.

cargo build # build project for debug, do not run
cargo run # build & run project
cargo check # fast compiling
cargo build --release # slow build with optimizations for speed of release version

Documentation of methods & traits used in code can be compiled an opened in browser with Cargo command:

cargo doc --open

Зависимости кода в cargo.toml

  • [dependencies] — Package library dependencies - видно отовсюду, из тестов;
  • [dev-dependencies] — Dependencies for examples, tests, and benchmarks. При сборке ПО видны только для тестов (в рамках #[cfg(test)] mod tests {});
  • [build-dependencies] — Dependencies for build scripts.

Сборка бинарных файлов

Можно в src папке создать подпапку bin, и там сложить вариации бинарных файлов. После чего указывать при сборке cargo run --bin <название файла в папке bin>

Сборка Examples

Create “examples” folder beside the “src” folder. Create a file named, for example, “variables.rs” and place some test code in the folder. Then in the project root folder run:

cargo run --example variables

❗Примеры в Examples папке наследуют features и прочие параметры из общего Cargo.toml. В случае условной компиляции (см, далее) команда будет выглядеть: cargo run --example variables --features mock

Условная компиляция

Позволяет исключить часть кода в зависимости от условий.

Атрибут #[cfg(condition)]

#[cfg(target_os = "windows")]
fn win_foo() {}

Атрибут #[cfg_attr(condition, attribute)]

#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(windows, path = "windows.rs")]
mod os;

Макрос cfg!(condition)

let os = if cfg!(unix) {"unix"} else {"windows"};

Можно комбинировать условия с помощью all(), any(), not():

#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() {}

Mock-компиляция

Вариант условной компиляции в целях тестирования:

#[cfg(feature = "debug")] // код сработает только с флагом debug
fn get_data() -> String {
    "мулька".to_string()
}

#[cfg(feature = "mock", feature = "debug")] // код сработает только с флагами mock И debug
fn get_data() -> String {
    "хитрая мулька".to_string()
}

#[cfg(not(feature = "debug"))] // код сработает без флага debug
fn get_data() -> String {
    "реальные данные".to_string()
}

fn main() {
    println!("{}", get_data());
}

Features нужно специально включать при сборке командой cargo run --features debug. Либо можно прописать флаг включения default = ["debug"] в файле Cargo.toml:

[features]
default = ["debug"] # по-умолчанию флаг = ВКЛ
debug = []

Отключить работу флага default можно командой cargo run --no-default-features.

Условная компиляция, встроенная в Cargo, позволяет подключать зависимости опционально. Можно проверять фичи в коде:

#[cfg(feature = "win")]
f use_winapi() {
let void = winapi::ctypes::c_int::from(t : 5);
let f_typ = winapi_util::console::Color::Blue;
}

Пример с подключением библиотеки rand только для создания случайного значения в “mock-варианте” кода:

[features]
default = ["mock"] # включить флаг mock по-умолчанию
mock = ["rand"] # использовать рандимозатор только при включенном флаге
mock-persistent = ["rand"]

[dependencies]
rand = { version = "0.10.0", optional = true } # опциональный рандомизатор

Диагностика сборки

cargo-bloat для поиска проблем

cargo install cargo-bloat
cargo bloat --release

Определяет, какие зависимости задерживают сборку.

cargo build –timings

Создаёт отчёт в HTML с указанием трат времени:

cargo build --timings
# далее открыть target/cargo-timings/cargo-timing.html

cargo-llvm-lines

Показывает, какие generic-функции создают больше всего нагрузки для LLVM:

cargo install cargo-llvm-lines
cargo llvm-lines --release

Generics создают мономорфизм - каждый новый тип создаёт новый код при сборке.

Ускорение сборки

Профили Cargo

Можно управлять ходом сборки проекта с помощью профилей. В том числе это сильно виляет на скорость сборки. Примеры профилей, которые можно создать в cargo.toml файле:

[profile.dev]
opt-level = 0          # No optimization (fastest compilation LLVM)
debug = 1              # Line info only (not full debug symbols)
codegen-units = 256    # More parallelism (max is 256)
incremental = true     # Recompile only changed code

[profile.release]
opt-level = 3          # Maximum runtime performance
lto = "thin"           # Faster than "fat" LTO, still good optimization
codegen-units = 1      # Better optimization at cost of compile time

Для debug сборок, opt-level = 0 и высокое число codegen-units даю самую высокую скорость сборки (разница в скорости в 108 раз!).

Кеширование сборки зависимостей

С помощью sccache можно кешировать собранные артефакты, в том числе таскать кеш между системами.

# Установка
cargo install sccache

# Применение
export RUSTC_WRAPPER=sccache
cargo build

cargo-wizard - быстрая оптимизация

Простой инструмент выбора типа оптимизации:

cargo install cargo-wizard
cargo wizard

Даёт 3 шаблона: fast compilationfast execution, и small binary size

Удалить лишние неиспользуемые зависимости

cargo install cargo-machete
cargo machete  # находит неиспользуемые crates

Создание RAM-disk для пути сборки

Linux/macOS:

mkdir -p /tmp/rust-target
mount -t tmpfs -o size=8G tmpfs /tmp/rust-target
export CARGO_TARGET_DIR=/tmp/rust-target

Управление потоками сборки

Использовать все ядра, кроме 2 (чтобы ОС не повисла):

cargo build -j $(nproc --ignore=2)

Либо указать в .cargo/config.toml:

[build]
jobs = 8

Panic Response

В ответ на панику, по умолчанию программа разматывает стек (unwinding) - проходит по стеку и вычищает данные всех функций. Для уменьшения размера можно просто отключать программу без очистки - abort. Для этого в файле Cargo.toml надо добавить в разделы [profile]:

[profile.release]
panic = 'abort'

Mold (только для Linux)

Проект для Linux (в macOS есть коммерческий аналог Sold, который ненамного лучше сборщику в XCode 15+, потому использование не оправдано).

Настройка

  • Ubuntusudo apt-get install mold clang
  • Fedorasudo dnf install mold clang
  • Arch Linuxsudo pacman -S mold clang

Создать в проекте папку и файл .cargo/config.toml (либо для всех проектов сразу - папка и файл ~/.cargo/config.toml) и вписать:

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

Где архитектуру нужно заменить на текущую: узнать её командой rustc -vV.

Проверка работы

Для проверки в папке с бинарным файлом запустить команду:

$ readelf -p .comment target/debug/your-app

String dump of section '.comment':
  [     0]  mold 2.4.0 ( ... )

Cargo Cache

Links:

Инструмент для оптимизации кеша скачиваний Cargo.

Установка и применение

cargo install cargo-cache

# Nightly версия
cargo install --git https://github.com/matthiaskrgr/cargo-cache cargo-cache

Для применения ввести:

cargo cache --info # какие папки у кеша и их размеры
cargo cache --top-cache-items 5 # топ-5 файлов по размеру

cargo cache --dry-run # показать, что будет удалено
cargo cache -a # авто-очистка (autoclean)
cargo cache -e # (дорогая/expensive) чистка + рекомпрессия git-архивов

Cargo Generate

Links:

Инструмент позволяет использовать готовые проекты из репозитория Git.

Установка и применение

cargo install cargo-generate

Для применения проекта нужно указать его ссылку на Git:

cargo-generate gen --git git@github.com:aagern/cargogen.git projlogged -n smarthouse

Где:

  • projlogged - название папки-шаблона, если в git-репозитории шаблонов несколько в разных папках;
  • -n smarthouse - название нового проекта, который будет сделан на базе шаблона.

Cargo Wizard

Links:

Инструмент для быстрой оптимизации времени сборки проекта.

Установка и применение

cargo install cargo-wizard

Для применения ввести:

cargo wizard

Далее настроить параметры согласно задачам.

❗ На macOS не следует включать LLD или MOLD линковщики. Нативный xcode - оптимальный вариант.

❗Для наилучших оптимизаций под скорость сборки лучше применять Rust Nightly:

Cargo Workspaces

Links:

Проект с Cargo Workspaces

Объединение кода в 1 проект. В Cargo.toml нужно объявить его структуру:

[package]
name = "ardan_workspace"
version = "0.1.0"
edition = "2021"

[dependencies]

[workspace]
members = [ 
  "session1/hello_world",
  "session1/variables",
]

После этого можно добавлять подпрограммы в members, через cargo new <подрограмма>.

Создание и подключение библиотеки в Workspace

Создание библиотеки вместо бинарника - `cargo new –lib examplelib

Прописать в файле cargo.toml у бинарной программы в workspace зависимость:

<..>

[dependencies]
examplelib = { path = "../examplelib"}

Далее, в код бинарной программы включить функции из библиотеки:

use examplelib::function01; // фукнкция должна быть публичной (pub fn)

Единая инициализация и сборка библиотек зависимостей в Workspace

Создаём проект типа workspace и прописываем в его файле Cargo.toml верхнего уровня все библиотеки с версиями (в примере anyhow) в спец разделе [workspace.dependencies]:

[dependencies]
anyhow.workspace = true

[workspace]
members = ["greeter"]

[workspace.dependencies]
anyhow = "1.0.99"

При этом в отдельном разделе [dependencies] указываем, что библиотеки будут распространяться на весь проект. Далее, создаём модуль внутри workspace (в примере = greeter) и в его файле Cargo.toml прописываем, что библиотека берётся из зависимостей workspace:

[dependencies]
anyhow = { workspace = true }

Можно добавлять features к библиотеке из workspace на этапе описания вложенных модулей:

[dependencies] regex = { workspace = true, features = ["unicode"] }

Проверка зависимостей

Можно построить дерево зависимостей и проверить правильность написания cargo.toml файла:

cargo check
cargo tree

cargo-audit

Links:

Cargo audit - это инструмент, который проверяет зависимости проекта на наличие известных уязвимостей. Он не запускает код, не генерирует тесты и не следит за памятью. Вместо этого он сверяет версии всех библиотек (crates) с базой данных уязвимостей (RustSec Advisory Database).

Установка и запуск

cargo install cargo-audit   # установить один раз
cargo audit                 # проверить проект

Пример вывода:

Crate:         regex
Version:       1.4.0
Title:         Regex panic on crafted input
Severity:      high
Solution:      upgrade to >= 1.5.0

Cargo audit находит только те проблемы, о которых кто-то сообщил и добавил в базу. Он не ищет новые, неизвестные уязвимости.

Какие проблемы находит Cargo Audit?

Тип уязвимостиПример
Паника при специальном вводеБиблиотека крашится на строке вида “aaaaaaaa…”
Утечка данныхБиблиотека отправляет данные на левый сервер
Проблемы с безопасностьюВ версии 1.2.3 веб-сервера есть дыра для DoS-атаки
RUSTSEC-уведомленияОфициально зарегистрированные уязвимости

Интеграция с GitHub Actions

- name: Security audit
  run: |
    cargo install cargo-audit
    cargo audit

CI/CD Pipeline

Link: https://github.com/RAprogramm/RustManifest/blob/main/README.ru.md

Проверка кода

Инструменты проверки

Pipeline применения инструментов для проверки кода:

1. Пишешь обычные unit-тесты (проверка очевидного)
   ↓
2. Добавляешь proptest (проверка неочевидного)
   ↓
3. Если есть unsafe → запускаешь Miri (проверка правил Rust)
   ↓
4. Для финальной проверки (опционально) → Valgrind
   ↓
5. Использование сторонних библиотек → Cargo Audit

Когда что использовать

ИнструментЧто делаетЧто ищетКогда использовать
MiriИнтерпретирует кодUndefined Behavior в unsafeПри написании unsafe кода
ValgrindСледит за готовой программойУтечки памяти, ошибки с памятьюДля C/C++ или для поиска утечек
ProptestГенерирует случайные данныеЛогические ошибки, крайние случаиДля проверки корректности функций
Cargo AuditПроверяет список зависимостейИзвестные уязвимости в библиотекахРегулярно, особенно перед релизом

Сравнение по параметрам

ПараметрMiriValgrindProptestCargo Audit
Что запускаетИсходный кодСкомпилированную программуТесты (твой код)Только Cargo.lock
Нужно ли писать кодНетНетДа (свойства)Нет
Скорость работыОчень медленноМедленноКак тестыМгновенно
Находит неизвестные проблемы✅ Да✅ Да✅ Да❌ Только известные
Требует unsafeЧаще даНетНетНет
CI-совместимостьТяжело (медленно)СреднеЛегкоЛегко

GitHub Actions пример основных проверок

name: Rust CI/CD Pipeline

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # Job 1: Format and Lint
  format-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly
          components: rustfmt, clippy
          override: true
      - name: Format check
        run: cargo +nightly fmt -- --check
      - name: Clippy
        run: >-
          cargo clippy --all-targets --all-features --workspace
          -- -D warnings
        
  # Job 2: Build
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      - name: Build release
        run: cargo build --release
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binary
          path: target/release/smarthouse

GitHub Actions со всеми инструментами

Как использовать:

  • Add to your project at .github/workflows/ci.yml
  • Install required dependencies (add to Cargo.toml):
[dev-dependencies]
proptest = "1.4"
  • For Miri, ensure you have unsafe code to test
  • For Valgrind, create a suppressions file (optional):
# Generate suppressions for false positives
valgrind --gen-suppressions=all ./target/debug/your_binary > valgrind.supp

Производительность

  • Fast path (every commit): Build + Clippy + Audit
  • Medium path (PRs): Add Proptest
  • Slow path (main/master branch): Add Miri + Valgrind

Полный код ci.yml:

name: Rust CI/CD Pipeline

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # Job 1: Format and Lint
  format-lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly
          components: rustfmt, clippy
          override: true
      - name: Format check
        run: cargo +nightly fmt -- --check
      - name: Clippy
        run: >-
          cargo clippy --all-targets --all-features --workspace
          -- -D warnings
        
  # Job 2: Build (original)
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      - name: Build release
        run: cargo build --release
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: binary
          path: target/release/smarthouse

  # NEW JOB 3: Security Audit (Cargo Audit)
  # Checks if any dependencies have known vulnerabilities
  security-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # Install and run cargo-audit
      - name: Install cargo-audit
        run: cargo install cargo-audit
      
      - name: Run security audit
        run: cargo audit
        # This will fail the build if any vulnerabilities found
        # Exit code 1 if vulnerabilities detected
      
  # NEW JOB 4: Property-based Testing (Proptest)
  # Runs extended tests with randomly generated data
  proptest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      # Run tests including proptest (slower than regular tests)
      - name: Run property-based tests
        run: cargo test --release --lib -- --include-ignored proptest
        # --include-ignored runs proptest tests marked with #[ignore]
        # Proptest generates thousands of random inputs to find edge cases
      
      # Alternative: Run all tests with proptest enabled (no ignore)
      - name: Run all tests including proptest (alternative)
        run: cargo test --release -- --nocapture
        # This runs ALL tests, including proptest tests
        # Remove this if you prefer the filtered version above
      
  # NEW JOB 5: Unsafe Code Verification (Miri)
  # Checks for Undefined Behavior in unsafe Rust code
  # WARNING: Miri is VERY slow (100-1000x) but catches critical bugs
  miri-check:
    runs-on: ubuntu-latest
    # Only run if you actually have unsafe code
    # Add a condition to avoid slow checks on simple projects
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: nightly  # Miri requires nightly Rust
          components: miri
          override: true
      
      # Install Miri component
      - name: Install Miri
        run: |
          rustup +nightly component add miri
          cargo +nightly miri setup
      
      # Run Miri on tests (catches UB in unsafe code)
      - name: Run Miri on tests
        run: cargo +nightly miri test
        # This will find:
        # - Invalid memory access
        # - Data races in unsafe code
        # - Violations of Rust's aliasing rules
        # - Using uninitialized memory
      
      # Optional: Run Miri on the main binary
      - name: Run Miri on binary (optional)
        run: cargo +nightly miri run
        # Check if the main program has UB
        continue-on-error: true  # Don't fail the build if this fails
        # Miri is extremely slow, so this might timeout in CI
      
  # NEW JOB 6: Memory Analysis (Valgrind)
  # Finds memory leaks and invalid memory access in compiled binary
  # NOTE: Only useful if you have C/C++ dependencies or complex unsafe code
  valgrind-check:
    runs-on: ubuntu-latest
    # Only run on main branch (Valgrind is slow)
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
      
      # Build with debug symbols for better Valgrind output
      - name: Build with debug symbols
        run: cargo build
      
      # Install Valgrind
      - name: Install Valgrind
        run: sudo apt-get update && sudo apt-get install -y valgrind
      
      # Run Valgrind on tests
      - name: Run Valgrind memory check
        run: |
          # Run each test binary through Valgrind
          for test in target/debug/deps/*; do
            if [ -x "$test" ] && [ ! -d "$test" ]; then
              valgrind --leak-check=full \
                       --error-exitcode=1 \
                       --suppressions=valgrind.supp \
                       "$test" 2>&1 | tee -a valgrind.log
            fi
          done
        # This finds:
        # - Memory leaks (Rust usually doesn't have them)
        # - Invalid reads/writes in C/C++ dependencies
        # - Use of uninitialized memory
        continue-on-error: true  # Don't fail the whole pipeline
      
      # Alternative: Run just the main binary
      - name: Run Valgrind on main binary (alternative)
        run: |
          valgrind --leak-check=full \
                   --error-exitcode=1 \
                   target/debug/smarthouse
        # Replace 'smarthouse' with your actual binary name
        continue-on-error: true

  # NEW JOB 7: Coverage Report (Optional)
  # Shows which parts of code are tested (complements Miri/Proptest)
  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install cargo-tarpaulin
        run: cargo install cargo-tarpaulin
      
      - name: Generate coverage report
        run: cargo tarpaulin --out Html --output-dir ./coverage
      
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: ./coverage

Оптимизация CI для скорости

# Only run expensive jobs when unsafe code changes
miri-check:
  if: contains(github.event.pull_request.labels.*.name, 'unsafe-changes')
  
# Cache dependencies to speed up builds
- name: Cache cargo registry
  uses: actions/cache@v3
  with:
    path: ~/.cargo/registry
    key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

Clippy

Настройка

Clippy идёт в составе стандартной поставки Rustup. Пример конфигурации:

cargo clippy --fix -- \
-W clippy::pedantic \
-W clippy::nursery \
-W clippy::unwrap_used \
-W clippy::expect_used

VSCode Settings

cargo check: поменять check на clippy: "rust-analyzer.check.command": "clippy"

Книга

У Clippy есть своя книга в формате Markdown:

cargo install mdbook
# Run from top level of your rust-clippy directory:
mdbook serve book --open
# Goto http://localhost:3000 to see the book

Исключения

Allow Unused / dead code

Убрать warnings на тему неиспользуемых функций или переменных - самой первой строчкой кода включить директиву:

#![allow(unused)]
fn main() {}

Либо у конкретных функций указывать:

#![allow(dead_code)]
fn some_func() { todo!() }

Miri & Valgrind

Links:

Miri - это интерпретатор, который запускает код внутри особой безопасной песочницы. Miri даже не компилирует твою программу в реальный код. Он берёт твой Rust код и интерпретирует, проверяя:

  • Правила заимствования (одна ссылка на запись или много на чтение)
  • Выход за границы массива
  • Использование после перемещения Miri понимает Rust: он знает, что такое &mut и почему два &mut на один объект — это плохо. Valgrind этого не поймёт. Miri замедляет работу кода в 100-1000х раз.

Valgrind запускает уже готовую скомпилированную программу и следит за ней со стороны:

  • Куда она обращается к памяти
  • Не забыла ли освободить то, что взяла
  • Не читает ли то, что уже удалила Valgrind не знает правил Rust: он видит просто машинный код. Поэтому он может найти многие ошибки, но не понимает высокоуровневые концепции вроде заимствования или времени жизни. При этом Valgrind поддерживает проекты с миксом из Rust и C++ и т.д. языками. Valgrind замедляет работу кода в 20-50х раз.

Установка и запуск

Miri работает только в Nightly-версии Rust:

rustup toolchain install nightly
rustup +nightly component add miri
cargo +nightly miri test


valgrind ./my_program  # программу уже скомпилировали

Проверка

Возьмём код с проблемами:

use std::alloc::{alloc, dealloc, Layout};

// BUG 1: Use-after-free (we'll read memory after freeing it)
fn use_after_free() -> u8 {
    let layout = Layout::new::<u8>();
    let ptr = unsafe { alloc(layout) };

    // Write something
    unsafe {
        *ptr = 42;
    }

    // Free the memory
    unsafe {
        dealloc(ptr, layout);
    }

    // BUG: Reading after free! This is undefined behavior.
    let value = unsafe { *ptr };
    value
}

// BUG 2: Memory leak (allocate but never free)
fn memory_leak() -> *mut u8 {
    let layout = Layout::new::<u8>();
    let ptr = unsafe { alloc(layout) };
    unsafe {
        *ptr = 99;
    }

    // BUG: We never call dealloc! The memory leaks.
    ptr
}

// BUG 3: Miri-specific - violating Rust's aliasing rules
fn aliasing_violation() {
    let mut x = 5;
    let raw_ptr = &mut x as *mut i32;

    // Create a second mutable pointer to the same memory
    let raw_ptr2 = &mut x as *mut i32;

    unsafe {
        // Miri catches this: two mutable references to same data in scope
        *raw_ptr = 10;
        *raw_ptr2 = 20; // Undefined behavior per Rust's Stacked Borrows rules
    }
}

fn main() {
    println!("Testing use-after-free...");
    let val = use_after_free();
    println!("Read value: {} (this might be garbage)", val);

    println!("\nLeaking memory...");
    let _leaked = memory_leak();
    println!("Memory leaked successfully!");

    println!("\nViolating aliasing rules...");
    aliasing_violation();
    println!("Aliasing violation completed (somehow)");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_use_after_free() {
        let val = use_after_free();
        // We're just calling it - the bug is inside
        assert!(val == 42 || val != 42); // Always passes, but the UB is the problem
    }

    #[test]
    fn test_aliasing_violation() {
        aliasing_violation(); // Miri should catch this
    }
}

При запуске cargo test ошибок не будет -> тесты напишут Ок!.

В случае проверки с Miri, инструмент отловит проблемы Bug 1 и Bug 3. Утечки памяти не специализация Miri, может пропустить.

Bug 2 с утечкой памяти отловит Valgrind.

Когда что использовать?

Miri — когда ты пишешь:

  • unsafe код
  • Свои коллекции (Vec, HashMap)
  • FFI обёртки (здесь без unsafe не обойтись)
  • Хочешь поймать тонкие нарушения правил Rust

Valgrind — когда нужно:

  • Найти утечки памяти (Rust их почти не допускает, но C да)
  • Проверить программу на другом языке (C, C++, Fortran)
  • Отловить ошибки в уже готовом бинарнике, когда нет исходников

Panamax

Links:

Cargo OFFLINE

Для создания локального сервера можно скачать все пакеты Cargo с помощью проекта Panamax.

cargo install --locked panamax
panamax init ~/test/my-mirror

Нужно зайти в папку my-mirror, проверить настройки в файле mirror.toml. И далее запустить синхронизацию:

panamax sync ~/test/my-mirror

Далее, можно публиковать зеркало по веб через встроенный сервер (по порту 8080):

panamax serve ~/test/my-mirror

На индекс странице сервера будет справка по подключению Rust клиентов к зеркалу. В частности, посредством создания файла настроек ~/.cargo/config :

[source.my-mirror]
registry = "http://panamax.local/crates.io-index"
[source.crates-io]
replace-with = "my-mirror"

Proptest

Links:

Proptest - это инструмент для тестирования на основе свойств (property-based testing). Он не проверяет память и не следит за выполнением кода. Вместо этого он автоматически генерирует тысячи случайных входных данных для твоей функции и проверяет, выполняются ли заданные тобой правила.

Пример работы

use proptest::prelude::*;

// Функция, которая должна работать для любых чисел
fn divide(a: i32, b: i32) -> i32 {
    a / b  // Ой! А если b = 0? Будет паника!
}

// Пишем свойство: "при любых a и b (где b != 0) деление работает"
proptest! {
    #[test]
    fn test_division_doesnt_panic(a: i32, b in 0..1000) {
        let result = divide(a, b);
        prop_assert!(result.is_finite());
    }
}

Для нахождения каких проблем нужен Proptest?

Тип проблемыПример
Крайние случаиДеление на ноль, выход за границы массива
Неочевидные багиФункция работает для 1, 2, 3, но ломается на 127 из-за переполнения
Нарушение инвариантов“После сортировки длина массива не изменилась”
Алгоритмические ошибки“Если я зашифрую и расшифрую — получится исходное сообщение”
РегрессииСтарый код работал для всех чисел, новый — нет