Продолжаем

Хорошо. Давайте теперь попробуем подключить в main.rs ещё один модуль — number2. И в нём мы определим функцию one2(), которая возвращает не «one», а «one2».
Подключаем:

use num::number;
use num::number2;


Командуем… Замечательно, но если у нас в корневом модуле num подключено штук 20 других модулей? Не будем же мы постоянно писать:

use num::number;
use num::number2;
use num::next;
...


Для этого мы можем написать так:

use num::{number, number2, next, ...};      // Все разом!


Перечисляем в фигурных скобках через запятую необходимые нам модули, объявленные в модуле num(num — это src/lib.rs). Заканчиваем фигурной скобкой и точкой с запятой. И главное — работает!!!

Вопрос: а можем ли мы использовать те функции из модулей number и number2 в самом модуле num? давате попробуем:
1) Пишем в модуле num функцию libfn():
pub fn libfn() {
	println!("{} + {} = 2",number::one(), number::one());      // one + one = 2
}

3) Далее в main.rs пишем:
extern crate num;

fn main() {
	num::libfn();       // Вызываем libfn().
}


И командуем…
выход:

one + one2 = 2

Работает — значит можем. При этом я использовал путь к функциям относительно текущего местоположения. А можем ли мы использовать их только по имени(без пути)? Пробуем:
1) Пишем в модуль num над объявленными модулями(
Rust требует, чтобы объявления use шли в первую очередь
) строки:

use self::number::one;   // Мы как бы вводим их в нашу область видимости.
use self::number2::one2;


2) + изменяем функцию:

println!("{} + {} = 2",one(), one2());     // one + one2 = 2


И командуем… Работает.
Что можно сказать о self? По умолчанию объявления use используют абсолютные
пути, начинающиеся с корня контейнера. self, напротив, формирует эти пути относительно
текущего места в иерархии. У use есть еще одна особая форма: вы можете использовать use
super::, чтобы подняться по дереву на один уровень вверх от вашего текущего
местоположения. Некоторые предпочитают думать о self как о., а о super как о…, что
для многих командных оболочек является представлением для текущей директории и для
родительской директории соответственно.


Таким образом, я, наверно, могу использовать функции one() и one2() в main.rs, ведь модуль num в нашей облати видимости… Пишем в main.rs:

extern crate num;
fn main() {
	println!("{}",one());      // Ведь одна из двух должна работать.
	println!("{}",num::one());
}


Но возникают ошибки:

error: unresolved name `one`
error: unresolved name `num::one`


И что не так? Пробуем исправить. Пишем в модуль num вместо

use self::number::one;
use self::number2::one2;


это:

pub use self::number::one;
pub use self::number2::one2;


Запускаем:

$ cargo run

Ага! теперь только одна ошибка:

error: unresolved name `one`

Ну теперь-то понятно. Мы сделаали видимыми функции one() и one2() в модуле num, но чтобы до них добраться нам нужно пройти через модуль num:

num::one()


Значит с помощью use мы сделали их видимыми в модуле, но чтобы мы могли ими пользоваться в main.rs их надо сделать публичными!

В н е use, пути относительны: foo::bar() ссылаться на функцию внутри foo
относительно того, где мы находимся. Если же используется префикс ::, то ::foo::bar()
будет ссылаться на другой foo, абсолютный путь относительно корня контейнера.


Надобно теперь испытать use super::, а потом ::.
Пишем в number/mod.rs вверху:

use super::libfn2;


И ниже добавляем функцию:

pub fn superfn() {
	libfn2();     // Вызов llibfn2().
}


Добавляем в модуль num функцию libfn2():

fn libfn2() {
	println!("super works");
}


Пишем в main.rs:

fn main() {
	num::number::superfn();     // Вызов superfn().
}


И компилируем.Работает. А что, если мы перед use super поставим pub? Пробуем:

Дописываем в number/mod.rs слово pub. Дописываем в модуль num перед функцией libfn2() слово pub( чтобы мы могли ею воспользоваться). Изменяем в main.rs имя функции:

num::number::libfn2();


Компилируем. Работает. Я в восторге!!! Ну, а если мы вместо pub use super::libfn2; напишем:

1) pub use super::libfn;
2) Удалим функцию libfn2(); // Т.к. модуль не видет функцию libfn2() он выдаст ошибку.
3) Изменим в main.rs имя вызываемой функции:

num::number::libfn();


Запускаем. Работает…

Теперь ::

Если подумать, то можно сделать вывод, что

pub use ::libfn;


и

pub use super::libfn;


в данном случае аналогичны, но нам всё же надо проверить. Переписываем, компилируем и запускаем. Мы оказались правы: работает.

Ну, на этом пока всё. До встречи, читатель.

Литература:

The Rust Reference (английский)
The Rust Programming Language (английский)
The Rust Programming Language (русский)

Комментарии (6)


  1. Sergey6661313
    19.01.2016 22:45

    я думал будет что то вроде

    for i in range(0, 10):
    use num::number[i]

    (сорри за питоностиль)


    1. ozkriff
      20.01.2016 10:49

      А можно немного развернуть мысль? А то я как-то вообще в этом `use num::number[i]` смысла не вижу)


      1. Sergey6661313
        20.01.2016 19:19

        ну а я не вижу смысла писать use num::{number, number2, next, ...}; если они отличаются всего лишь одной цифрой в конце должен быть способ организовать простейший цикл с перебором этой цифры.
        А если их будет 300 таких?


        1. withkittens
          20.01.2016 20:58

          А вы можете придумать пример из реальной жизни, где у вас будет 300 таких number1, number2 и т.д.?


          1. Sergey6661313
            21.01.2016 18:37
            -2

            Раз пошла такая бяка предлагаю вам начинать Ваши статьи с описания того где ваш пример может пригодится в реальной жизни.


  1. Gorthauer87
    20.01.2016 11:58

    Помню по началу долго тупил с модулями и тем как правильно use использовать, поэтому статья с примерами очень даже кстати, а то в rustbook таких примеров маловато, приходилось просматривать всякие проекты с crates.io.