Как в Cargo создать пакет, который содержит и библиотеку, и исполняемый файл?
Основное решение: стандартная структура Cargo
В Rust пакет (crate) может содержать как библиотеку, так и один или несколько исполняемых файлов. Cargo автоматически определяет их по наличию файлов src/lib.rs (библиотека) и src/main.rs (главный бинарник). Если присутствуют оба файла, пакет считается гибридным.
Ваш код почти правильный, но есть нюансы. Вот корректная структура:
my_project/
├── Cargo.toml
└── src/
├── lib.rs # библиотека
└── main.rs # бинарник
Шаг 1: Библиотека (src/lib.rs)
Экспортируйте функции с ключевым словом pub. Они станут частью публичного API библиотеки:
// src/lib.rs
pub fn factorial(n: u64) -> u64 {
(1..=n).product()
}
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
Шаг 2: Бинарник (src/main.rs)
В бинарнике вы можете обращаться к библиотеке как к внешнему крейту с тем же именем, что и пакет (по умолчанию — имя проекта). Не используйте mod lib; — это пытается объявить модуль, а не подключить библиотеку. Правильно — использовать use:
// src/main.rs
// Имя крейта совпадает с именем пакета в Cargo.toml
use my_project::{factorial, fibonacci};
fn main() {
println!("Факториал 5: {}", factorial(5));
println!("Фибоначчи 10: {}", fibonacci(10));
}
Шаг 3: Cargo.toml
В простейшем случае ничего дописывать не нужно. Cargo сам определит:
- Библиотеку — по
src/lib.rs(имя крейта = имя пакета) - Бинарник — по
src/main.rs(имя бинарника = имя пакета)
Если хотите явно указать имена или добавить несколько бинарников, используйте секции [lib] и [[bin]]:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
[lib]
name = "my_lib" # имя библиотеки (по умолчанию = имя пакета)
path = "src/lib.rs"
[[bin]]
name = "my_bin" # имя исполняемого файла
path = "src/main.rs"
Как это работает
- Библиотека компилируется в
.rlibфайл. Её можно подключить в других проектах через[dependencies]в ихCargo.toml. - Бинарник компилируется в исполняемый файл. Он может использовать библиотеку этого же пакета как внешний крейт.
- При запуске
cargo runвыполняется главный бинарник. Если бинарников несколько, используйтеcargo run --bin my_bin.
Проверка
Выполните в терминале:
cargo build # скомпилирует и библиотеку, и бинарник
cargo run # запустит main.rs
cargo test # протестирует библиотеку (если есть тесты в lib.rs)
Важные замечания
- Библиотека не может иметь точку входа
fn main(). Она только экспортирует функции, типы и модули. - Бинарник не может быть подключен как зависимость в других проектах.
- Если вы хотите, чтобы бинарник использовал внутренние (непубличные) функции библиотеки, они должны быть помечены как
pub(crate)или простоpub.
Альтернативный подход: раздельные крейты в одном workspace
Если ваш проект разрастается, и вы хотите чётко разделить библиотеку и бинарник (например, чтобы библиотека была независимой и могла иметь свои зависимости), используйте workspace (рабочее пространство).
Структура проекта:
my_project/
├── Cargo.toml # корневой workspace
├── my_lib/
│ ├── Cargo.toml # библиотека
│ └── src/lib.rs
└── my_bin/
├── Cargo.toml # бинарник
└── src/main.rs
Корневой Cargo.toml:
[workspace]
members = ["my_lib", "my_bin"]
В my_bin/Cargo.toml добавляем зависимость от библиотеки:
[dependencies]
my_lib = { path = "../my_lib" }
Теперь бинарник может использовать библиотеку через use my_lib::factorial;. Преимущество: библиотека и бинарник компилируются отдельно, у каждого свои версии и зависимости. Это удобно для крупных проектов или если вы планируете публиковать библиотеку на crates.io.