Путаница с модулями в Rust: main.rs и lib.rs в одном проекте
Основная причина вашей путаницы
В Rust main.rs и lib.rs — это две разные корневые точки входа для модульной системы. Они не видят друг друга автоматически. Каждый из них является корнем своего собственного дерева модулей. main.rs — это корень для бинарного крейта (исполняемого файла), а lib.rs — корень для библиотечного крейта. Если вы хотите использовать код из lib.rs в main.rs, вам нужно явно подключить библиотеку как внешний крейт.
Решение: используйте ключевое слово extern crate или имя проекта
В современном Rust (начиная с edition 2018) вам не нужно писать extern crate в явном виде для крейтов из того же проекта. Достаточно использовать имя вашего проекта (оно указано в Cargo.toml в поле name).
Предположим, ваш Cargo.toml выглядит так:
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
Тогда в main.rs вы можете обращаться к публичным элементам из lib.rs через префикс my_project:::
// main.rs
fn main() {
my_project::greet(); // вызываем функцию из lib.rs
println!("2 + 3 = {}", my_project::utils::add(2, 3)); // вызываем функцию из модуля utils
}
Но это ещё не всё. Вам также нужно правильно объявить модуль utils в lib.rs с модификатором pub, чтобы он был доступен снаружи:
// lib.rs
pub mod utils; // обратите внимание на pub!
pub fn greet() {
println!("Hello from lib!");
}
Теперь структура проекта будет работать. Компилируем и запускаем:
$ cargo run
Hello from lib!
2 + 3 = 5
Как это работает под капотом?
- lib.rs — корень библиотечного крейта. Всё, что объявлено как
pubвlib.rsили в его дочерних модулях (если они тожеpub), становится доступно внешним потребителям. - main.rs — корень бинарного крейта. Он может использовать библиотечный крейт, но не наоборот (библиотека не знает о существовании бинарника).
- Когда вы пишете
my_project::utils::add, Rust ищет крейт с именемmy_project(вашу библиотеку) и затем внутри него — публичный модульutilsи функциюadd.
Альтернатива: используйте use для удобства
Чтобы не писать длинные пути, можно импортировать нужные элементы:
// main.rs
use my_project::greet;
use my_project::utils::add;
fn main() {
greet();
println!("2 + 3 = {}", add(2, 3));
}
Важное замечание про файловую структуру
Если у вас в проекте есть только main.rs (без lib.rs), то все модули объявляются внутри main.rs. Например:
// main.rs
mod utils; // объявляем модуль, который лежит в src/utils.rs
fn main() {
utils::add(2, 3);
}
Но если есть и main.rs, и lib.rs, то mod utils в main.rs и mod utils в lib.rs — это два разных модуля, которые могут даже не знать друг о друге. Будьте внимательны!
Другой подход: сделайте main.rs тонкой обёрткой
В некоторых проектах принято, что main.rs содержит только минимальный код запуска, а вся логика вынесена в lib.rs. Это упрощает тестирование (библиотеку можно тестировать отдельно) и переиспользование кода.
Пример:
// main.rs
fn main() {
my_project::run();
}
// lib.rs
pub mod utils;
pub fn run() {
greet();
println!("2 + 3 = {}", utils::add(2, 3));
}
fn greet() {
println!("Hello from lib!");
}
Обратите внимание: функция greet здесь не объявлена как pub, потому что она используется только внутри библиотеки. Это хорошая практика — скрывать внутренние детали реализации.
Когда использовать lib.rs?
- Если вы пишете библиотеку, которая будет подключаться в другие проекты.
- Если у вас есть бинарник с несколькими исполняемыми файлами (например,
src/bin/), то общая логика выносится вlib.rs. - Для модульного тестирования: код в
lib.rsлегче тестировать, чем код вmain.rs.
Если ваш проект — просто одна программа без необходимости переиспользования, можно обойтись одним main.rs и объявлять модули внутри него. Но разделение на lib.rs и main.rs — это стандартная практика в Rust, к которой стоит привыкнуть.
Ответов пока нет.
Войдите чтобы оставить ответ