Avatar ·

Путаница с модулями в Rust при наличии main.rs и lib.rs

Привет! Я новичок в Rust и пытаюсь разобраться с системой модулей. У меня есть проект, который я создал через `cargo new my_project`. Внутри оказались два файла: `main.rs` и `lib.rs`. Я думал, что смогу просто объявить модуль в одном из них и использовать его в другом, но ничего не работает. Вот моя структура: ``` my_project/ ├── Cargo.toml ├── src/ │ ├── main.rs │ └── lib.rs ``` Я хочу определить функцию в `lib.rs` и вызвать её из `main.rs`. Я попробовал написать в `lib.rs`: ```rust mod utils { pub fn greet(name: &str) -> String { format!("Hello, {}!", name) } } ``` А в `main.rs`: ```rust mod utils; use utils::greet; fn main() { println!("{}", greet("Alice")); } ``` Но компилятор выдаёт ошибку: `error[E0432]: unresolved import 'utils::greet'`. Я также пробовал разные комбинации `pub mod`, `use crate::`, но всё равно путаюсь. Почему модуль, определённый в `lib.rs`, не виден из `main.rs`? И как правильно организовать код, если я хочу использовать общую библиотеку? Помогите, пожалуйста, разобраться с этой кухней модулей в Rust! <<>>

Понимание корневых файлов и модулей в Rust

Ваша путаница связана с тем, что main.rs и lib.rs — это два разных корневых модуля (crate roots) в Rust. Каждый из них является точкой входа для своего крейта: main.rs для исполняемого файла (binary crate), а lib.rs для библиотеки (library crate). Они не видят модули друг друга напрямую.

Когда вы пишете mod utils в lib.rs, вы объявляете модуль внутри библиотечного крейта. А когда вы пишете mod utils в main.rs, вы пытаетесь объявить модуль внутри бинарного крейта — и компилятор ищет файл src/utils.rs или директорию src/utils/mod.rs, но не находит его. Вот почему возникает ошибка.

Решение: Использование библиотеки из бинарного крейта

Чтобы использовать код из lib.rs в main.rs, вам нужно правильно организовать модули и импорты. Вот правильный подход:

Шаг 1: Определите публичный модуль в lib.rs

// src/lib.rs
pub mod utils;

pub fn add(x: i32, y: i32) -> i32 {
    x + y
}

Шаг 2: Создайте файл модуля

Создайте src/utils.rs:

// src/utils.rs
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

Шаг 3: Импортируйте библиотеку в main.rs

// src/main.rs
use my_project::utils::greet;
// или use my_project::add;

fn main() {
    println!("{}", greet("Alice"));
    println!("2 + 3 = {}", my_project::add(2, 3));
}

Обратите внимание: по умолчанию имя крейта совпадает с именем проекта (указано в Cargo.toml в поле name). Если ваш проект называется my_project, то библиотечный крейт доступен как my_project.

Альтернатива: Всё в одном файле

Если вы не хотите разделять код на библиотеку и бинарник, просто удалите lib.rs и работайте только с main.rs:

// src/main.rs
mod utils;

fn main() {
    println!("{}", utils::greet("Bob"));
}

mod utils {
    pub fn greet(name: &str) -> String {
        format!("Hi, {}!", name)
    }
}

Ключевые моменты для запоминания

  • Корневые модули: main.rs и lib.rs — это разные корни. Они не видят модули друг друга напрямую.
  • Использование библиотеки: Чтобы использовать функции из lib.rs в main.rs, импортируйте их через имя крейта: use crate_name::module::function.
  • Публичность: Модули и функции, которые вы хотите использовать из другого крейта, должны быть помечены как pub.
  • Файловая система: Модуль mod utils; ожидает найти файл src/utils.rs или src/utils/mod.rs.

Если вы хотите, чтобы бинарный крейт зависел от библиотечного, но при этом они были в одном проекте — это стандартный паттерн. Вы также можете добавить в Cargo.toml секцию [[bin]] для явного указания путей, но стандартная структура работает отлично.

<<>>

Другой подход: Использование модулей через папки

Если у вас много модулей, удобнее организовать их в отдельные папки. Вместо одного файла utils.rs создайте папку utils/ с файлом mod.rs внутри:

src/
├── main.rs
├── lib.rs
└── utils/
    └── mod.rs

В lib.rs объявите модуль как обычно:

pub mod utils;

В utils/mod.rs определите функции и подмодули:

pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

pub mod inner; // если нужен подмодуль

Тогда в main.rs импорт будет таким же: use my_project::utils::greet;. Этот подход масштабируется лучше, когда модулей много, и помогает избежать длинных файлов.

Ещё один совет: если вы хотите использовать lib.rs исключительно как библиотеку, а main.rs — как точку входа, можно сделать main.rs очень коротким, просто вызывая функции из библиотеки. Так вы отделяете логику от исполнения.