Эта рукопись собирается без ошибок. Обратите внимание, что приставка `r#` в определении имени способы (функции) указана так же, как она указана в месте её вызова в `main`.
Сырые определители позволяют вам использовать любое слово, которое вы выберете, в качестве определителя, даже если это слово окажется забронированным ключевым словом. Это даёт нам больше свободы в выборе имён определителей, а также позволяет нам встраиваться с приложениеми, написанными на языке, где эти слова не являются ключевыми. Кроме того, необработанные определители позволяют вам использовать библиотеки, написанные в исполнениях Ржавчины, отличной от используемой в вашем дополнение. Например, `try` не является ключевым словом в выпуске 2015года, но является в выпуске 2018года. Если вы зависите от библиотеки, написанной с использованием исполнения 2015 года и имеющей способ (функцию) `try`, вам потребуется использовать правила написания сырого определителя, в данном случае `r#try`, для вызова этой способы (функции) из рукописи исполнения 2018 года. См. [Приложение Д]<!-- ignore --> для получения дополнительных сведений о изданиях Ржавчины.
Сырые определители позволяют вам использовать любое слово, которое вы выберете, в качестве определителя, даже если это слово окажется забронированным ключевым словом. Это даёт нам больше свободы в выборе имён определителей, а также позволяет нам встраиваться с приложениеми, написанными на языке, где эти слова не являются ключевыми. Кроме того, необработанные определители позволяют вам использовать библиотеки, написанные в исполнениях Ржавчины, отличной от используемой в вашем дополнение. Например, `try` не является ключевым словом в выпуске 2015года, но является в выпуске 2018года. Если вы зависите от библиотеки, написанной с использованием исполнения 2015 года и имеющей способ (функцию) `try`, вам потребуется использовать правила написания сырого определителя, в данном случае `r#try`, для вызова этого способа (функции) из рукописи исполнения 2018 года. См. [Приложение Д]<!-- ignore --> для получения дополнительных сведений о изданиях Ржавчины.
Вот небольшая неполадка программирования: напишите способ (функцию), которая принимает строку слов, разделённых пробелами, и возвращает первое слово, которое она находит в этой строке. Если способ (функция) не находит пробела в строке, вся строка должна состоять из одного слова, поэтому должна быть возвращена вся строка.
Давайте рассмотрим, как бы мы написали ярлык этой способы (функции) без использования срезов, чтобы понять неполадку, которую решат срезы:
Давайте рассмотрим, как бы мы написали ярлык этого способа (функции) без использования срезов, чтобы понять неполадку, которую решат срезы:
*Разделы* позволяют упорядочивать рукопись внутри дополнения для удобства читаемости и лёгкого повторного использования. Разделы также позволяют нам управлять *закрытостью*переменных, поскольку рукопись внутри раздела по умолчанию является закрытой. Частные переменные — это внутренние подробности выполнения, недоступные для внешнего использования. Мы можем сделать разделы и их содержимое внутри них общедоступными, что позволит внешним ящикам,приложениям использовать их и быть к ним привязанными.
*Разделы* позволяют упорядочить рукопись внутри ящика для удобства прочтения и облегчения повторного использования. Разделы также позволяют нам управлять *закрытостью*содержимого, поскольку содержимое внутри раздела по умолчанию является закрытым. Частное содержимое — это внутренние звенья раздела (способы-переменные-сущности и т.д.), недоступные для внешнего использования. Мы можем сделать разделы и связанное внутри них содержимое внутри них общедоступно, что позволит внешним ящикам,дополнениям использовать их и быть с ним привязанными.
В качестве примера, давайте создадим библиотечный ящик, предоставляющий управлять рестораном. Мы изложим способы способа (функции), но оставим их тела пустыми, чтобы сосредоточиться на создании и согласовании рукописи, вместо запуска этих способов (функция) для управления рестораном.
В качестве примера, давайте создадим библиотечный ящик, предназначенный для управления рестораном. Мы изложим способы (функции), но оставим их тела пустыми, чтобы сосредоточиться на создании и согласовании рукописи, вместо вызова этих способов (функций) для управления рестораном.
В ресторане принято, что есть 2 части ресторана, которые называются *передом дома*, а другие *задней частью дома*. *Перед дома* это там, где находятся конечные потребители. Здесь размещаются места конечных потребителей, официанты принимают заказы и оплаты, а бармены делают напитки. Задняя часть дома это где шеф-повара и повара работают на кухне, работают посудомоечные машины, а управленцы занимаются решением поступающих вопросов.
В ресторане принято, что есть 2 части ресторана, которые называются *передом дома*, а другие *задней частью дома*. *Перед дома* это там, где находятся конечные потребители. Здесь рассаживаются посетители, официанты принимают заказы и оплаты, а бармены делают напитки. Задняя часть дома это где шеф-повара и повара работают на кухне, работают посудомоечные машины, а управленцы занимаются решением поступающих вопросов.
Чтобы внутренне выстроить ящик подобно тому, как работает настоящий ресторан, необходимо согласовать размещение способов (функций) во вложенных разделах. Создадим новую библиотеку (библиотечный ящик) с именем `restaurant` вызвав приказ `cargo new restaurant --lib`. Затем вставим рукопись из приложения 7-1 в *src/lib.rs* для определения некоторых разделов и изложений функций. Это раздел переда дома:
<spanclass="caption">Приложение 7-1: Раздел <code>front_of_house</code> , содержащий другие разделы, которые в свою очередь содержат способы способа (функции)</span>
<spanclass="caption">Приложение 7-1: Раздел <code>front_of_house</code> , содержащий другие разделы, которые в свою очередь содержат способы (функции)</span>
Мы создаём раздел, начиная с ключевого слова `mod`, затем определяем название раздела (в данном случае `front_of_house`);. После чего тело раздела помещаем в узорчатые скобки. Внутри разделов, можно иметь другие разделы, как в случае с разделами `hosting` и `serving`. Разделы также могут содержать изложения для других составляющих, таких как стопки, перечисления, постоянные переменные, сущности или — как в приложении 7-1 — способы способа (функции).
Мы создаём раздел, начиная с ключевого слова `mod`, затем определяем название раздела (в данном случае `front_of_house`). После чего тело раздела помещаем в узорчатые скобки. Внутри разделов, можно иметь другие разделы, как в случае с разделами `hosting` и `serving`. Разделы также могут содержать изложения для других составляющих, таких как стопки, перечисления, постоянные переменные, сущности или — как в приложении 7-1 — способы (функции).
Используя разделы, мы можем объединять связанное их содержимое вместе и сказать, почему они являются связанными. Разработчикам будет легче найти необходимое содержимое в объединенном ящике с разделами, вместо того чтобы искать её в одном общем списке. Разработчики, добавляющие новые возможности в этот ящик, будут знать, где разместить изложение того или иного составляющего звена для согласования общего устройства ящика.
Как мы упоминали ранее, файлы *src/main.rs* и *src/lib.rs* называются *корневыми разделами ящика*. Причина такого именования в том, что содержимое любого из этих двух файлов образует раздел с именем `crate` в корне дерева разделов дополнения, известным как *дерево разделов*.
Как мы упоминали ранее, файлы *src/main.rs* и *src/lib.rs* называются *корневыми разделами ящика*. Причина такого именования в том, что содержимое любого из этих двух файлов создаёт ящик с именем `crate` в корне дерева разделов ящика, известным как *дерево разделов*.
В приложении 7-2 показано дерево разделов для дерева разделов, приведённого в рукописи приложения 7-1.
В приложении 7-2 показано дерево разделов для ящика из приложения 7-1.
```text
crate
@ -98,6 +98,6 @@ crate
@@ -98,6 +98,6 @@ crate
<spanclass="caption">Приложение 7-2: Древо разделов для приложения из Приложения 7-1</span>
Это дерево показывает, как некоторые из разделов вкладываются друг в друга. Например, `hosting` находится внутри `front_of_house`. Дерево также показывает, что некоторые разделы являются *родственными* (siblings) друг для друга, то есть они определены в одном разделе. Так `hosting` и `serving` это родственными, которые определены внутри `front_of_house`. Если раздел A содержится внутри раздела B, мы говорим, что раздел A является *порождённым* (child) от раздела B, а раздел B является *родителем* (parent) раздела A. Обратите внимание, что родителем всего дерева разделов является неявный раздел с именем `crate`.
Это дерево показывает, как некоторые из разделов вкладываются друг в друга. Например, `hosting` находится внутри `front_of_house`. Дерево также показывает, что некоторые разделы являются *родственными* (siblings) друг для друга, то есть они определены в одном разделе. Так `hosting` и `serving` это родственные разделы, которые определены внутри `front_of_house`. Если раздел A содержится внутри раздела B, мы говорим, что раздел A является *порождённым* (child) от раздела B, а раздел B является *родителем* (parent) раздела A. Обратите внимание, что родителем всего дерева разделов является неявный раздел с именем `crate`.
Дерево разделов может напомнить вам дерево папок файловой системы на компьютере, это очень удачное сравнение! По подобию с папками в файловой системе, мы используем разделы для согласования ящиков. И так же, как нам надо искать файлы в папках на компьютере, нам требуется способ поиска нужных разделов.
## Пути для ссылки на переменную в дереве разделов
Чтобы указать Ржавчине, где найти то или иное звено (способ/переменная/стопка/сущность и т.д.) в дереве разделов, мы используем путь так же, как мы используем путь при поиске по файловой системе. Чтобы вызвать способ (способ (функцию)), нам нужно знать её путь.
Чтобы указать Ржавчине, где найти то или иное звено (способ/переменная/стопка/сущность и т.д.) в дереве разделов, мы используем `путь` так же, как мы используем `путь` при поиске по файловой системе. Чтобы вызвать способ(функцию)), нам нужно знать её `путь`.
Пути бывают двух видов:
- *безусловный путь* - это полный путь, начинающийся от корневого раздела дополнения. Для содержимого из внешнего дополнения безусловный путь начинается с имени дополнения, а для содержимого из текущего ящика он начинается с ключевого слова `crate`.
- *относительный путь* начинается с текущего раздела и использует ключевые слова `self`, `super` или определитель в текущем разделе.
- *безусловный путь* - это `полный путь`, начинающийся от корневого раздела ящика. Для содержимого из внешнего дополнения `безусловный путь` начинается с имени ящика, а для содержимого из текущего ящика он начинается с ключевого слова `crate`.
- *Относительный путь* - он начинается с текущего раздела и использует ключевые слова `self`, `super` или ссылку на текущий раздел.
Как безусловные, так и относительные, пути состоят из одного или нескольких определителей, разделённых двойными двоеточиями (`::`).
Как *безусловные*, так и *относительные*, `пути` состоят из одного или нескольких двойных разделителей в виде (`::`).
Вернёмся к приложению 7-1, скажем, мы хотим вызвать способ (функцию) `add_to_waitlist`. Это то же самое, что спросить: какой путь у способа (функции) `add_to_waitlist` ? В приложении 7-3 мы сократили содержимое приложения 7-1, удалив некоторые разделы и способы способа (функции).
Вернёмся к приложению 7-1, скажем, мы хотим вызвать способ (функцию) `add_to_waitlist`. Это то же самое, что спросить: какой *путь* до способа (функции) `add_to_waitlist` ? В приложении 7-3 мы сократили содержимое приложения 7-1, удалив некоторые разделы и способы (функции).
Мы покажем два способа вызова способа (функции) `add_to_waitlist` из нового способа (функции) `eat_at_restaurant`, определённом в корневом разделе ящика. Эти пути правильные, но остаётся ещё одна неполадка, которая не позволит этому примеру собраться как есть. Мы скоро объясним почему.
Мы покажем два исхода вызова способа (функции) `add_to_waitlist`внутри способа `eat_at_restaurant`, определённом в корневом разделе ящика. Эти `пути` правильные, но остаётся ещё одна неполадка, которая не позволит этому примеру собрать в текущем изложении. Мы скоро объясним почему.
Способ (функция) `eat_at_restaurant` является частью общедоступного API нашего библиотечного ящика, поэтому мы помечаем её ключевым словом `pub`. В разделе <adata-md-type="raw_html"href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword">"Раскрываем закрытые пути с помощью ключевого слова `pub`"</a><!-- ignore --> мы рассмотрим более подробно само ключевое слово`pub`.
Способ (функция) `eat_at_restaurant` является частью общедоступного API нашего библиотечного ящика, поэтому мы помечаем её ключевым словом `pub`. В разделе <adata-md-type="raw_html"href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword">"Раскрываем закрытые `пути` с помощью ключевого слова `pub`"</a><!-- ignore --> мы рассмотрим более подробно само ключевое слово`pub`.
<spanclass="caption">Приложение 7-3: Вызов способа (функции) <code>add_to_waitlist</code> с использованием безусловного и относительного пути</span>
<spanclass="caption">Приложение 7-3: Вызов способа (функции) <code>add_to_waitlist</code> с использованием `безусловного` и `относительного пути`</span>
При первом вызове способа (функции) `add_to_waitlist` из `eat_at_restaurant` мы используем безусловный путь. Способ (функция) `add_to_waitlist` определена в том же ящике, что и `eat_at_restaurant`, и это означает, что мы можем использовать ключевое слово `crate` в начале безусловного пути. Затем, мы добавляем каждый из последующих порождённых разделов, пока не составим путь до `add_to_waitlist`. Вы можете представить себе файловую устройство с таким же устройством: мы указываем путь`/front_of_house/hosting/add_to_waitlist` для запуска приложения `add_to_waitlist`. Использование ключевого слова `crate` в качестве корневого раздела ящика подобно использованию `/` для указания корня файловой системы в вашей оболочке.
При первом вызове способа (функции) `add_to_waitlist` из `eat_at_restaurant` мы используем `безусловный путь`. Способ (функция) `add_to_waitlist` определена в том же разделе, что и `eat_at_restaurant`, и это означает, что мы можем использовать ключевое слово `crate` в начале *безусловного пути*. Затем, мы добавляем каждый из последующих порождённых разделов, пока не составим `путь` до `add_to_waitlist`. Вы можете представить себе устройство папок на вашей ОС, которые так же устроены: мы указываем *путь*`/front_of_house/hosting/add_to_waitlist` для запуска приложения `add_to_waitlist`. Использование ключевого слова `crate` в качестве корневого раздела ящика подобно использованию `/` для указания корня устройства папок в вашей оболочке.
Второй раз, когда мы вызываем `add_to_waitlist` из `eat_at_restaurant`, мы используем относительный путь. Путь начинается с имени раздела `front_of_house`, определённого на том же уровне дерева разделов, что и `eat_at_restaurant`. В соответствии с файловой системой, использовался бы путь`front_of_house/hosting/add_to_waitlist`. Начало пути с имени раздела означает, что путь является относительным.
Второй раз, когда мы вызываем `add_to_waitlist` из `eat_at_restaurant`, мы используем *относительный путь*. `Путь` начинается с имени главного раздела `front_of_house`, определённого на том же этаже дерева разделов, что и `eat_at_restaurant`. В соответствии с файловой системой, *путь* соответствует`front_of_house/hosting/add_to_waitlist`. Начало пути с имени раздела означает, что `путь` является относительным.
Выбор, использовать относительный или безусловный путь, является решением, которое вы примете на основании вашего ящика или дополнения. Решение должно зависеть от того, с какой вероятностью вы переместите объявление переменной или способа отдельно от или вместе с рукописью, использующей эту переменную или способ. Например, в случае перемещения раздела `front_of_house` и его способ (способ (функцию)) `eat_at_restaurant` в другой раздел с именем `customer_experience`, необходимо будет обновить безусловный путь до `add_to_waitlist`, но относительный путь всё равно будет действителен. Однако, если мы переместим отдельно способ (функцию) `eat_at_restaurant` в раздел с именем `dining`, то безусловный путь вызова `add_to_waitlist` останется прежним, но относительный путь нужно будет обновить. Мы предпочитаем указывать безусловные пути, потому что это позволяет безошибочно перемещать содержимое разделов и ящиков, в том числе обращения к звеньям содержимого разделов независимо друг от друга.
Выбор, использовать `относительный` или `безусловный путь`, является решением, которое вы примете на основании вашего ящика или дополнения. Решение должно зависеть от того, с какой вероятностью вы переместите объявление внутреннего содержимого раздела отдельно от или вместе с разделом, использующей это звено раздела. Например, в случае перемещения раздела `front_of_house` и его способ(функцию) `eat_at_restaurant` в другой раздел с именем `customer_experience`, необходимо будет изменить `безусловный путь` до `add_to_waitlist`, но `относительный путь` всё равно будет действителен. Однако, если мы переместим способ (функцию) `eat_at_restaurant`отдельно в раздел с именем `dining`, то `безусловный путь` вызова `add_to_waitlist` останется прежним, но `относительный путь` нужно будет изменить. Мы предпочитаем указывать безусловные пути, потому что это позволяет безошибочно перемещать содержимое разделов и ящиков, в том числе обращения к звеньям содержимого разделов независимо друг от друга.
Давайте попробуем собрать ящик из приложения 7-3 и выяснить, почему он собирается с ошибкой. Ошибка, которую мы получаем, показана в приложении 7-4.
Давайте попробуем собрать ящик из приложения 7-3 и выяснить, почему он собирается с ошибкой. Ошибка, которую мы получаем, указана в приложении 7-4.
<spanclass="caption">Приложение 7-4: Ошибки сборки при сборке рукописи из приложения 7-3</span>
Сообщения об ошибках говорят о том, что раздел `hosting` является закрытым. Другими словами, у нас указаны правильные пути к разделу `hosting` и способы (функции) `add_to_waitlist`, но Ржавчина не позволяет нам использовать их, потому что у него нет доступа к закрытым разделам. В Ржавчине все переменные (способы (функции), способы, стопки, перечисления, разделы и постоянные переменные) по умолчанию являются закрытыми для родительских разделов. Если вы хотите сделать способ (функцию) или вид данных, закрытым, вы помещаете их в раздел.
Сообщения об ошибках говорят о том, что раздел `hosting` является закрытым. Другими словами, у нас указаны правильные `пути` к разделу `hosting` и способы (функции) `add_to_waitlist`, но Ржавчина не позволяет нам использовать их, потому что у него нет доступа к закрытым разделам. В Ржавчине все переменные (способы (функции), способы, стопки, перечисления, разделы и постоянные переменные) по умолчанию являются закрытыми для родительских разделов. Если вы хотите сделать способ (функцию) или вид данных, закрытым, вы помещаете их в раздел.
Переменные в родительском разделе не могут использовать закрытые переменные внутри дочерних разделов, но переменные в дочерних разделах могут использовать переменные у своих разделов-предков. Это связано с тем, что дочерние разделы оборачивают и скрывают подробности своего выполнения, но дочерние разделы могут видеть среду, в которой они определены. Продолжая наше сравнение, подумайте о правилах закрытости как о задней части ресторана: то, что там происходит, скрыто от конечных потребителей ресторана, но офис-управленцы могут видеть и делать всё в ресторане, которым они управляют.
@ -77,27 +77,27 @@
@@ -77,27 +77,27 @@
<spanclass="caption">Приложение 7-7: Добавление ключевого слова <code>pub</code> к <code>mod hosting</code> и к <code>fn add_to_waitlist</code> позволяет нам вызывать способ (функцию) из <code>eat_at_restaurant</code></span>
Теперь рукопись собирается! Чтобы понять, почему добавление ключевого слова `pub` позволяет нам использовать эти пути для `add_to_waitlist` в соответствии с правилами закрытости, давайте рассмотрим безусловный и относительный пути.
Теперь рукопись собирается! Чтобы понять, почему добавление ключевого слова `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`, и так как мы можем получить доступ к её родительскому разделу, то вызов этой способы (функции) разрешён!
В случае `безусловного пути` мы начинаем с `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`, остальная часть пути работает и вызов этой способы (функции) разрешён!
В случае `относительного пути` ход мыслей такой же как для `безусловного пути`, за исключением первого шага: вместо того, чтобы начинать с корневого раздела дополнения, `путь` начинается с `front_of_house`. Раздел `front_of_house` определён в том же разделе, что и `eat_at_restaurant`, поэтому `относительный путь`, начинающийся с раздела, в котором определена `eat_at_restaurant` тоже работает. Тогда, по причине того, что `hosting` и `add_to_waitlist` помечены как `pub`, остальная часть `пути` работает и вызов этого способа (функции) разрешён!
Если вы собираетесь предоставить общий доступ к своему библиотечному дополнениеу, чтобы другие приложения могли использовать вашу рукопись, ваш общедоступный API — это ваш договор с пользователями вашего дополнения, определяющий, как они могут взаимодействовать с вашей рукописью. Есть много соображений по поводу управления изменениями в вашем общедоступном API, чтобы сделать необременительным для людей дополнение от вашего дополнения. Эти соображения выходят за рамки этой книги; если вам важен этот вопрос, см. [The Ржавчина API Guidelines].
Если вы собираетесь предоставить общий доступ к своему библиотечному дополнению, чтобы другие приложения могли использовать вашу рукопись, ваш общедоступный API — это ваш договор с пользователями вашего дополнения, определяющий, как они могут взаимодействовать с вашей рукописью. Есть много соображений по поводу управления изменениями в вашем общедоступном API, чтобы сделать необременительным для людей дополнение от вашего дополнения. Эти соображения выходят за рамки этой книги; если вам важен этот вопрос, см. [The Ржавчина API Guidelines].
> #### Лучшие опыты для дополнений с исполняемым и библиотечным дополнениями
>
> Мы упоминали, что дополнение может содержать как корневой раздел исполняемого дополнения *src/main.rs*, так и корневой раздел библиотечного дополнения *src/lib.rs*, и оба дополнения будут по умолчанию иметь имя дополнения. Как правило, дополнения с таким образцом, содержащим как библиотечный, так и исполняемый дополнение, будут иметь достаточно рукописи в исполняемом дополнение, чтобы запустить исполняемый файл, который вызывает рукопись из библиотечного дополнения. Это позволяет другим делам извлечь выгоду из большей части возможности, предоставляемой дополнением, поскольку рукопись библиотечного дополнения можно использовать совместно.
>
> Дерево разделов должно быть определено в *src/lib.rs*. Затем любые общедоступные переменные можно использовать в исполняемом дополнение, начав пути с имени дополнения. Исполняемый дополнение становится пользователем библиотечного дополнения точно так же, как полностью внешний дополнение использует библиотечное дополнение: он может использовать только общедоступный API. Это поможет вам разработать хороший API; вы не только составитель, но и пользователь!
> Дерево разделов должно быть определено в *src/lib.rs*. Затем любые общедоступные переменные можно использовать в исполняемом дополнение, начав `пути` с имени дополнения. Исполняемый дополнение становится пользователем библиотечного дополнения точно так же, как полностью внешний дополнение использует библиотечное дополнение: он может использовать только общедоступный API. Это поможет вам разработать хороший API; вы не только составитель, но и пользователь!
>
> В [Главе 12]<!-- ignore --> мы использовали данный опыт согласования рукописи с помощью приложения с окном вывода, которое будет содержать как исполняемый, так и библиотечный дополнения.
### Начинаем относительный путь с помощью `super`
Также можно построить относительные пути, которые начинаются в родительском разделе, используя ключевое слово `super` в начале пути. Это похоже на правила написания начала пути файловой системы `..`. Использование `super` позволяет нам сослаться на переменную, который, как мы знаем, находится в родительском разделе, что может упростить переупорядочение дерева разделов, чем когда раздел тесно связан с родителем, но родитель может когда-нибудь быть перемещён в другое место в дереве разделов.
Также можно построить `относительные пути`, которые начинаются в родительском разделе, используя ключевое слово `super` в начале `пути`. Это похоже на правила написания начала `пути` файловой системы `..`. Использование `super` позволяет нам сослаться на переменную, который, как мы знаем, находится в родительском разделе, что может упростить переупорядочение дерева разделов, чем когда раздел тесно связан с родителем, но родитель может когда-нибудь быть перемещён в другое место в дереве разделов.
Рассмотрим рукопись в приложении 7-8, где рассчитывается случай, в котором повар исправляет неправильный заказ и лично приносит его потребителю. Способ (функция) `fix_incorrect_order` вызывает способ (функцию) `deliver_order`, определённую в родительском разделе, указывая путь к `deliver_order`, начинающийся с `super`:
Рассмотрим рукопись в приложении 7-8, где рассчитывается случай, в котором повар исправляет неправильный заказ и лично приносит его потребителю. Способ (функция) `fix_incorrect_order` вызывает способ (функцию) `deliver_order`, определённую в родительском разделе, указывая *путь* к `deliver_order`, начинающийся с `super`:
<spanclass="caption">Приложение 7-8: Вызов способа (функции) с использованием относительного пути, начинающегося с <code>super</code></span>
<spanclass="caption">Приложение 7-8: Вызов способа (функции) с использованием `относительного пути`, начинающегося с <code>super</code></span>
Способ (функция) `fix_incorrect_order` находится в разделе `back_of_house`, поэтому мы можем использовать `super` для перехода к родительскому разделу раздела `back_of_house`, который в этом случае является `crate`, корневым разделом. В этом разделе мы ищем `deliver_order` и находим его. Успех! Мы думаем, что раздел `back_of_house` и способ (функция) `deliver_order`, скорее всего, останутся в тех же родственных отношениях друг с другом, и должны будут перемещены вместе, если мы решим повторно согласовать дерево разделов дополнения. Поэтому мы использовали `super`, чтобы в будущем у нас было меньше мест для обновления рукописи, если эта рукопись будет перемещена в другой раздел.
<spanclass="caption">Приложение 7-15: Для включения двух способов с одинаковыми именами в одну область видимости необходимо использовать их родительские разделы.</span>
Как видите, использование имени родительских разделов позволяет различать два способа `Result`. Если бы вместо этого мы указали `use std::fmt::Result` и `use std::io::Result`, мы бы имели два способа `Result` в одной области видимости, и Ржавчина не смогла бы понять какой из двух способов `Result` мы имели в виду, когда нашла бы их употребление в рукописи.
Как видите, использование имени родительских разделов позволяет различать два исхода `Result`. Если бы вместо этого мы указали `use std::fmt::Result` и `use std::io::Result`, мы бы имели два способа `Result` в одной области видимости, и Ржавчина не смогла бы понять какой из двух способов `Result` мы имели в виду, когда нашла бы их употребление в рукописи.
### Предоставление новых имён с помощью ключевого слова `as`
Эти выражения создают строку с содержимым `initial contents`.
Мы также можем использовать способ (способ (функцию)) `String::from` для создания вида данных `String` из строковой записи (строкового среза). Рукопись приложения 8-13 является равнозначной рукописи из приложения 8-12, которая использует способ `to_string`:
Мы также можем использовать способ(функцию)) `String::from` для создания вида данных `String` из строковой записи (строкового среза). Рукопись приложения 8-13 является равнозначной рукописи из приложения 8-12, которая использует способ `to_string`:
Когда вы пишете способ (функцию), выполнение которой вызывает что-то, что может завершиться ошибкой, вместо обработки ошибки в этой способы (функции), вы можете вернуть ошибку в вызывающую рукопись, чтобы она могла решить, что с ней делать. Такой приём известен как *распространение ошибки* (*propagating the error*). Благодаря нему мы даём больше управления вызывающей рукописи, где может быть больше сведений или хода мыслей, которые диктуют, как ошибка должна обрабатываться, чем то, что доступно вам в среде вашей рукописи.
Когда вы пишете способ (функцию), выполнение которой вызывает что-то, что может завершиться ошибкой, вместо обработки ошибки в этого способа (функции), вы можете вернуть ошибку в вызывающую рукопись, чтобы она могла решить, что с ней делать. Такой приём известен как *распространение ошибки* (*propagating the error*). Благодаря нему мы даём больше управления вызывающей рукописи, где может быть больше сведений или хода мыслей, которые диктуют, как ошибка должна обрабатываться, чем то, что доступно вам в среде вашей рукописи.
Например, рукопись приложения 9-6 считывает имя пользователя из файла. Если файл не существует или не может быть прочтён, то способ (функция) возвращает ошибку в рукопись, которая вызвала данную способ (функцию).
@ -163,9 +163,9 @@ don't want to include it for rustdoc testing purposes. -->
@@ -163,9 +163,9 @@ don't want to include it for rustdoc testing purposes. -->
Эта способ (функция) может быть написана гораздо более коротким способом, но мы начнём с того, что многое сделаем вручную, чтобы изучить обработку ошибок; а в конце покажем более короткий способ. Давайте сначала рассмотрим вид данных возвращаемого значения: `Result<String, io::Error>`. Здесь есть возвращаемое значение способы (функции) вида данных `Result<T, E>`, где образец свойства `T` был заполнен определенным видом данных `String` и образец свойства `E` был заполнен определенным видом `io::Error`.
Если эта способ (функция) выполнится без неполадок, то рукопись, вызывающая эту способ (функцию), получит значение `Ok`, содержащее `String` - имя пользователя, которое эта способ (функция) прочитала из файла. Если способ (функция) столкнётся с какими-либо неполадками, вызывающая рукопись получит значение `Err`, содержащее образец данных `io::Error`, который включает дополнительные сведения о том, какие возникли ошибки. Мы выбрали `io::Error` в качестве возвращаемого вида данных этой способы (функции), потому что это вид значения ошибки, возвращаемого из обоих действий, которые мы вызываем в теле этой способы (функции) и которые могут завершиться неудачей: способ (функция) `File::open` и способ `read_to_string`.
Если эта способ (функция) выполнится без неполадок, то рукопись, вызывающая эту способ (функцию), получит значение `Ok`, содержащее `String` - имя пользователя, которое эта способ (функция) прочитала из файла. Если способ (функция) столкнётся с какими-либо неполадками, вызывающая рукопись получит значение `Err`, содержащее образец данных `io::Error`, который включает дополнительные сведения о том, какие возникли ошибки. Мы выбрали `io::Error` в качестве возвращаемого вида данных этого способа (функции), потому что это вид значения ошибки, возвращаемого из обоих действий, которые мы вызываем в теле этого способа (функции) и которые могут завершиться неудачей: способ (функция) `File::open` и способ `read_to_string`.
Тело способы (функции) начинается с вызова `File::open`. Затем мы обрабатываем значение `Result` с помощью `match`, подобно `match` из приложения 9-4. Если `File::open` завершается успешно, то указатель файла в переменной образца данных `file` становится значением в изменяемой переменной `имя_пользователя_file` и способ (функция) продолжит свою работу. В случае `Err`, вместо вызова `panic!`, мы используем ключевое слово `return` для досрочного возврата из способы (функции) и передаём значение ошибки из `File::open`, которое теперь находится в переменной образца данных `e`, обратно в вызывающую рукопись как значение ошибки этой способы (функции).
Тело способы (функции) начинается с вызова `File::open`. Затем мы обрабатываем значение `Result` с помощью `match`, подобно `match` из приложения 9-4. Если `File::open` завершается успешно, то указатель файла в переменной образца данных `file` становится значением в изменяемой переменной `имя_пользователя_file` и способ (функция) продолжит свою работу. В случае `Err`, вместо вызова `panic!`, мы используем ключевое слово `return` для досрочного возврата из способы (функции) и передаём значение ошибки из `File::open`, которое теперь находится в переменной образца данных `e`, обратно в вызывающую рукопись как значение ошибки этого способа (функции).
Таким образом, если у нас есть файловый указатель в `имя_пользователя_file`, способ (функция) создаёт новую `String` в переменной `имя_пользователя` и вызывает способ `read_to_string` для файлового указателя в `имя_пользователя_file`, чтобы прочитать содержимое файла в `имя_пользователя`. Способ `read_to_string` также возвращает `Result`, потому что он может потерпеть неудачу, даже если `File::open` завершился успешно. Поэтому нам нужен ещё один `match` для обработки этого `Result`: если `read_to_string` завершится успешно, то наша способ (функция) сработала, и мы возвращаем имя пользователя из файла, которое теперь находится в `имя_пользователя`, обёрнутое в `Ok`. Если `read_to_string` потерпит неудачу, мы возвращаем значение ошибки таким же образом, как мы возвращали значение ошибки в `match`, который обрабатывал возвращаемое значение `File::open`. Однако нам не нужно явно указывать `return`, потому что это последнее выражение в способы (функции).
@ -197,7 +197,7 @@ don't want to include it for rustdoc testing purposes. -->
@@ -197,7 +197,7 @@ don't want to include it for rustdoc testing purposes. -->
В случае приложения 9-7 приказчик `?` в конце вызова `File::open` вернёт значение внутри `Ok` в переменную `имя_пользователя_file`. Если произойдёт ошибка, приказчик `?` выполнит ранний возврат значения `Err` вызывающей рукописи. То же самое относится к приказчику `?` в конце вызова `read_to_string`.
Приказчик `?` позволяет избавиться от большого количества образцов и упростить выполнение этой способы (функции). Мы могли бы даже ещё больше сократить эту рукопись, если бы использовали цепочку вызовов способов сразу после `?`, как показано в приложении 9-8.
Приказчик `?` позволяет избавиться от большого количества образцов и упростить выполнение этого способа (функции). Мы могли бы даже ещё больше сократить эту рукопись, если бы использовали цепочку вызовов способов сразу после `?`, как показано в приложении 9-8.
<spanclass="filename">Файл: src/main.rs</span>
@ -277,7 +277,7 @@ don't want to include it for rustdoc testing purposes. -->
@@ -277,7 +277,7 @@ don't want to include it for rustdoc testing purposes. -->
<spanclass="caption">Приложение 9-12: Замена <code>main</code> на return <code>Result<(), E></code> позволяет использовать приказчик <code>?</code> приказчик над значениями <code>Result</code></span>
Вид данных `Box<dyn Error>` является *сущность-предметом*, о котором мы поговорим в разделе ["Использование сущность-предметов, допускающих значения разных видов данных"][Сущность-предмет]<!-- ignore --> в главе 17. Пока что вы можете считать, что `Box<dyn Error>` означает "любой вид ошибки". Использование `?` для значения `Result` в способы (функции) `main` с видом ошибки `Box<dyn Error>` разрешено, так как позволяет вернуть любое значение `Err` раньше времени. Даже если тело этой способы (функции) `main` будет возвращать только ошибки вида данных `std::io::Error`, указав `Box<dyn Error>`, эта ярлык останется правильным, даже если в тело `main` будет добавлена рукопись, возвращающая другие ошибки.
Вид данных `Box<dyn Error>` является *сущность-предметом*, о котором мы поговорим в разделе ["Использование сущность-предметов, допускающих значения разных видов данных"][Сущность-предмет]<!-- ignore --> в главе 17. Пока что вы можете считать, что `Box<dyn Error>` означает "любой вид ошибки". Использование `?` для значения `Result` в способы (функции) `main` с видом ошибки `Box<dyn Error>` разрешено, так как позволяет вернуть любое значение `Err` раньше времени. Даже если тело этого способа (функции) `main` будет возвращать только ошибки вида данных `std::io::Error`, указав `Box<dyn Error>`, эта ярлык останется правильным, даже если в тело `main` будет добавлена рукопись, возвращающая другие ошибки.
Когда `main` способ (функция) возвращает `Result<(), E>`, исполняемый файл завершится со значением `0`, если `main` вернёт `Ok(())`, и выйдет с ненулевым значением, если `main` вернёт значение `Err`. Исполняемые файлы, написанные на C, при выходе возвращают целые числа: успешно завершённые приложения возвращают целое Число `0`, а приложения с ошибкой возвращают целое число, отличное от `0`. Ржавчина также возвращает целые числа из исполняемых файлов, чтобы быть совместимым с этим соглашением.
Описание этой способы (функции) менее краткое: название способы (функции), список переменных, и возвращаемый вид данных находятся рядом, а описание не содержит в себе множество привязок к сущности.
Описание этого способа (функции) менее краткое: название способы (функции), список переменных, и возвращаемый вид данных находятся рядом, а описание не содержит в себе множество привязок к сущности.
### Возврат значений вида данных, использующих определённые сущности
Третье правило о том, что если есть множество входных свойств времени жизни, но один из них является ссылкой `&self` или `&mut self`, так как эта способ (функция) является способом, то время жизни `self` назначается временем жизни всем выходным свойствам. Это третье правило делает способы намного приятнее для чтения и записи, потому что требуется меньше знаков.
Представим, что мы сборщик и применим эти правила, чтобы вывести времена жизни ссылок в ярлыке способы (функции) `первое_слово` приложения 10-25. Ярлык этой способы (функции) начинается без объявления времён жизни ссылок:
Представим, что мы сборщик и применим эти правила, чтобы вывести времена жизни ссылок в ярлыке способы (функции) `первое_слово` приложения 10-25. Ярлык этого способа (функции) начинается без объявления времён жизни ссылок:
Теперь все ссылки в этой способы (функции) имеют свойства времени жизни и сборщик может продолжить свой оценка без необходимости просить у программиста указать изложении времён жизни в ярлыке этой способы (функции).
Теперь все ссылки в этого способа (функции) имеют свойства времени жизни и сборщик может продолжить свой оценка без необходимости просить у программиста указать изложении времён жизни в ярлыке этого способа (функции).
Давайте рассмотрим ещё один пример: на этот раз способ (функцию) `наибольшее`, в которой не было свойств времени жизни, когда мы начали с ней работать в приложении 10-20:
Соблюдение правил приложения считается то, в какой степени наша рукопись выполняет именно то, что мы задумывали. Ржавчина разработана с учётом большой озабоченности соблюдением правил программ, но соблюдение правил сложна и нелегко доказуема. Система определения Ржавчины берёт на себя огромную часть этого бремени, но она не может уловить безусловно все сбои. Поэтому в Ржавчине предусмотрена возможность написания самопроверок.
Допустим, мы пишем способ (функцию) `add_two`, которая прибавляет 2 к любому переданному ей числу. Ярлык этой способы (функции) принимает целое Число в качестве свойства и возвращает целое Число в качестве итога. Когда мы используем и собираем эту способ (функцию), Ржавчина выполняет всю проверку видов данных и проверку заимствований, которую вы уже изучили, чтобы убедиться, что, например, мы не передаём значение `String` или недопустимую ссылку в эту способ (функцию). Но Ржавчина *не способен* проверить, что эта способ (функция) сделает именно то, что мы задумали, то есть вернёт свойство + 2, а не, скажем, свойство + 10 или свойство - 50! Вот тут-то и приходят на помощь проверки.
Допустим, мы пишем способ (функцию) `add_two`, которая прибавляет 2 к любому переданному ей числу. Ярлык этого способа (функции) принимает целое Число в качестве свойства и возвращает целое Число в качестве итога. Когда мы используем и собираем эту способ (функцию), Ржавчина выполняет всю проверку видов данных и проверку заимствований, которую вы уже изучили, чтобы убедиться, что, например, мы не передаём значение `String` или недопустимую ссылку в эту способ (функцию). Но Ржавчина *не способен* проверить, что эта способ (функция) сделает именно то, что мы задумали, то есть вернёт свойство + 2, а не, скажем, свойство + 10 или свойство - 50! Вот тут-то и приходят на помощь проверки.
Мы можем написать проверки, которые утверждают, например, что когда мы передаём `3` в способ (функцию) `add_two`, возвращаемое значение будет `5`. Мы можем запускать эти проверки всякий раз, когда мы вносим изменения в нашу рукопись, чтобы убедиться, что любое существующее правильное поведение не изменилось.
Допустим, у вас есть дополнение, который вы хотите обнародовать. Перед обнародованием вам нужно добавить некоторые метаданные в раздел `[package]` файла *Cargo.toml* дополнения.
Вашему дополнениеу понадобится не повторяющееся имя. Пока вы работаете над дополнением своё, вы можете назвать его как угодно. Однако названия дополнений на [crates.io](https://crates.io/)<!-- ignore --> определятся в мгновение первой обнародования. Как только дополнениеу присвоено название, никто другой не сможет обнародовать дополнение с таким же именем. Перед тем как обнародовать дополнение, поищите название, которое вы хотите использовать. Если такое имя уже используется, вам придётся подобрать другое и изменить поле `name` в файле *Cargo.toml* в разделе `[package]`, чтобы использовать новое имя в качестве размещаяемого, например, так:
Вашему дополнению понадобится не повторяющееся имя. Пока вы работаете над дополнением своё, вы можете назвать его как угодно. Однако названия дополнений на [crates.io](https://crates.io/)<!-- ignore --> определятся в мгновение первой обнародования. Как только дополнению присвоено название, никто другой не сможет обнародовать дополнение с таким же именем. Перед тем как обнародовать дополнение, поищите название, которое вы хотите использовать. Если такое имя уже используется, вам придётся подобрать другое и изменить поле `name` в файле *Cargo.toml* в разделе `[package]`, чтобы использовать новое имя в качестве размещаяемого, например, так:
@ -47,7 +47,7 @@ $ cargo new adder
@@ -47,7 +47,7 @@ $ cargo new adder
└── target
```
Рабочая область содержит на верхнем уровне одна папка *target*, в который будут помещены собранные артефакты; дополнение `adder` не имеет собственного папки *target*. Даже если мы запустим `cargo build` из папки *adder*, собранные артефакты все равно окажутся в *add/target*, а не в *add/adder/target*. Cargo так определил папку *target* в рабочем пространстве, потому что дополнения в рабочем пространстве должны зависеть друг от друга. Если бы каждый дополнение имел свою собственную папку *target*, каждому дополнениеу пришлось бы пересобирать каждый из других дополнений в рабочем пространстве, чтобы поместить артефакты в свою собственную папку *target*. Благодаря совместному использованию единого папки *target* дополнения могут избежать ненужной пересборки.
Рабочая область содержит на верхнем уровне одна папка *target*, в который будут помещены собранные артефакты; дополнение `adder` не имеет собственного папки *target*. Даже если мы запустим `cargo build` из папки *adder*, собранные артефакты все равно окажутся в *add/target*, а не в *add/adder/target*. Cargo так определил папку *target* в рабочем пространстве, потому что дополнения в рабочем пространстве должны зависеть друг от друга. Если бы каждый дополнение имел свою собственную папку *target*, каждому дополнению пришлось бы пересобирать каждый из других дополнений в рабочем пространстве, чтобы поместить артефакты в свою собственную папку *target*. Благодаря совместному использованию единого папки *target* дополнения могут избежать ненужной пересборки.
### Добавление второго дополнения в рабочее пространство
Finished dev [unoptimized + debuginfo] target(s) in 10.18s
```
Файл *Cargo.lock* верхнего уровня теперь содержит сведения о зависимости `add_one` к дополнениеу`rand`. Тем не менее, не смотря на то что `rand` использован где-то в рабочем пространстве, мы не можем использовать его в других дополнениях рабочего пространства, пока не добавим дополнение `rand` в отдельные *Cargo.toml* файлы. Например, если мы добавим `use rand;` в файл *adder/src/main.rs* дополнения `adder`, то получим ошибку:
Файл *Cargo.lock* верхнего уровня теперь содержит сведения о зависимости `add_one` к дополнению`rand`. Тем не менее, не смотря на то что `rand` использован где-то в рабочем пространстве, мы не можем использовать его в других дополнениях рабочего пространства, пока не добавим дополнение `rand` в отдельные *Cargo.toml* файлы. Например, если мы добавим `use rand;` в файл *adder/src/main.rs* дополнения `adder`, то получим ошибку:
<!-- manual-regeneration
cd listings/ch14-more-about-cargo/output-only-03-use-rand/add
### Вызов небезопасной способы (функции) или способа
Второй вид действий, которые можно выполнять в небезопасном разделе - это вызов небезопасных функций. Небезопасные способы (функции) и способы выглядят точно так же, как обычные способы (функции) и способы, но перед остальным определением у них есть дополнительное `unsafe`. Ключевое слово `unsafe` в данном среде указывает на то, что к способы (функции) предъявляются требования, которые мы должны соблюдать при вызове этой способы (функции), поскольку Ржавчина не может обеспечить, что мы их выполняем. Вызывая небезопасную способ (функцию) внутри раздела `unsafe`, мы говорим, что прочитали пособие к этой способы (функции) и берём на себя ответственность за соблюдение её условий.
Второй вид действий, которые можно выполнять в небезопасном разделе - это вызов небезопасных функций. Небезопасные способы (функции) и способы выглядят точно так же, как обычные способы (функции) и способы, но перед остальным определением у них есть дополнительное `unsafe`. Ключевое слово `unsafe` в данном среде указывает на то, что к способы (функции) предъявляются требования, которые мы должны соблюдать при вызове этого способа (функции), поскольку Ржавчина не может обеспечить, что мы их выполняем. Вызывая небезопасную способ (функцию) внутри раздела `unsafe`, мы говорим, что прочитали пособие к этого способа (функции) и берём на себя ответственность за соблюдение её условий.
Вот небезопасная способ (функция) с именем `dangerous` которая ничего не делает в своём теле:
@ -167,7 +167,7 @@
@@ -167,7 +167,7 @@
> #### Вызов функций Ржавчина из других языков
>
> Также можно использовать `extern` для создания внешней оболочки, позволяющего другим языкам вызывать способы (функции) Ржавчины. Вместо того чтобы создавать целый раздел`extern`, мы добавляем ключевое слово `extern` и указываем ABI для использования непосредственно перед ключевым словом `fn` для необходимой способы (функции). Нам также нужно добавить изложение `#[no_mangle]`, чтобы сказать сборщику Ржавчины не искажать имя этой способы (функции). *Искажение* - это когда сборщик меняет имя, которое мы дали способы (функции), на другое имя, которое содержит больше сведений для других частей этапа сборки, но менее удобно для чтения для человека. Сборщик каждого языка программирования искажает имена по-разному, поэтому, чтобы способ (функция) Ржавчины могла быть использована другими языками, мы должны отключить искажение имён в сборщике Ржавчины.
> Также можно использовать `extern` для создания внешней оболочки, позволяющего другим языкам вызывать способы (функции) Ржавчины. Вместо того чтобы создавать целый раздел`extern`, мы добавляем ключевое слово `extern` и указываем ABI для использования непосредственно перед ключевым словом `fn` для необходимой способы (функции). Нам также нужно добавить изложение `#[no_mangle]`, чтобы сказать сборщику Ржавчины не искажать имя этого способа (функции). *Искажение* - это когда сборщик меняет имя, которое мы дали способы (функции), на другое имя, которое содержит больше сведений для других частей этапа сборки, но менее удобно для чтения для человека. Сборщик каждого языка программирования искажает имена по-разному, поэтому, чтобы способ (функция) Ржавчины могла быть использована другими языками, мы должны отключить искажение имён в сборщике Ржавчины.
>
> В следующем примере мы делаем способ (функцию) `call_from_c` доступной из рукописи на C, после того как она будет собрана в разделяемую библиотеку и прилинкована с C:
<spanclass="caption">Приложение 19-21: Использование полного правил написания для указания, что мы мы хотим вызвать способ (функцию) <code>baby_name</code> у сущности <code>Animal</code> выполненную в <code>Dog</code></span>
Мы указываем изложение вида данных в угловых скобках, которая указывает на то что мы хотим вызвать способ `baby_name` из сущности `Animal` выполненный в `Dog`, также указывая что мы хотим рассматривать вид данных `Dog` в качестве `Animal` для вызова этой способы (функции). Эта рукопись теперь выведет то, что мы хотим:
Мы указываем изложение вида данных в угловых скобках, которая указывает на то что мы хотим вызвать способ `baby_name` из сущности `Animal` выполненный в `Dog`, также указывая что мы хотим рассматривать вид данных `Dog` в качестве `Animal` для вызова этого способа (функции). Эта рукопись теперь выведет то, что мы хотим: