Как в Rust создать пакет, который содержит и библиотеку (lib), и исполняемый файл (bin)?
Структура пакета с библиотекой и бинарником
В Rust и Cargo нет ничего сложного. По умолчанию, если в папке src/ есть файл lib.rs, Cargo считает, что это библиотека. Если есть main.rs — это бинарник. Если есть оба файла — Cargo автоматически собирает и то, и другое. Никаких специальных настроек в Cargo.toml для этого не требуется (кроме случаев, когда нужно несколько бинарников).
Пример минимального проекта
Создайте следующую структуру:
my_project/
├── Cargo.toml
└── src/
├── lib.rs # библиотека
└── main.rs # исполняемый файл
Cargo.toml — самый простой:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
Код библиотеки (src/lib.rs)
/// Публичная функция для обработки CSV (пример)
pub fn process_csv(data: &str) -> Vec<Vec<String>> {
data.lines()
.filter(|line| !line.trim().is_empty())
.map(|line| line.split(',').map(|s| s.trim().to_string()).collect())
.collect()
}
// Приватная функция, недоступная из main.rs
fn _helper() {
println!("Внутренний помощник библиотеки");
}
Код бинарника (src/main.rs)
// Подключаем библиотеку из того же пакета
// Имя пакета (my_project) становится именем крейта
use my_project::process_csv;
fn main() {
let csv_data = "\
name,age,city
Alice,30,New York
Bob,25,London";
let result = process_csv(csv_data);
for row in &result {
println!("{:?}", row);
}
}
Как это работает
- Библиотека (lib.rs) — это отдельный крейт с именем вашего пакета (в примере
my_project). Все, что помеченоpub, доступно снаружи. - Бинарник (main.rs) — это тоже отдельный крейт, но он неявно считается "зависимым" от библиотеки. Поэтому вы можете использовать
use my_project::.... - Cargo собирает два артефакта:
target/debug/my_project(бинарник) иtarget/debug/libmy_project.rlib(библиотека).
Проверка сборки
Запустите в терминале:
cargo build # Соберет и библиотеку, и бинарник
cargo run # Запустит бинарник
Вы увидите результат обработки CSV. При этом библиотеку можно подключить в другой проект, добавив в его Cargo.toml:
[dependencies]
my_project = { path = "../my_project" } # или версию с crates.io
Дополнительные возможности
Если вам нужно несколько бинарников (например, main.rs и tool.rs), укажите их в Cargo.toml:
[[bin]]
name = "main_tool"
path = "src/bin/main_tool.rs"
[[bin]]
name = "helper"
path = "src/bin/helper.rs"
Но для одного бинарника + библиотеки это не требуется.
<<Альтернативный подход: разделение на workspace
Если вы планируете, что библиотека и бинарник будут развиваться независимо, или если у вас несколько бинарников, использующих одну библиотеку, лучше использовать workspace (рабочую область). Это более "взрослый" подход.
Структура будет такой:
my_project/
├── Cargo.toml # workspace-файл
├── lib_core/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs # библиотека
└── bin_tool/
├── Cargo.toml
└── src/
└── main.rs # бинарник
Корневой Cargo.toml (workspace):
[workspace]
members = ["lib_core", "bin_tool"]
В bin_tool/Cargo.toml указываете зависимость:
[dependencies]
lib_core = { path = "../lib_core" }
Плюсы: четкое разделение ответственности, возможность публиковать библиотеку отдельно на crates.io, независимые версии. Минусы: чуть больше файлов и настроек.
Для простого проекта (один бинарник + библиотека) первый ответ (lib.rs + main.rs в одной папке) — идеальный выбор.