ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 11.01.2024
Просмотров: 1149
Скачиваний: 5
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Подключение путей в область видимости с помощью
ключевого слова use
Необходимость записывать пути к функциям вызова может показаться неудобной и повторяющейся. В листинге 7-7 независимо от того, выбирали ли мы абсолютный или относительный путь к функции add_to_waitlist
, каждый раз, когда мы хотели вызвать add_to_waitlist
, нам приходилось также указывать front_of_house и hosting
. К
счастью, есть способ упростить этот процесс: мы можем один раз создать псевдоним на путь при помощи ключевого слова use
, а затем использовать более короткое имя везде в области видимости.
В листинге 7-11 мы подключили модуль crate::front_of_house::hosting в область действия функции eat_at_restaurant
, поэтому нам достаточно только указать hosting::add_to_waitlist для вызова функции add_to_waitlist внутри eat_at_restaurant
Файл: src/lib.rs
Листинг 7-11. Добавление модуля в область видимости при помощи
use
Добавление use и пути в область видимости аналогично созданию символической ссылки в файловой системе. С добавлением use crate::front_of_house::hosting в
корневой модуль крейта, hosting становится допустимым именем в этой области, как если бы модуль hosting был определён в корневом модуле крейта. Пути, подключённые в область видимости с помощью use
, также проверяются на доступность, как и любые другие пути.
Обратите внимание, что use создаёт псевдоним только для той конкретной области, в которой это объявление use и находится. В листинге 7-12 функция eat_at_restaurant перемещается в новый дочерний модуль с именем customer
, область действия которого отличается от области действия оператора use
, поэтому тело функции не будет компилироваться:
Файл: src/lib.rs mod front_of_house { pub mod hosting { pub fn add_to_waitlist
() {}
}
} use crate::front_of_house::hosting; pub fn eat_at_restaurant
() { hosting::add_to_waitlist();
}
Листинг 7-12. Объявление
use
действительно только для той области, в которой оно находится.
Ошибка компилятора показывает, что данный псевдоним не может использоваться в модуле customer
:
Обратите внимание, что есть также предупреждение о том, что use не используется в своей области! Чтобы решить эту проблему, можно переместить use в модуль customer
,
или же можно сослаться на псевдоним в родительском модуле с помощью super::hosting в дочернем модуле customer
Создание идиоматических путей с use
В листинге 7-11 вы могли бы задаться вопросом, почему мы указали use crate::front_of_house::hosting
, а затем вызвали hosting::add_to_waitlist внутри eat_at_restaurant вместо указания в use полного пути прямо до функции add_to_waitlist для получения того же результата, что в листинге 7-13.
mod front_of_house { pub mod hosting { pub fn add_to_waitlist
() {}
}
} use crate::front_of_house::hosting; mod customer { pub fn eat_at_restaurant
() { hosting::add_to_waitlist();
}
}
$
cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant) error[E0433]: failed to resolve: use of undeclared crate or module `hosting`
-->
src/lib.rs:11:9
|
11 | hosting::add_to_waitlist();
| ^^^^^^^ use of undeclared crate or module `hosting` warning: unused import: `crate::front_of_house::hosting`
-->
src/lib.rs:7:5
|
7 | use crate::front_of_house::hosting;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
For more information about this error, try `rustc --explain E0433`. warning: `restaurant` (lib) generated 1 warning error: could not compile `restaurant` due to previous error; 1 warning emitted
Файл: src/lib.rs
1 ... 11 12 13 14 15 16 17 18 ... 62
Листинг 7-13: Добавление функции
add_to_waitlist
в область видимости с
use
неидиоматическим
способом
Хотя листинги 7-11 и 7-13 выполняют одну и ту же задачу, листинг 7-11 является идиоматическим способом подключения функции в область видимости с помощью use
Подключение родительского модуля функции в область видимости при помощи use означает, что мы должны указывать родительский модуль при вызове функции. Указание родительского модуля при вызове функции даёт понять, что функция не определена локально, но в то же время сводя к минимуму повторение полного пути. В коде листинга
7-13 не ясно, где именно определена add_to_waitlist
С другой стороны, при подключении структур, перечислений и других элементов используя use
, идиоматически правильным будет указывать полный путь. Листинг 7-14
показывает идиоматический способ подключения структуры стандартной библиотеки
HashMap в область видимости бинарного крейта.
Файл: src/main.rs
Листинг 7-14. Включение
HashMap
в область видимости идиоматическим способом
За этой идиомой нет веской причины: это просто соглашение, которое появилось само собой. Люди привыкли читать и писать код на Rust таким образом.
Исключением из этой идиомы является случай, когда мы подключаем два элемента с одинаковыми именами в область видимости используя оператор use
- Rust просто не позволяет этого сделать. Листинг 7-15 показывает, как подключить в область действия два типа с одинаковыми именами
Result
, но из разных родительских модулей и как на них ссылаться.
mod front_of_house { pub mod hosting { pub fn add_to_waitlist
() {}
}
} use crate::front_of_house::hosting::add_to_waitlist; pub fn eat_at_restaurant
() { add_to_waitlist();
} use std::collections::HashMap; fn main
() { let mut map = HashMap::new(); map.insert(
1
,
2
);
}
Файл: src/lib.rs
Листинг 7-15. Для включения двух типов с одинаковыми именами в одну область видимости необходимо
использовать их родительские модули.
Как видите, использование имени родительских модулей позволяет различать два типа
Result
. Если бы вместо этого мы указали use std::fmt::Result и use std::io::Result
,
мы бы имели два типа
Result в одной области видимости, и Rust не смог бы понять какой из двух
Result мы имели в виду, когда нашёл бы их употребление в коде.
Предоставление новых имён с помощью ключевого слова as
Есть другое решение проблемы добавления двух типов с одинаковыми именами в одну и ту же область видимости используя use
: после пути можно указать as и новое локальное имя (псевдоним) для типа. Листинг 7-16 показывает как по-другому написать код из листинга 7-15, путём переименования одного из двух типов
Result используя as
Файл: src/lib.rs
Листинг 7-16: Переименование типа, когда он включён в область видимости с помощью ключевого слова
as
Во втором операторе use мы выбрали новое имя
IoResult для типа std::io::Result
,
которое теперь не будет конфликтовать с типом
Result из std::fmt
, который также подключён в область видимости. Листинги 7-15 и 7-16 считаются идиоматичными,
поэтому выбор за вами!
use std::fmt; use std::io; fn function1
() -> fmt::
Result
{
// --snip--
} fn function2
() -> io::
Result
<()> {
// --snip--
} use std::fmt::
Result
; use std::io::
Result as
IoResult; fn function1
() ->
Result
{
// --snip--
} fn function2
() -> IoResult<()> {
// --snip--
}
Реэкспорт имён с pub use
Когда мы подключаем имя в область видимости, используя ключевое слово use
, то имя,
доступное в новой области видимости, является приватным. Чтобы позволить коду,
который вызывает наш код, ссылаться на это имя, как если бы оно было определено в области видимости данного кода, можно объединить pub и use
. Этот метод называется
реэкспортом (re-exporting), потому что мы подключаем элемент в область видимости, но также делаем этот элемент доступным для подключения в других областях видимости.
Листинг 7-17 показывает код из листинга 7-11, где use в корневом модуле заменено на pub use
Файл: src/lib.rs
Листинг 7-17. Предоставление имени для использования любым кодом из новой области при помощи
pub use
До этого изменения внешний код должен был вызывать функцию add_to_waitlist
,
используя путь restaurant::front_of_house::hosting::add_to_waitlist()
. Теперь, когда это объявление pub use повторно экспортировало модуль hosting из корневого модуля, внешний код теперь может использовать вместо него путь restaurant::hosting::add_to_waitlist()
Реэкспорт полезен, когда внутренняя структура вашего кода отличается от того, как программисты, вызывающие ваш код, думают о предметной области. Например, по аналогии с рестораном люди, управляющие им, думают о «передней части дома» и
«задней части дома». Но клиенты, посещающие ресторан, вероятно, не будут думать о частях ресторана в таких терминах. Используя pub use
, мы можем написать наш код с одной структурой, но сделать общедоступной другую структуру. Благодаря этому наша библиотека хорошо организована для программистов, работающих над библиотекой, и для программистов, вызывающих библиотеку. Мы рассмотрим ещё один пример pub use и его влияние на документацию вашего крейта в разделе
«Экспорт удобного общедоступного API с pub use
»
Главы 14.
Использование внешних пакетов
mod front_of_house { pub mod hosting { pub fn add_to_waitlist
() {}
}
} pub use crate::front_of_house::hosting; pub fn eat_at_restaurant
() { hosting::add_to_waitlist();
}
В Главе 2 мы запрограммировали игру угадывания числа, где использовался внешний пакет с именем rand для генерации случайного числа. Чтобы использовать rand в
нашем проекте, мы добавили эту строку в Cargo.toml:
Файл: Cargo.toml
Добавление rand в качестве зависимости в Cargo.toml указывает Cargo загрузить пакет rand и все его зависимости из crates.io и сделать rand доступным для нашего проекта.
Затем, чтобы подключить определения rand в область видимости нашего пакета, мы добавили строку use начинающуюся с названия пакета rand и списка элементов,
которые мы хотим подключить в область видимости. Напомним, что в разделе "Генерация случайного числа"
Главы 2, мы подключили трейт
Rng в область видимости и вызвали функцию rand::thread_rng
:
Члены сообщества Rust сделали много пакетов доступными на ресурсе crates.io
, и добавление любого из них в ваш пакет включает в себя одни и те же шаги: добавить внешние пакеты в файл Cargo.toml вашего пакета, использовать use для подключения элементов внешних пакетов в область видимости.
Обратите внимание, что стандартная библиотека std также является крейтом, внешним по отношению к нашему пакету. Поскольку стандартная библиотека поставляется с языком Rust, нам не нужно изменять Cargo.toml для подключения std
. Но нам нужно ссылаться на неё при помощи use
, чтобы добавить элементы оттуда в область видимости нашего пакета. Например, с
HashMap мы использовали бы эту строку:
Это абсолютный путь, начинающийся с std
, имени крейта стандартной библиотеки.
Использование вложенных путей для уменьшения длинных списков
use
Если мы используем несколько элементов, определённых в одном крейте или в том же модуле, то перечисление каждого элемента в отдельной строке может занимать много вертикального пространства в файле. Например, эти два объявления use используются rand =
"0.8.3"
use rand::Rng; fn main
() { let secret_number = rand::thread_rng().gen_range(
1
..=
100
);
} use std::collections::HashMap;
в программе угадывания числа (листинг 2-4) для подключения элементов из std в
область видимости:
Файл: src/main.rs
Вместо этого, мы можем использовать вложенные пути, чтобы добавить эти элементы в область видимости одной строкой. Мы делаем это, как показано в листинге 7-18,
указывая общую часть пути, за которой следуют два двоеточия, а затем фигурные скобки вокруг списка тех частей продолжения пути, которые отличаются.
Файл: src/main.rs
Листинг 7-18. Указание вложенного пути для добавления нескольких элементов с одинаковым префиксом
в область видимости
В больших программах, подключение множества элементов из одного пакета или модуля с использованием вложенных путей может значительно сократить количество необходимых отдельных операторов use
!
Можно использовать вложенный путь на любом уровне, что полезно при объединении двух операторов use
, которые имеют общую часть пути. Например, в листинге 7-19
показаны два оператора use
: один подключает std::io
, а другой подключает std::io::Write в область видимости.
Файл: src/lib.rs
Листинг 7-19: Два оператора
use
, где один является частью другого
Общей частью этих двух путей является std::io
, и это полный первый путь. Чтобы объединить эти два пути в одном операторе use
, мы можем использовать ключевое слово self во вложенном пути, как показано в листинге 7-20.
Файл: src/lib.rs
Листинг 7-20: Объединение путей из Листинга 7-19 в один оператор
use
// --snip-- use std::cmp::Ordering; use std::io;
// --snip--
// --snip-- use std::{cmp::Ordering, io};
// --snip-- use std::io; use std::io::Write; use std::io::{
self
, Write};
область видимости:
Файл: src/main.rs
Вместо этого, мы можем использовать вложенные пути, чтобы добавить эти элементы в область видимости одной строкой. Мы делаем это, как показано в листинге 7-18,
указывая общую часть пути, за которой следуют два двоеточия, а затем фигурные скобки вокруг списка тех частей продолжения пути, которые отличаются.
Файл: src/main.rs
Листинг 7-18. Указание вложенного пути для добавления нескольких элементов с одинаковым префиксом
в область видимости
В больших программах, подключение множества элементов из одного пакета или модуля с использованием вложенных путей может значительно сократить количество необходимых отдельных операторов use
!
Можно использовать вложенный путь на любом уровне, что полезно при объединении двух операторов use
, которые имеют общую часть пути. Например, в листинге 7-19
показаны два оператора use
: один подключает std::io
, а другой подключает std::io::Write в область видимости.
Файл: src/lib.rs
Листинг 7-19: Два оператора
use
, где один является частью другого
Общей частью этих двух путей является std::io
, и это полный первый путь. Чтобы объединить эти два пути в одном операторе use
, мы можем использовать ключевое слово self во вложенном пути, как показано в листинге 7-20.
Файл: src/lib.rs
Листинг 7-20: Объединение путей из Листинга 7-19 в один оператор
use
// --snip-- use std::cmp::Ordering; use std::io;
// --snip--
// --snip-- use std::{cmp::Ordering, io};
// --snip-- use std::io; use std::io::Write; use std::io::{
self
, Write};
Эта строка подключает std::io и std::io::Write в область видимости.
Оператор * (glob)
Если мы хотим включить в область видимости все общедоступные элементы,
определённые в пути, мы можем указать этот путь, за которым следует оператор
*
:
Этот оператор use подключает все открытые элементы из модуля std::collections в
текущую область видимости. Будьте осторожны при использовании оператора
*
! Он может усложнить понимание, какие имена находятся в области видимости и где были определены имена, используемые в вашей программе.
Оператор
*
часто используется при тестировании для подключения всего что есть в модуле tests
; мы поговорим об этом в разделе "Как писать тесты"
Главы 11. Оператор
*
также иногда используется как часть шаблона автоматического импорта (prelude):
смотрите документацию по стандартной библиотеке для получения дополнительной информации об этом шаблоне.
use std::collections::*;