Руководство Rust по попаданию в яму: хорошо организованный

задняя часть Rust
Руководство Rust по попаданию в яму: хорошо организованный

Поскольку наши ямы становятся все больше и больше, нам приходится управлять всеми видами ям. Rust предоставляет нам систему управления питами, с помощью которой каждый может найти, управлять и заполнять свои питы упорядоченным образом.

Rust предоставляет нам некоторые возможности для управления кодом:

  • Пакеты:Функция Cargo, которая помогает вам создавать, тестировать и делиться ящиками
  • Ящики:Создать дерево модулей для библиотеки или исполняемого файла
  • Modulesа такжеиспользовать:Используется для управления организацией кода, областью действия и путями конфиденциальности.
  • Пути:Метод именования структуры, функции и модуля

Давайте подробнее рассмотрим, как эти функции помогают нам организовать наш код.

Пакеты и ящики

Пакет можно понимать как проект, а крейт — как кодовую базу. Ящик может использоваться несколькими проектами. Итак, как в нашем проекте определены пакеты и ящики?

Раньше мы всегда использовали IDEA для создания новых проектов, сегодня мы меняем метод и используем команду cargo для их создания в командной строке.

$ cargo new hello-world
     Created binary (application) `hello-world` package
$ ls hello-world
Cargo.toml
src
$ ls hello-world/src
main.rs

Как видите, после того, как мы использовали Cargo для создания проекта, в каталоге src остались только два файла, Cargo.toml и main.rs.

Cargo.toml — это файл, который управляет зависимостями проекта, и каждый файл Cargo.toml определяет пакет. Существование файла main.rs указывает на то, что пакет содержит двоичный крейт, который является входным файлом двоичного крейта, а имя крейта совпадает с именем пакета. Если файл lib.rs существует в каталоге src, пакет содержит крейт библиотеки с тем же именем, что и пакет.

Пакет может содержать несколько двоичных крейтов, которые определяются файлами в каталоге src/lib. Если ваш проект хочет ссылаться на ящики других людей, вы можете добавить зависимости в файл Cargo.toml. Каждый крейт имеет свое собственное пространство имен, поэтому, если вы импортируете крейт, определяющий функцию с именем hello, вы все равно можете определить функцию с именем hello в своем собственном крейте.

Module

Модуль помогает нам организовывать код в ящики, а модуль также является важным инструментом для инкапсуляции кода. Далее давайте узнаем больше о модуле через каштан.

Ранее мы говорили, что крейт библиотеки определяется в файле src/lib.rs. Здесь сначала создайте пакет, содержащий крейт библиотеки:

cargo new --lib restaurant

Затем определите некоторые модули и функции в src.

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

Вы можете видеть, что мы используем ключевые словаmodЧтобы определить модуль, модуль может продолжать определять модуль или функцию. Таким образом, мы можем удобно поместить связанные функции в модуль и назвать модуль, чтобы улучшить читаемость кода. Кроме того, модуль также может определять структуру и перечисление. Поскольку подмодули могут быть вложены и определены в модулях, конечный код, который мы определяем, напоминает дерево.

Так как доступа к функциям в модуле? Это упоминать путь. Эта часть легко понять, дерево модуля эквивалентно каталогу файлового файла, а путь является путь каталога.

Path

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

Теперь позвольте мне попробовать вызвать функцию add_to_waitlist в функции:

05-1

Видно, что независимо от того, используется ли здесь абсолютный или относительный путь, сообщается об ошибке Сообщение об ошибке заключается в том, что хостинг модуля и функция add_to_waitlist являются приватными. Давайте временно поместим эту ошибку. Согласно сообщению об ошибке здесь, мы знаем, что когда мы определяем модуль, он по умолчанию является закрытым. Таким образом, мы можем инкапсулировать детали реализации некоторого кода.

Хорошо, вернемся к предыдущему вопросу, как мы можем решить эту ошибку? Все на земле знают, что соответствующие модули и функции должны быть обнародованы. Ключевые слова в Rust, которые идентифицируют модуль или функцию как общедоступные:pub.

Мы используем ключевое слово pub для предоставления соответствующих модулей и функций.

05-2

Таким образом, мы можем вызывать функции в модуле извне модуля.

Приватные правила в Rust

Теперь давайте вернемся и посмотрим на некоторые приватные правила в Rust, если вы поэкспериментируете с приведенным выше примером, возможно, вы что-то найдете.

Приватные правила в Rust применяются ко всем элементам (функциям, методам, структурам, перечислениям, модулям и константам), которые по умолчанию являются приватными. Элементы в родительских модулях не могут получить доступ к закрытым элементам в подмодулях, в то время как элементы в подмодулях могут получить доступ к элементам в своих предках (родительский модуль и выше).

Конфиденциальность структуры и перечисления

Приваятность структуры и мошенничества немного отличается. Для структуры я могу установить только некоторые поля как общедоступные, и другие поля могут оставаться частными.

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);
}

Для Enum, если Enum является общедоступным, то все его значения являются общедоступными, потому что частные значения не имеют значения.

Выбор относительных и абсолютных путей

В этом выборе нет правильного или неправильного, есть только то, насколько он уместен. Итак, здесь мы просто приводим примеры некоторых подходящих ситуаций.

Давайте по-прежнему используем приведенный выше код в качестве примера, если мы можем предвидеть необходимость перемещения модуля front_of_house и функции eat_at_restaurant в новый модуль с именем customer_experience в будущем, мы должны использовать относительные пути, чтобы мы могли их настроить.

Точно так же, если нам нужно переместить функцию eat_at_restaurant в обеденный модуль, нам не нужно вносить коррективы, если мы выберем абсолютный путь.

Подводя итог, мы должны смотреть вперед в направлении оптимизации кода и использовать это, чтобы решить, нужно ли нам использовать относительный путь или абсолютный путь.

Относительные пути могут начинаться с super в дополнение к текущему модулю. Он представляет собой родительский модуль, похожий на две точки в файловой системе (..).

использовать ключевое слово

Абсолютные пути и относительные пути могут помочь нам найти указанную функцию, но они также очень хлопотны в использовании, и нам приходится каждый раз писать длинный список путей. К счастью, Rust предоставляет нам ключевое слово use. Ключевые слова импорта есть во многих языках, и их использование здесь чем-то похоже на импорт. Но Rust обеспечит более богатое использование.

Самое простое использование use — это введение пути. Мы можем использовать некоторые методы по этому пути более удобно:

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();
}

Этот путь может быть абсолютным или относительным путем, но если это относительный путь, он должен начинаться с себя. Приведенный выше пример можно записать так:

use self::front_of_house::hosting;

Кажется, это противоречит относительному пути, о котором мы говорили ранее, и официальные лица Rust говорят, что эта проблема будет решена в более поздней версии.

use также может пойти дальше и указать непосредственно на конкретную функцию, Struct или Enum. Но обычно, когда мы используем функции, за use следуют пути, чтобы мы могли знать, к какому модулю он принадлежит, когда мы вызываем функцию, а когда мы используем Struct/Enum, мы специально указываем на них. Конечно, это только привычка программирования, рекомендованная официальным лицом, у вас тоже может быть своя привычка, но лучше следовать официальной рекомендации или спецификациям, согласованным проектом.

Для некоторых подмодулей по одному пути они могут быть объединены в одну строку при импорте, например:

use std::io;
use std::cmp::Ordering;
// 等价于
use std::{cmp::Ordering, io};

Иногда мы также сталкиваемся со случаем обращения к одному и тому же имени Struct в разных пакетах, на данный момент есть два решения: одно — не указывать конкретный Struct и добавлять другой путь при его использовании, другое — использоватьasКлючевое слово, псевдоним для Struct.

метод первый:

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--
}

Если вы хотите импортировать все модули или функции по определенному пути, вы можете использовать*Представлять. Конечно, я не рекомендую использовать этот метод, потому что, если вы импортируете все, будет сложно устранить проблему, если возникнет конфликт имен.

Для внешних пакетов зависимостей нам нужно сначала добавить зависимости в файл Cargo.toml, а затем использовать use в коде, чтобы указать путь в библиотеке зависимостей. Rust предоставляет несколько стандартных библиотек, в том числе std. При использовании этих стандартных библиотек нет необходимости добавлять зависимости.

Некоторые ученики могут начать возмущаться, увидев это.Они уже объяснили, как разделить файл.Теперь они все еще играют одним файлом.Это не обман читателей.

Не волнуйтесь, здесь начинается раскол.

начать раскол

Давайте возьмем код прямо сейчас в качестве примера

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

Во-первых, мы можем разделить содержимое модуля front_of_house.Нам нужно создать новый файл front_of_house.rs в каталоге src, а затем записать содержимое модуля front_of_house в этот файл. В файле lib.rs необходимо объявить только модуль front_of_house, и никакого специального определения не требуется. При объявлении модуля достаточно поменять фигурные скобки, то есть содержимое, на точку с запятой.

mod front_of_house;

Затем мы можем продолжить разделять модуль хостинга и модуль обслуживания в модуле front_of_house.В это время нам нужно создать новый файл с именем front_of_house, поместить файл с тем же именем модуля, который нужно разделить, в эту папку, и запишите содержимое определения модуля в файл В файле front_of_house.rs также сохраняется только объявление.

Разделенный каталог файлов показан на рисунке

rust05-3

В этой статье в основном рассказывается о концепциях и использовании Package, Crate, Module и Path в Rust. С этими основами мы сможем позже разработать несколько более крупных проектов.

ps: все примеры кода в этой статье взяты изthe book.