Главная страница
Навигация по странице:

  • Раскрываем приватные пути с помощью ключевого слова

  • Листинг 7-5. Объявление модуля hosting как pub для его использования из

  • Листинг 7-6: Ошибки компиляции при сборке кода в листинге 7-5

  • Листинг 7-7. Добавление ключевого слова pub к mod hosting и к fn add_to_waitlist позволяет нам вызывать функцию из

  • Лучшие практики для пакетов с бинарным и библиотечным крейтами

  • Начинаем относительный путь с помощью

  • Листинг 7-8: Вызов функции с использованием относительного пути, начинающегося с

  • Делаем общедоступными структуры и перечисления

  • Листинг 7-10. Определяя перечисление общедоступным мы делаем все его варианты общедоступными

  • Подключение путей в область видимости с помощью ключевого слова

  • Листинг 7-11. Добавление модуля в область видимости при помощи

  • Листинг 7-12. Объявление use действительно только для той области, в которой оно находится.

  • Создание идиоматических путей с

  • Язык программирования Rust


    Скачать 7.02 Mb.
    НазваниеЯзык программирования Rust
    Дата12.04.2023
    Размер7.02 Mb.
    Формат файлаpdf
    Имя файлаThe Rust Programming Language_ru.pdf
    ТипУчебник
    #1056301
    страница15 из 62
    1   ...   11   12   13   14   15   16   17   18   ...   62
    Листинг 7-4. Ошибки компиляции при сборке кода из листинга 7-3
    Сообщения об ошибках говорят о том, что модуль hosting является приватным.
    Другими словами, у нас есть правильные пути к модулю hosting и функции add_to_waitlist
    , но Rust не позволяет нам использовать их, потому что у него нет доступа к приватным разделам. В Rust все элементы (функции, методы, структуры,
    перечисления, модули и константы) по умолчанию являются приватными для родительских модулей. Если вы хотите сделать элемент, например функцию или структуру, приватным, вы помещаете его в модуль.
    Элементы в родительском модуле не могут использовать приватные элементы внутри дочерних модулей, но элементы в дочерних модулях могут использовать элементы у своих модулях-предках. Это связано с тем, что дочерние модули оборачивают и скрывают детали своей реализации, но дочерние модули могут видеть контекст, в котором они определены. Продолжая нашу метафору, подумайте о правилах приватности как о задней части ресторана: то, что там происходит, скрыто от клиентов ресторана, но офис-менеджеры могут видеть и делать всё в ресторане, которым они управляют.
    В Rust решили, что система модулей должна функционировать таким образом, чтобы по умолчанию скрывать детали реализации. Таким образом, вы знаете, какие части внутреннего кода вы можете изменять не нарушая работы внешнего кода. Тем не менее,
    $
    cargo build
    Compiling restaurant v0.1.0 (file:///projects/restaurant) error[E0603]: module `hosting` is private
    -->
    src/lib.rs:9:28
    |
    9 | crate::front_of_house::hosting::add_to_waitlist();
    | ^^^^^^^ private module
    | note: the module `hosting` is defined here
    -->
    src/lib.rs:2:5
    |
    2 | mod hosting {
    | ^^^^^^^^^^^ error[E0603]: module `hosting` is private
    -->
    src/lib.rs:12:21
    |
    12 | front_of_house::hosting::add_to_waitlist();
    | ^^^^^^^ private module
    | note: the module `hosting` is defined here
    -->
    src/lib.rs:2:5
    |
    2 | mod hosting {
    | ^^^^^^^^^^^
    For more information about this error, try `rustc --explain E0603`. error: could not compile `restaurant` due to 2 previous errors

    Rust даёт нам возможность открывать внутренние части кода дочерних модулей для внешних модулей-предков, используя ключевое слово pub
    , чтобы сделать элемент общедоступным.
    Раскрываем приватные пути с помощью ключевого слова pub
    Давайте вернёмся к ошибке в листинге 7-4, которая говорит, что модуль hosting является приватным. Мы хотим, чтобы функция eat_at_restaurant из родительского модуля имела доступ к функции add_to_waitlist в дочернем модуле, поэтому мы помечаем модуль hosting ключевым словом pub
    , как показано в листинге 7-5.
    Файл: src/lib.rs
    Листинг 7-5. Объявление модуля
    hosting
    как
    pub
    для его использования из
    eat_at_restaurant
    К сожалению, код в листинге 7-5 всё ещё приводит к ошибке, как показано в листинге 7-6.
    mod front_of_house { pub mod hosting { fn add_to_waitlist
    () {}
    }
    } pub fn eat_at_restaurant
    () {
    // Absolute path crate::front_of_house::hosting::add_to_waitlist();
    // Relative path front_of_house::hosting::add_to_waitlist();
    }

    Листинг 7-6: Ошибки компиляции при сборке кода в листинге 7-5
    Что произошло? Добавление ключевого слова pub перед mod hosting сделало модуль общедоступным. После этого изменения, если мы можем получить доступ к модулю front_of_house
    , то мы можем доступ к модулю hosting
    . Но содержимое модуля hosting всё ещё является приватным: превращение модуля в общедоступным не делает его содержимое общедоступным. Ключевое слово pub позволяет внешнему коду в модулях- предках обращаться только к модулю, без доступа ко внутреннему коду. Поскольку модули являются контейнерами, мы мало что можем сделать, просто сделав модуль общедоступным; нам нужно пойти дальше и сделать один или несколько элементов в модуле общедоступными.
    Ошибки в листинге 7-6 говорят, что функция add_to_waitlist является приватной.
    Правила приватности применяются к структурам, перечислениям, функциям и методам,
    также как и к модулям.
    Давайте также сделаем функцию add_to_waitlist общедоступной, добавив ключевое слово pub перед её определением, как показано в листинге 7-7.
    Файл: src/lib.rs
    $
    cargo build
    Compiling restaurant v0.1.0 (file:///projects/restaurant) error[E0603]: function `add_to_waitlist` is private
    -->
    src/lib.rs:9:37
    |
    9 | crate::front_of_house::hosting::add_to_waitlist();
    | ^^^^^^^^^^^^^^^ private function
    | note: the function `add_to_waitlist` is defined here
    -->
    src/lib.rs:3:9
    |
    3 | fn add_to_waitlist() {}
    | ^^^^^^^^^^^^^^^^^^^^ error[E0603]: function `add_to_waitlist` is private
    -->
    src/lib.rs:12:30
    |
    12 | front_of_house::hosting::add_to_waitlist();
    | ^^^^^^^^^^^^^^^ private function
    | note: the function `add_to_waitlist` is defined here
    -->
    src/lib.rs:3:9
    |
    3 | fn add_to_waitlist() {}
    | ^^^^^^^^^^^^^^^^^^^^
    For more information about this error, try `rustc --explain E0603`. error: could not compile `restaurant` due to 2 previous errors

    Листинг 7-7. Добавление ключевого слова
    pub
    к
    mod hosting
    и к
    fn add_to_waitlist
    позволяет нам
    вызывать функцию из
    eat_at_restaurant
    Теперь код компилируется! Чтобы понять, почему добавление ключевого слова pub позволяет нам использовать эти пути для add_to_waitlist в соответствии с правилами приватности, давайте рассмотрим абсолютный и относительный пути.
    В случае абсолютного пути мы начинаем с crate
    , корня дерева модулей нашего крейта.
    Модуль front_of_house определён в корневом модуле крейта. Хотя front_of_house не является общедоступным, но поскольку функция eat_at_restaurant определена в том же модуле, что и front_of_house
    (то есть, eat_at_restaurant и front_of_house являются потомками одного родителя), мы можем ссылаться на front_of_house из eat_at_restaurant
    . Далее идёт модуль hosting
    , помеченный как pub
    . Мы можем получить доступ к родительскому модулю модуля hosting
    , поэтому мы можем получить доступ и к hosting
    . Наконец, функция add_to_waitlist помечена как pub
    , и так как мы можем получить доступ к её родительскому модулю, то вызов этой функции разрешён!
    В случае относительного пути логика такая же как для абсолютного пути, за исключением первого шага: вместо того, чтобы начинать с корневого модуля крейта, путь начинается с front_of_house
    . Модуль front_of_house определён в том же модуле, что и eat_at_restaurant
    , поэтому относительный путь, начинающийся с модуля, в котором определена eat_at_restaurant тоже работает. Тогда, по причине того, что hosting и add_to_waitlist помечены как pub
    , остальная часть пути работает и вызов этой функции разрешён!
    Если вы планируете предоставить общий доступ к своему библиотечному крейту, чтобы другие проекты могли использовать ваш код, ваш общедоступный API — это ваш контракт с пользователями вашего крейта, определяющий, как они могут взаимодействовать с вашим кодом. Есть много соображений по поводу управления изменениями в вашем общедоступном API, чтобы сделать необременительным для людей зависимость от вашего крейта. Эти соображения выходят за рамки этой книги;
    если вам интересна эта тема, см.
    The Rust API Guidelines mod front_of_house { pub mod hosting { pub fn add_to_waitlist
    () {}
    }
    } pub fn eat_at_restaurant
    () {
    // Absolute path crate::front_of_house::hosting::add_to_waitlist();
    // Relative path front_of_house::hosting::add_to_waitlist();
    }

    Лучшие практики для пакетов с бинарным и библиотечным крейтами
    Мы упоминали, что пакет может содержать как корневой модуль бинарного крейта
    src/main.rs, так и корневой модуль библиотечного крейта src/lib.rs l, и оба крейта по умолчанию будут иметь имя пакета. Как правило, пакеты с таким шаблоном,
    содержащим как библиотечный, так и бинарный крейт, будут иметь достаточно кода в бинарном крейте, чтобы запустить исполняемый файл, который вызывает код из библиотечного крейта. Это позволяет другим проектам извлечь выгоду из большей части функциональности, предоставляемой пакетом, поскольку код библиотечного крейта можно использовать совместно.
    Дерево модулей должно быть определено в src/lib.rs. Затем любые общедоступные элементы можно использовать в бинарном крейте, начав пути с имени пакета.
    Бинарный крейт становится пользователем библиотечного крейта точно так же, как полностью внешний крейт использует библиотечный крейт: он может использовать только общедоступный API. Это поможет вам разработать хороший API; вы не только автор, но и пользователь!
    В Главе 12 мы продемонстрируем эту организационную практику с помощью программы командной строки, которая будет содержать как бинарный, так и библиотечный крейты.
    Начинаем относительный путь с помощью super
    Также можно построить относительные пути, которые начинаются в родительском модуле, используя ключевое слово super в начале пути. Это похоже на синтаксис начала пути файловой системы
    . Использование super позволяет нам сослаться на элемент,
    который, как мы знаем, находится в родительском модуле, что может упростить переупорядочение дерева модулей, чем когда модуль тесно связан с родителем, но родитель может когда-нибудь быть перемещён в другое место в дереве модулей.
    Рассмотрим код в листинге 7-8, где моделируется ситуация, в которой повар исправляет неправильный заказ и лично приносит его клиенту. Функция fix_incorrect_order вызывает функцию deliver_order
    , определённую в родительском модуле, указывая путь к deliver_order
    , начинающийся с super
    :
    Файл: src/lib.rs

    Листинг 7-8: Вызов функции с использованием относительного пути, начинающегося с
    super
    Функция fix_incorrect_order находится в модуле back_of_house
    , поэтому мы можем использовать super для перехода к родительскому модулю модуля back_of_house
    ,
    который в этом случае является crate
    , корневым модулем. В этом модуле мы ищем deliver_order и находим его. Успех! Мы думаем, что модуль back_of_house и функция deliver_order
    , скорее всего, останутся в тех же родственных отношениях друг с другом,
    и должны будут перемещены вместе, если мы решим реорганизовать дерево модулей крейта. Поэтому мы использовали super
    , чтобы в будущем у нас было меньше мест для обновления кода, если этот код будет перемещён в другой модуль.
    Делаем общедоступными структуры и перечисления
    Мы также можем использовать pub для обозначения структур и перечислений как общедоступных, но есть несколько дополнительных деталей использования pub со структурами и перечислениями. Если мы используем pub перед определением структуры, мы делаем структуру общедоступной, но поля структуры по-прежнему остаются приватными. Мы можем сделать каждое поле общедоступным или нет в каждом конкретном случае. В листинге 7-9 мы определили общедоступную структуру back_of_house::Breakfast с общедоступным полем toast и с приватным полем seasonal_fruit
    . Это моделирует случай в ресторане, когда клиент может выбрать тип хлеба, который подаётся с едой, а шеф-повар решает какие фрукты сопровождают еду,
    исходя из того, что сезонно и что есть в наличии. Доступные фрукты быстро меняются,
    поэтому клиенты не могут выбирать фрукты или даже увидеть, какие фрукты они получат.
    Файл: src/lib.rs fn deliver_order
    () {} mod back_of_house { fn fix_incorrect_order
    () { cook_order(); super::deliver_order();
    } fn cook_order
    () {}
    }

    Листинг 7-9: Структура с общедоступными и приватными полями
    Поскольку поле toast в структуре back_of_house::Breakfast является открытым, то в функции eat_at_restaurant можно писать и читать поле toast
    , используя точечную нотацию. Обратите внимание, что мы не можем использовать поле seasonal_fruit в eat_at_restaurant
    , потому что seasonal_fruit является приватным. Попробуйте убрать комментирование с последней строки для значения поля seasonal_fruit
    , чтобы увидеть какую ошибку вы получите!
    Также обратите внимание, что поскольку back_of_house::Breakfast имеет приватное поле, то структура должна предоставить публичную ассоциированную функцию, которая создаёт экземпляр
    Breakfast
    (мы назвали её summer
    ). Если
    Breakfast не имел бы такой функции, мы бы не могли создать экземпляр
    Breakfast внутри eat_at_restaurant
    ,
    потому что мы не смогли бы установить значение приватного поля seasonal_fruit в
    функции eat_at_restaurant
    В отличии от структуры, если мы сделаем общедоступным перечисление, то все его варианты будут общедоступными. Нужно только указать pub перед ключевым словом enum
    , как показано в листинге 7-10.
    Файл: src/lib.rs mod back_of_house { pub struct
    Breakfast
    { pub toast:
    String
    , seasonal_fruit:
    String
    ,
    } impl
    Breakfast { pub fn summer
    (toast: &
    str
    ) -> Breakfast {
    Breakfast { toast:
    String
    ::from(toast), seasonal_fruit:
    String
    ::from(
    "peaches"
    ),
    }
    }
    }
    } pub fn eat_at_restaurant
    () {
    // Order a breakfast in the summer with Rye toast let mut meal = back_of_house::Breakfast::summer(
    "Rye"
    );
    // Change our mind about what bread we'd like meal.toast =
    String
    ::from(
    "Wheat"
    ); println!
    (
    "I'd like {} toast please"
    , meal.toast);
    // The next line won't compile if we uncomment it; we're not allowed
    // to see or modify the seasonal fruit that comes with the meal
    // meal.seasonal_fruit = String::from("blueberries");
    }

    Листинг 7-10. Определяя перечисление общедоступным мы делаем все его варианты общедоступными
    Поскольку мы сделали общедоступным перечисление
    Appetizer
    , то можно использовать варианты
    Soup и
    Salad в функции eat_at_restaurant
    Перечисления не очень полезны, если их варианты не являются общедоступными: было бы досадно каждый раз аннотировать все варианты перечисления как pub
    . По этой причине по умолчанию варианты перечислений являются общедоступными. Структуры часто полезны, если их поля не являются общедоступными, поэтому поля структуры следуют общему правилу, согласно которому, всё по умолчанию является приватным,
    если не указано pub
    Есть ещё одна ситуация с pub
    , которую мы не освещали, и это последняя особенность модульной системы: ключевое слово use
    . Мы сначала опишем use само по себе, а затем покажем как сочетать pub и use вместе.
    mod back_of_house { pub enum
    Appetizer
    {
    Soup,
    Salad,
    }
    } pub fn eat_at_restaurant
    () { let order1 = back_of_house::Appetizer::Soup; let order2 = back_of_house::Appetizer::Salad;
    }

    Подключение путей в область видимости с помощью
    ключевого слова 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


    написать администратору сайта