Avatar ·

Как подключить модуль из другого файла в том же проекте на Rust?

Я только начал изучать Rust и пытаюсь разобраться с модульной системой. У меня есть простой проект, созданный через `cargo new my_project`. В нём есть стандартный `src/main.rs`, куда я поместил свою основную логику. Но код становится слишком большим, и я хочу вынести несколько функций в отдельные файлы. Я создал файл `src/math.rs` с простой функцией: ```rust // src/math.rs fn add(x: i32, y: i32) -> i32 { x + y } ``` Теперь я хочу использовать эту функцию в `src/main.rs`. Я попробовал написать `mod math;` и затем вызвать `math::add(2, 3)`, но компилятор выдаёт ошибку: `error[E0603]: function `add` is private`. Я также пробовал разные варианты с `use`, но ничего не работает. Я читал документацию, но она кажется сложной. Объясните, пожалуйста, как правильно организовать код в нескольких файлах? Что я делаю не так? Нужно ли что-то менять в `Cargo.toml`? Как сделать функцию видимой из других модулей? И как вообще правильно структурировать проект, если у меня будет много файлов? Заранее спасибо за помощь! Пример моего `main.rs`: ```rust // src/main.rs mod math; fn main() { let result = math::add(5, 3); println!("Result: {}", result); } ``` <<>>

Основная проблема: видимость (приватность) элементов модуля

Вы почти всё сделали правильно, но в Rust по умолчанию всё в модулях приватно. Функция add в math.rs не помечена как pub, поэтому она недоступна за пределами модуля. Исправьте это:

// src/math.rs
pub fn add(x: i32, y: i32) -> i32 {
    x + y
}

После этого ваш main.rs будет работать без ошибок. Ключевое слово pub делает функцию публичной и доступной для других модулей.

Как работает система модулей в Rust

В Rust нет отдельного понятия "файл как модуль" — файловая система автоматически отображается на модули:

  • Файл src/main.rs — корневой модуль крейта (если вы создаёте исполняемый файл).
  • Файл src/math.rs — модуль math, дочерний по отношению к корневому.
  • Объявление mod math; в main.rs сообщает компилятору: "ищи файл math.rs (или math/mod.rs) и загрузи его как модуль".

Полный пример правильной структуры

Допустим, вы хотите вынести несколько функций в отдельный модуль utils. Создайте файл src/utils.rs:

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

pub fn double(x: i32) -> i32 {
    x * 2
}

Затем в main.rs подключите модуль и используйте его:

// src/main.rs
mod utils;  // объявляем модуль

fn main() {
    let greeting = utils::greet("Alice");
    println!("{}", greeting);
    
    let result = utils::double(10);
    println!("Double: {}", result);
}

Что НЕ нужно делать

  • Не нужно изменять Cargo.toml — модули в пределах одного крейта не требуют дополнительных настроек.
  • Не используйте use для импорта модулей — use нужен для импорта имён (функций, типов) из модуля, а не для объявления самого модуля.
  • Не забывайте про pub — если функция/структура не публичная, она останется приватной.

Как правильно использовать use

После объявления mod math; вы можете сократить вызовы с помощью use:

// src/main.rs
mod math;
use math::add;  // импортируем функцию

fn main() {
    let result = add(5, 3);  // теперь можно вызывать без префикса math::
    println!("Result: {}", result);
}

Также можно импортировать несколько элементов:

use math::{add, subtract};

Вложенные модули (папки)

Если у вас сложная структура, используйте папки. Например, для модуля utils/helpers.rs:

  1. Создайте папку src/utils/.
  2. Поместите в неё файл helpers.rs с публичными функциями.
  3. Создайте файл src/utils/mod.rs (или используйте src/utils.rs как корень модуля) и объявите в нём дочерний модуль:
    // src/utils/mod.rs
    pub mod helpers;
  4. В main.rs объявите внешний модуль:
    // src/main.rs
    mod utils;
    use utils::helpers::some_function;

Теперь ваш код будет организован и легко расширяем. Главное правило: каждый файл — это модуль, а mod объявляет связь между ними.

<<>>

Альтернативный подход: модули внутри одного файла

Если вы не хотите создавать отдельные файлы, Rust позволяет определять вложенные модули прямо в main.rs. Это удобно для небольших проектов или прототипов:

// src/main.rs
mod math {
    pub fn add(x: i32, y: i32) -> i32 {
        x + y
    }
    
    pub fn subtract(x: i32, y: i32) -> i32 {
        x - y
    }
}

fn main() {
    let sum = math::add(10, 5);
    let diff = math::subtract(10, 5);
    println!("Sum: {}, Diff: {}", sum, diff);
}

Этот метод позволяет быстро организовать код без создания дополнительных файлов. Однако для больших проектов лучше использовать отдельные файлы — это улучшает читаемость и упрощает навигацию.

Когда использовать файлы, а когда встроенные модули?

  • Отдельные файлы — для модулей с большим количеством кода (100+ строк) или которые будут переиспользоваться.
  • Встроенные модули — для небольших вспомогательных функций, тестовых модулей или когда код тесно связан с основным.

Также помните: если вы используете отдельные файлы, вы всё равно можете объявлять внутри них вложенные модули. Например, в math.rs можно добавить:

// src/math.rs
pub fn add(x: i32, y: i32) -> i32 { x + y }

pub mod advanced {
    pub fn multiply(x: i32, y: i32) -> i32 { x * y }
}

Тогда в main.rs вы сможете вызывать math::advanced::multiply(2, 3).