TokioMiddleTechnical
Как feature flags Tokio влияют на binary size и доступные модули?
Каждый feature flag добавляет соответствующий код в бинарник (net +120 KB, sync +100 KB и т.д.). full удобен для разработки, но в production используйте минимальный явный набор; cargo-bloat помогает измерить реальный вклад.
Влияние feature flags на binary size и модули
Каждый feature flag Tokio добавляет скомпилированный код конкретного модуля в итоговый бинарник. Rust компилирует только то, что запрошено — но «лишние» флаги могут незаметно раздуть исполняемый файл и замедлить сборку.
Измерение влияния
# Создайте два одинаковых проекта с разными флагами и сравните
cargo build --release
ls -lh target/release/my_app
# Анализ символов
nm --size-sort target/release/my_app | tail -20
# Что занимает место в бинарнике
cargo install cargo-bloat
cargo bloat --release --crates
cargo bloat --release -n 20 # топ-20 функций по размеру
Конкретные примеры влияния флагов
# Вариант A: минимальный
[dependencies]
tokio = { version = "1", features = ["rt", "macros"] }
# Модули: current_thread runtime, #[tokio::main]
# Нет: net, fs, time, sync — их API недоступны
# Вариант B: полный
[dependencies]
tokio = { version = "1", features = ["full"] }
# Добавляет: epoll/kqueue driver, fs syscalls,
# DNS resolver, Unix sockets, signal handling
# Реальное сравнение (stripped release build)
# Только rt + macros: ~280 KB
# + net: +120 KB (epoll/IO driver)
# + fs: +80 KB
# + time: +60 KB
# + sync: +100 KB (channels, mutex)
# full (всё): ~900 KB
# С LTO и strip:
cargo build --release
strip target/release/my_app
Какие модули становятся доступны
// С features = ["net", "io-util"]
use tokio::net::{TcpListener, TcpStream}; // Ok
use tokio::io::{AsyncReadExt, AsyncWriteExt}; // Ok
// Без feature "time" — не скомпилируется:
// use tokio::time::sleep; // error: module `time` not found
// Без feature "sync":
// use tokio::sync::Mutex; // error
// Но std::sync::Mutex — всегда доступен
Оптимизация binary size
# Cargo.toml — profile.release настройки
[profile.release]
opt-level = "z" # оптимизация по размеру (vs "3" по скорости)
lto = true # Link-Time Optimization — убирает мёртвый код
codegen-units = 1 # одна единица компиляции — лучший LTO
strip = true # убрать символы отладки
panic = "abort" # убрать unwinding код (~50 KB)
Проверка какие features включены транзитивно
# Посмотреть все активированные features для tokio
cargo metadata --format-version 1 | \
python3 -c "
import json,sys
d=json.load(sys.stdin)
for p in d['packages']:
if p['name']=='tokio':
print(p['features'])
"
# Или через cargo tree
cargo tree -e features | grep tokio
Рекомендации для библиотечных крейтов
[features]
default = []
async-net = ["tokio/net", "tokio/io-util"]
async-fs = ["tokio/fs"]
[dependencies]
tokio = { version = "1", optional = true, default-features = false }
Библиотека не должна принуждать пользователей включать features, которые им не нужны. Объявляйте Tokio как опциональную зависимость и прокидывайте features через собственные флаги.
Подводные камни
- full в production:
features = ["full"]удобен для прототипов, но тянет весь Tokio включая DNS-resolver, Unix-сокеты и signal handler — даже если приложение их не использует. - Cargo feature unification: если зависимость A требует
tokio/net, а зависимость B требуетtokio/time, итоговый бинарник включит оба — минимальный набор в Cargo.toml не защищает от транзитивных включений. - Размер vs скорость:
opt-level = "z"уменьшает размер, но может замедлить горячие пути; измеряйте перед принятием решения. - lto = true замедляет сборку: LTO требует дополнительного прохода линковщика — добавьте только в profile.release, не в profile.dev.
- Скрытые зависимости через "full": некоторые крейты (например, axum) включают
tokio/fullтранзитивно — используйтеcargo tree --featuresдля аудита реального набора. - panic = "abort" несовместим с некоторыми C-библиотеками: если FFI-зависимость ожидает размотку стека (unwinding),
panic = "abort"приведёт к некорректному завершению.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.