Привет, Хаброжители! Официальный гайд по языку программирования Rust поможет вам создавать более быстрое и надежное программное обеспечение. Высокоуровневая эргономика и низкоуровневое управление часто противоречат друг другу, но Rust бросает вызов этому конфликту.
Авторы книги входят в команду разработчиков языка, а значит, вы получите всю информацию из первых рук — от установки языка до создания надежных и масштабируемых программ. От создания функций, выбора типов данных и привязки переменных вы перейдете к более сложным концепциям:
Вы найдете множество примеров кода, а также три главы, посвященные созданию полноценных проектов для закрепления знаний: игры-угадайки, создание инструмента командной строки и многопоточного сервера.
Мы предполагаем, что вы писали код на другом языке программирования, но не делаем никаких допущений относительно того, на каком именно. Мы постарались сделать этот материал доступным для тех, кто имеет широкий спектр навыков программирования. Мы не будем тратить время на разговоры о том, что такое программирование. Если вы в программировании абсолютный новичок, то для начала прочтите введение в программирование.
В Rust паттерны много где появляются, и вы часто их использовали, даже не осознавая этого! В данном разделе рассматриваются ситуации, в которых паттерны допустимы.
Как обсуждалось в главе 6, мы используем паттерны в ветвях выражений match. Формально выражения match определяются как ключевое слово match, затем значение для сопоставления и один или несколько ветвей совпадения, состоящих из паттерна и выполняемого выражения, если значение совпадает с паттерном этой ветви, например:
Одно из требований к выражениям match состоит в том, что они должны быть исчерпывающими в том смысле, что в match должны быть учтены все возможные значения. Чтобы вы учитывали все возможные варианты, нужно иметь всеохватывающий паттерн в последней ветви: например, имя переменной, совпадающее с любым значением, всегда будет срабатывать и, таким образом, охватывать все оставшиеся случаи.
Особый паттерн _ будет совпадать с чем угодно, но он не привязывается к переменной и поэтому часто используется в последнем рукаве совпадения. Паттерн _ бывает полезен, например, если вы хотите проигнорировать любое неуказанное значение. Мы рассмотрим паттерн _ подробнее в разделе «Игнорирование значений в паттерне».
В главе 6 мы обсуждали выражения if let главным образом как более краткий способ написания эквивалента выражения match, который совпадает только с одним случаем. Как вариант if let может иметь соответствующий else, содержащий выполняемый код, если паттерн в выражении if let не совпадает.
Листинг 18.1 показывает, что также существует возможность смешивать и сочетать выражения if let, else if и else if let. Благодаря этому мы получаем больше гибкости, чем при использовании выражения match, в котором можно выразить только одно сравниваемое с паттернами значение. Кроме того, условия в серии выражений if let, else if и else if let не обязательно должны относиться друг к другу.
Код в листинге 18.1 показывает серию проверок на несколько условий, которые решают, каким должен быть фоновый цвет. В этом примере мы создали переменные с жестко заданными значениями, которые реальная программа могла бы получить из данных, вводимых пользователем.
Листинг 18.1. Смешивание выражений if let, else if, else if let и else
Если пользователь указывает любимый цвет (1), то это фоновый цвет (2). Если сегодня — вторник (3), то фоновый цвет — зеленый (4). Если пользователь указывает свой возраст в качестве строки и мы можем успешно разобрать ее как число (5), то цвет будет либо фиолетовым (7), либо оранжевым (8) в зависимости от значения числа (6). Если ни одно из этих условий не применимо (9), то фоновый цвет — синий (10).
Эта условная структура позволяет поддерживать сложные требования. С жестко закодированными значениями, которые здесь есть, этот пример будет выводить
Вы видите, что выражение if let может также вводить затененные переменные таким же образом, как и рукава выражения match: строка кода if let Ok(age) = age (5) вводит новую затененную переменную age, содержащую значение внутри варианта Ok. Это означает, что нужно поместить условие if age > 30 в этот блок (6): мы не можем совместить эти два условия в выражении if let Ok (age) = age && age > 30. Затененная переменная age, которую мы хотим сравнить с 30, будет недействительна до тех пор, пока новая область видимости не начнется с фигурной скобки.
Недостатком использования выражений if let является то, что компилятор не проверяет исчерпываемость, в то время как с выражениями match он это делает. Если бы мы пропустили последний блок else (9) и, следовательно, обработку некоторых случаев, то компилятор не предупредил бы нас о возможной логической ошибке.
Похожий по конструкции с выражением if let условный цикл while let позволяет циклу while работать до тех пор, пока паттерн совпадает. Пример в листинге 18.2 показывает цикл while let, который использует вектор в качестве стека и выводит значения в векторе в порядке, обратном тому, в котором они были добавлены.
Листинг 18.2. Использование цикла while let для печати значений, пока метод stack.pop() возвращает Some
В этом примере выводятся 3, 2 и затем 1. Метод pop берет последний элемент из вектора и возвращает Some(value). Если вектор пуст, то pop возвращает None. Цикл while продолжает выполнение кода в своем блоке до тех пор, пока pop возвращает Some. Когда pop возвращает None, цикл останавливается. Мы можем использовать условный цикл while let, чтобы удалить каждый элемент из стека.
В главе 3 мы упоминали, что цикл for — это наиболее распространенная циклическая конструкция в коде на языке Rust, но мы еще не обсуждали паттерн, который берет for. В цикле for паттерном является значение, следующее непосредственно за ключевым словом for, поэтому в for x in y паттерном является x.
Листинг 18.3 показывает использование паттерна в цикле for для деструктурирования, или разложения, кортежа в рамках for.
Листинг 18.3. Использование паттерна в цикле for для деструктурирования кортежа
Код из листинга 18.3 выводит следующее:
Мы используем метод enumerate, чтобы переделать итератор для порождения значения и индекса этого значения в итераторе, помещенных в кортеж. Первый вызов метода enumerate порождает кортеж (0, 'a'). Когда это значение сочетается с паттерном (index, value), то index равен 0, а value равно 'a', выводится первая строка данных.
До этой главы мы прямо обсуждали использование паттернов только с выражениями match и if let, но на самом деле мы использовали паттерны и в других местах, в том числе в инструкциях let. Рассмотрим простую передачу значения переменной с помощью let:
На протяжении всей книги мы сотни раз использовали инструкции let подобного рода, и хотя вы, возможно, не осознавали этого, вы использовали паттерны! В более формальном плане инструкция let выглядит так:
В таких инструкциях, как let x = 5;, с именем переменной в слоте ПАТТЕРН, имя переменной — это всего лишь простая форма паттерна. Rust сравнивает выражение с паттерном и назначает любые имена, которые он находит. Поэтому в примере let x = 5; паттерном является x, который означает «связать то, что совпадает здесь, с переменной x». Поскольку имя x представляет весь паттерн, то этот паттерн фактически означает «связать все с переменной x, каким бы ни было значение».
Чтобы четче увидеть сопоставление с паттерном инструкции let, рассмотрим листинг 18.4, который использует паттерн с let для деструктурирования кортежа.
Листинг 18.4. Использование паттерна для деструктурирования кортежа и создания сразу трех переменных
Здесь мы сопоставляем кортеж с паттерном. Rust сравнивает (1, 2, 3) с (x, y, z) и видит, что это значение совпадает с паттерном, поэтому Rust связывает 1 с x, 2 с y и 3 с z. Можно думать об этом кортежном паттерне как о вложении в него трех отдельных паттернов переменных.
Если число элементов в паттерне не совпадает с числом элементов в кортеже, то совокупный тип не будет совпадать и мы получим ошибку компилятора. Например, листинг 18.5 показывает попытку деструктурирования кортежа из трех элементов в две переменные, которая не будет работать.
Листинг 18.5. Неправильное построение паттерна, переменные которого не совпадают с числом элементов в кортеже
Попытка компиляции этого кода приводит к ошибке типа:
Если бы мы хотели проигнорировать одно или несколько значений в кортеже, то могли бы использовать _ или .., как вы увидите в разделе «Игнорирование значений в паттерне». Если проблема состоит в том, что в паттерне слишком много переменных, то нужно сделать так, чтобы типы совпадали, удалив переменные, чтобы число переменных равнялось числу элементов в кортеже.
Параметры функций также могут быть паттернами. Код в листинге 18.6, объявляющий функцию foo, которая берет один параметр x типа i32, теперь вам знаком.
Листинг 18.6. Сигнатура функции использует паттерны в параметрах
Часть x — это паттерн! Как и в случае с let, мы можем сопоставить кортеж в аргументах функции с паттерном. Листинг 18.7 разбивает значения в кортеже в момент, когда мы передаем его внутрь функции.
Листинг 18.7. Функция с параметрами, которые деструктурируют кортеж
Этот код выводит
Значения &(3, 5) совпадают с паттерном &(x, y), поэтому x равно 3, а y — 5.
Кроме того, мы можем использовать паттерны в списках параметров замыкания таким же образом, как и в списках параметров функций, поскольку замыкания похожи на функции, как описано в главе 13.
Вы уже увидели несколько способов использования паттернов, но они не работают одинаково всюду, где можно их применить. В некоторых ситуациях эти паттерны должны быть неопровержимы, в других — они могут быть опровержимы. Далее мы обсудим эти два понятия.
Паттерны бывают двух видов: опровержимые и неопровержимые. Паттерны, которые будут совпадать с любым возможным переданным значением, неопровержимые. Примером является x в инструкции let x = 5;, потому что x совпадает абсолютно со всем и, следовательно, не может не совпасть. Паттерны, которые не совпадают с некоторыми возможными значениями, являются опровержимыми. Примером служит Some(x) в выражении if let Some(x) = a_value, потому что, если значение в переменной a_value равно None, а не Some, то паттерн Some(x) не совпадет.
Параметры функций, инструкции let и циклы for могут принимать только неопровержимые паттерны, поскольку программа не сможет делать ничего значимого, когда значения не совпадают. Выражения if let и while let принимают только опровержимые паттерны, поскольку по определению они предназначены для обработки возможной ошибки: функциональность условного выражения заключается в его способности выполнять разные действия в зависимости от успеха или провала.
В целом вам не следует беспокоиться о различии между опровержимыми и неопровержимыми паттернами. Однако вам все-таки нужно знать о понятии опровержимости, чтобы реагировать при виде его в сообщении об ошибке. В этих случаях вам потребуется изменить либо паттерн, либо конструкцию, с которой вы используете паттерн, в зависимости от предполагаемого поведения кода.
Давайте рассмотрим, что происходит, когда мы пытаемся использовать опровержимый паттерн в месте, где Rust требует неопровержимый паттерн, и наоборот. Листинг 18.8 показывает инструкцию let, но для паттерна мы задали Some(x), опровержимый паттерн. Как и следовало ожидать, этот код не компилируется.
Листинг 18.8. Попытка использовать опровержимый паттерн с let
Если бы значение some_option_value было равно None, то оно не совпало бы с паттерном Some(x), то есть паттерн является опровержимым. Однако инструкция let может принимать только неопровержимый паттерн, поскольку код не может сделать ничего допустимого со значением None. Во время компиляции язык Rust будет жаловаться, что мы пытались использовать опровержимый паттерн там, где требуется неопровержимый паттерн:
Поскольку мы не охватили (и не могли охватить!) каждое допустимое значение с паттерном Some(x), Rust по праву выдает ошибку компилятора.
Для устранения проблемы, когда у нас опровержимый паттерн вместо неопровержимого, мы можем изменить код, использующий паттерн: вместо let можно применить if let. Тогда, если паттерн не совпадает, то код в фигурных скобках будет пропущен и работа продолжится корректно. Листинг 18.9 показывает, как исправить код из листинга 18.8.
Листинг 18.9. Использование выражения if let и блока с опровержимыми паттернами вместо let
Код готов! Это абсолютно правильный код, хотя он означает, что мы не можем использовать неопровержимый паттерн без ошибки. Если мы дадим выражению if let паттерн, который всегда будет совпадать, например x, как показано в листинге 18.10, то он компилироваться не будет.
Листинг 18.10. Попытка использовать неопровержимый паттерн с выражением if let
Компилятор жалуется, что использовать выражение if let с неопровержимым паттерном не имеет смысла:
По этой причине рукава выражения match должны использовать опровержимые паттерны, за исключением последнего рукава, который должен сопоставлять любые оставшиеся значения с неопровержимым паттерном. Rust позволяет использовать неопровержимый паттерн в выражении match только с одним рукавом, но этот синтаксис не особо полезен, и его можно заменить более простой инструкцией let.
Теперь, когда вы знаете, где используются паттерны и чем отличаются опровержимые и неопровержимые паттерны, давайте познакомимся с синтаксисом, который мы можем использовать для создания паттернов.
Стив Клабник возглавляет команду по документированию Rust и является одним из ключевых разработчиков языка. Часто выступает с лекциям и пишет много открытого исходного кода. Ранее работал над такими проектами, как Ruby и Ruby on Rails.
Кэрол Николс является членом команды разработчиков Rust Core и соучредителем Integer 32, LLC, первой в мире консалтинговой компании по разработке ПО, ориентированной на Rust. Николс является организатором конференции «Ржавый пояс» (Rust Belt) по языку Rust.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Rust
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Авторы книги входят в команду разработчиков языка, а значит, вы получите всю информацию из первых рук — от установки языка до создания надежных и масштабируемых программ. От создания функций, выбора типов данных и привязки переменных вы перейдете к более сложным концепциям:
- Владение и заимствование, жизненный цикл и типажи.
- Гарантированная безопасность программ.
- Тестирование, обработка ошибок и эффективный рефакторинг.
- Обобщения, умные указатели, многопоточность, типажные объекты и сопоставления.
- Работа со встроенным менеджером пакетов Cargo для создания, тестирования, документирования кода и управления зависимостями.
- Продвинутые средства работы с Unsafe Rust.
Вы найдете множество примеров кода, а также три главы, посвященные созданию полноценных проектов для закрепления знаний: игры-угадайки, создание инструмента командной строки и многопоточного сервера.
Для кого эта книга
Мы предполагаем, что вы писали код на другом языке программирования, но не делаем никаких допущений относительно того, на каком именно. Мы постарались сделать этот материал доступным для тех, кто имеет широкий спектр навыков программирования. Мы не будем тратить время на разговоры о том, что такое программирование. Если вы в программировании абсолютный новичок, то для начала прочтите введение в программирование.
Как пользоваться этой книгой
В общем-то, авторы этой книги исходят из того, что вы читаете ее последовательно, от начала до конца. Последующие главы строятся на понятиях предыдущих глав, и в начальных главах мы можем не углубляться в детали по конкретной теме; обычно мы возвращаемся к этой теме в дальнейшем.
В этой книге вы найдете два типа глав: концептуальные и проектные. В концептуальных главах вы будете усваивать тот или иной аспект языка. Мы вместе будем создавать небольшие программы, применяя то, что вы уже усвоили. Главы 2, 12 и 20 посвящены разработке проектов, остальные главы — концептуальные.
В главе 1 рассказано, как установить Rust, написать программу «Hello, World!» и использовать пакетный менеджер и инструмент Cargo. Глава 2 представляет собой практическое введение в язык Rust. Здесь мы рассмотрим понятия с точки зрения высокоуровневого языка, а в последующих главах приведем дополнительные подробности. Если вы хотите сразу же приступить к практике, то сможете это сделать. Можно даже пропустить главу 3, в которой рассматриваются средства языка Rust, аналогичные средствам других языков программирования, сразу перейти к главе 4 и узнать о системе владения в Rust. Но если вы дотошны и предпочитаете разбирать каждую деталь, прежде чем переходить к следующей, то можете пропустить главу 2, перейти к главе 3, а затем вернуться к главе 2, когда захотите поработать над проектом. Так вы сможете применить знания, которые освоили.
В главе 5 обсуждаются структуры и методы, а в главе 6 рассматриваются перечисления, выражения match и конструкция управления потоком if let. Вы будете использовать структуры и перечисления для создания в языке Rust настраиваемых типов.
В главе 7 вы узнаете о системе модулей и правилах конфиденциальности для выстраивания организационной структуры вашего кода и его публичном интерфейсе программирования приложений (API). В главе 8 обсуждаются некоторые часто встречающиеся структуры сбора данных, обеспечиваемые стандартной библиотекой, такие как векторы, строки и хеш-отображения. В главе 9 изучаются философия и методы обработки ошибок.
В главе 10 мы погрузимся в обобщения, типажи и жизненные циклы, которые дают вам возможность определять код, применимый к нескольким типам. Глава 11 полностью посвящена тестированию, которое даже несмотря на гарантии безопасности языка Rust является необходимым для обеспечения правильной логики программы. В главе 12 мы построим собственную реализацию подмножества функциональности инструмента командной строки grep, которая ищет текст внутри файлов. Для этого мы воспользуемся многими понятиями, которые обсуждаются в предыдущих главах.
В главе 13 рассматриваются замыкания и итераторы — средства, которые восходят к функциональным языкам программирования. В главе 14 мы изучим Cargo подробнее и расскажем о лучших практических приемах обмена библиотеками с другими разработчиками. В главе 15 обсуждаются умные указатели, которые обеспечивает стандартная библиотека, и типажи, которые гарантируют их функциональность.
В главе 16 мы рассмотрим разные модели конкурентного программирования и поговорим о том, как Rust помогает вам безбоязненно программировать в множестве потоков исполнения. Глава 17 обращается к сопоставлению идиом Rust с принципами объектно-ориентированного программирования, с которыми вы, возможно, знакомы.
Глава 18 представляет собой справочный материал о паттернах и сопоставлении с паттернами, которые являются мощными способами выражения идей во всех программах на языке Rust. Глава 19 содержит ряд дополнительных тем, представляющих интерес, включая небезопасный код Rust, макрокоманды и другие сведения о типажах, типах, функциях и замыканиях.
В главе 20 мы осуществим проект, в котором выполним реализацию низкоуровневого многопоточного сервера!
Наконец, несколько приложений в конце книги содержат полезную информацию о языке в справочном формате. В приложении А приводятся ключевые слова языка Rust, в приложении Б рассказывается об операторах и символах языка Rust, в приложении В рассматриваются генерируемые типажи, предусмотренные стандартной библиотекой, в приложении Г приводятся некоторые полезные инструменты разработчика, а в приложении Д даются пояснения по поводу редакций языка Rust.
Просто невозможно прочитать эту книгу неправильно: если вы хотите пропустить что-то, пропускайте! В случае если вы почувствуете какую-то путаницу, то, возможно, вам придется вернуться к предыдущим главам. Короче, делайте все, что вам подходит.
Важная часть процесса усвоения языка Rust — научиться читать сообщения об ошибках, выводимые на экран компилятором: они будут направлять вас к рабочему коду. В связи с этим мы приведем много примеров, которые не компилируются вместе с сообщением об ошибке, которое компилятор покажет вам в каждой ситуации. Знайте, что если вы введете и запустите выполнение случайного примера, то он может не скомпилироваться! Обязательно прочтите окружающий текст, чтобы увидеть, что пример, который вы пытаетесь выполнить, является неизбежно ошибочным. В большинстве ситуаций мы будем вести вас к правильной версии любого кода, который не компилируется.
В этой книге вы найдете два типа глав: концептуальные и проектные. В концептуальных главах вы будете усваивать тот или иной аспект языка. Мы вместе будем создавать небольшие программы, применяя то, что вы уже усвоили. Главы 2, 12 и 20 посвящены разработке проектов, остальные главы — концептуальные.
В главе 1 рассказано, как установить Rust, написать программу «Hello, World!» и использовать пакетный менеджер и инструмент Cargo. Глава 2 представляет собой практическое введение в язык Rust. Здесь мы рассмотрим понятия с точки зрения высокоуровневого языка, а в последующих главах приведем дополнительные подробности. Если вы хотите сразу же приступить к практике, то сможете это сделать. Можно даже пропустить главу 3, в которой рассматриваются средства языка Rust, аналогичные средствам других языков программирования, сразу перейти к главе 4 и узнать о системе владения в Rust. Но если вы дотошны и предпочитаете разбирать каждую деталь, прежде чем переходить к следующей, то можете пропустить главу 2, перейти к главе 3, а затем вернуться к главе 2, когда захотите поработать над проектом. Так вы сможете применить знания, которые освоили.
В главе 5 обсуждаются структуры и методы, а в главе 6 рассматриваются перечисления, выражения match и конструкция управления потоком if let. Вы будете использовать структуры и перечисления для создания в языке Rust настраиваемых типов.
В главе 7 вы узнаете о системе модулей и правилах конфиденциальности для выстраивания организационной структуры вашего кода и его публичном интерфейсе программирования приложений (API). В главе 8 обсуждаются некоторые часто встречающиеся структуры сбора данных, обеспечиваемые стандартной библиотекой, такие как векторы, строки и хеш-отображения. В главе 9 изучаются философия и методы обработки ошибок.
В главе 10 мы погрузимся в обобщения, типажи и жизненные циклы, которые дают вам возможность определять код, применимый к нескольким типам. Глава 11 полностью посвящена тестированию, которое даже несмотря на гарантии безопасности языка Rust является необходимым для обеспечения правильной логики программы. В главе 12 мы построим собственную реализацию подмножества функциональности инструмента командной строки grep, которая ищет текст внутри файлов. Для этого мы воспользуемся многими понятиями, которые обсуждаются в предыдущих главах.
В главе 13 рассматриваются замыкания и итераторы — средства, которые восходят к функциональным языкам программирования. В главе 14 мы изучим Cargo подробнее и расскажем о лучших практических приемах обмена библиотеками с другими разработчиками. В главе 15 обсуждаются умные указатели, которые обеспечивает стандартная библиотека, и типажи, которые гарантируют их функциональность.
В главе 16 мы рассмотрим разные модели конкурентного программирования и поговорим о том, как Rust помогает вам безбоязненно программировать в множестве потоков исполнения. Глава 17 обращается к сопоставлению идиом Rust с принципами объектно-ориентированного программирования, с которыми вы, возможно, знакомы.
Глава 18 представляет собой справочный материал о паттернах и сопоставлении с паттернами, которые являются мощными способами выражения идей во всех программах на языке Rust. Глава 19 содержит ряд дополнительных тем, представляющих интерес, включая небезопасный код Rust, макрокоманды и другие сведения о типажах, типах, функциях и замыканиях.
В главе 20 мы осуществим проект, в котором выполним реализацию низкоуровневого многопоточного сервера!
Наконец, несколько приложений в конце книги содержат полезную информацию о языке в справочном формате. В приложении А приводятся ключевые слова языка Rust, в приложении Б рассказывается об операторах и символах языка Rust, в приложении В рассматриваются генерируемые типажи, предусмотренные стандартной библиотекой, в приложении Г приводятся некоторые полезные инструменты разработчика, а в приложении Д даются пояснения по поводу редакций языка Rust.
Просто невозможно прочитать эту книгу неправильно: если вы хотите пропустить что-то, пропускайте! В случае если вы почувствуете какую-то путаницу, то, возможно, вам придется вернуться к предыдущим главам. Короче, делайте все, что вам подходит.
Важная часть процесса усвоения языка Rust — научиться читать сообщения об ошибках, выводимые на экран компилятором: они будут направлять вас к рабочему коду. В связи с этим мы приведем много примеров, которые не компилируются вместе с сообщением об ошибке, которое компилятор покажет вам в каждой ситуации. Знайте, что если вы введете и запустите выполнение случайного примера, то он может не скомпилироваться! Обязательно прочтите окружающий текст, чтобы увидеть, что пример, который вы пытаетесь выполнить, является неизбежно ошибочным. В большинстве ситуаций мы будем вести вас к правильной версии любого кода, который не компилируется.
Где могут использоваться паттерны
В Rust паттерны много где появляются, и вы часто их использовали, даже не осознавая этого! В данном разделе рассматриваются ситуации, в которых паттерны допустимы.
Ветви выражения match
Как обсуждалось в главе 6, мы используем паттерны в ветвях выражений match. Формально выражения match определяются как ключевое слово match, затем значение для сопоставления и один или несколько ветвей совпадения, состоящих из паттерна и выполняемого выражения, если значение совпадает с паттерном этой ветви, например:
match ЗНАЧЕНИЕ {
ПАТТЕРН => ВЫРАЖЕНИЕ,
ПАТТЕРН => ВЫРАЖЕНИЕ,
ПАТТЕРН => ВЫРАЖЕНИЕ,
}
Одно из требований к выражениям match состоит в том, что они должны быть исчерпывающими в том смысле, что в match должны быть учтены все возможные значения. Чтобы вы учитывали все возможные варианты, нужно иметь всеохватывающий паттерн в последней ветви: например, имя переменной, совпадающее с любым значением, всегда будет срабатывать и, таким образом, охватывать все оставшиеся случаи.
Особый паттерн _ будет совпадать с чем угодно, но он не привязывается к переменной и поэтому часто используется в последнем рукаве совпадения. Паттерн _ бывает полезен, например, если вы хотите проигнорировать любое неуказанное значение. Мы рассмотрим паттерн _ подробнее в разделе «Игнорирование значений в паттерне».
Условные выражения if let
В главе 6 мы обсуждали выражения if let главным образом как более краткий способ написания эквивалента выражения match, который совпадает только с одним случаем. Как вариант if let может иметь соответствующий else, содержащий выполняемый код, если паттерн в выражении if let не совпадает.
Листинг 18.1 показывает, что также существует возможность смешивать и сочетать выражения if let, else if и else if let. Благодаря этому мы получаем больше гибкости, чем при использовании выражения match, в котором можно выразить только одно сравниваемое с паттернами значение. Кроме того, условия в серии выражений if let, else if и else if let не обязательно должны относиться друг к другу.
Код в листинге 18.1 показывает серию проверок на несколько условий, которые решают, каким должен быть фоновый цвет. В этом примере мы создали переменные с жестко заданными значениями, которые реальная программа могла бы получить из данных, вводимых пользователем.
Листинг 18.1. Смешивание выражений if let, else if, else if let и else
src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
(1) if let Some(color) = favorite_color {
(2) println!("Используя ваш любимый цвет, {}, в качестве фона", color);
(3) } else if is_tuesday {
(4) println!("Вторник - зеленый день!");
(5) } else if let Ok(age) = age {
(6) if age > 30 {
(7) println!("Использование фиолетового цвета в качестве фона");
} else {
(8) println!("Использование оранжевого цвета в качестве фона");
}
(9) } else {
(10) println!("Использование синего цвета в качестве фона");
}
}
Если пользователь указывает любимый цвет (1), то это фоновый цвет (2). Если сегодня — вторник (3), то фоновый цвет — зеленый (4). Если пользователь указывает свой возраст в качестве строки и мы можем успешно разобрать ее как число (5), то цвет будет либо фиолетовым (7), либо оранжевым (8) в зависимости от значения числа (6). Если ни одно из этих условий не применимо (9), то фоновый цвет — синий (10).
Эта условная структура позволяет поддерживать сложные требования. С жестко закодированными значениями, которые здесь есть, этот пример будет выводить
Использование фиолетового цвета в качестве фона.
Вы видите, что выражение if let может также вводить затененные переменные таким же образом, как и рукава выражения match: строка кода if let Ok(age) = age (5) вводит новую затененную переменную age, содержащую значение внутри варианта Ok. Это означает, что нужно поместить условие if age > 30 в этот блок (6): мы не можем совместить эти два условия в выражении if let Ok (age) = age && age > 30. Затененная переменная age, которую мы хотим сравнить с 30, будет недействительна до тех пор, пока новая область видимости не начнется с фигурной скобки.
Недостатком использования выражений if let является то, что компилятор не проверяет исчерпываемость, в то время как с выражениями match он это делает. Если бы мы пропустили последний блок else (9) и, следовательно, обработку некоторых случаев, то компилятор не предупредил бы нас о возможной логической ошибке.
Условные циклы while let
Похожий по конструкции с выражением if let условный цикл while let позволяет циклу while работать до тех пор, пока паттерн совпадает. Пример в листинге 18.2 показывает цикл while let, который использует вектор в качестве стека и выводит значения в векторе в порядке, обратном тому, в котором они были добавлены.
Листинг 18.2. Использование цикла while let для печати значений, пока метод stack.pop() возвращает Some
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
В этом примере выводятся 3, 2 и затем 1. Метод pop берет последний элемент из вектора и возвращает Some(value). Если вектор пуст, то pop возвращает None. Цикл while продолжает выполнение кода в своем блоке до тех пор, пока pop возвращает Some. Когда pop возвращает None, цикл останавливается. Мы можем использовать условный цикл while let, чтобы удалить каждый элемент из стека.
Циклы for
В главе 3 мы упоминали, что цикл for — это наиболее распространенная циклическая конструкция в коде на языке Rust, но мы еще не обсуждали паттерн, который берет for. В цикле for паттерном является значение, следующее непосредственно за ключевым словом for, поэтому в for x in y паттерном является x.
Листинг 18.3 показывает использование паттерна в цикле for для деструктурирования, или разложения, кортежа в рамках for.
Листинг 18.3. Использование паттерна в цикле for для деструктурирования кортежа
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} находится в индексе {}", value, index);
}
Код из листинга 18.3 выводит следующее:
а находится в индексе 0
b находится в индексе 1
с находится в индексе 2
Мы используем метод enumerate, чтобы переделать итератор для порождения значения и индекса этого значения в итераторе, помещенных в кортеж. Первый вызов метода enumerate порождает кортеж (0, 'a'). Когда это значение сочетается с паттерном (index, value), то index равен 0, а value равно 'a', выводится первая строка данных.
Инструкции let
До этой главы мы прямо обсуждали использование паттернов только с выражениями match и if let, но на самом деле мы использовали паттерны и в других местах, в том числе в инструкциях let. Рассмотрим простую передачу значения переменной с помощью let:
let x = 5;
На протяжении всей книги мы сотни раз использовали инструкции let подобного рода, и хотя вы, возможно, не осознавали этого, вы использовали паттерны! В более формальном плане инструкция let выглядит так:
let ПАТТЕРН = ВЫРАЖЕНИЕ;
В таких инструкциях, как let x = 5;, с именем переменной в слоте ПАТТЕРН, имя переменной — это всего лишь простая форма паттерна. Rust сравнивает выражение с паттерном и назначает любые имена, которые он находит. Поэтому в примере let x = 5; паттерном является x, который означает «связать то, что совпадает здесь, с переменной x». Поскольку имя x представляет весь паттерн, то этот паттерн фактически означает «связать все с переменной x, каким бы ни было значение».
Чтобы четче увидеть сопоставление с паттерном инструкции let, рассмотрим листинг 18.4, который использует паттерн с let для деструктурирования кортежа.
Листинг 18.4. Использование паттерна для деструктурирования кортежа и создания сразу трех переменных
let (x, y, z) = (1, 2, 3);
Здесь мы сопоставляем кортеж с паттерном. Rust сравнивает (1, 2, 3) с (x, y, z) и видит, что это значение совпадает с паттерном, поэтому Rust связывает 1 с x, 2 с y и 3 с z. Можно думать об этом кортежном паттерне как о вложении в него трех отдельных паттернов переменных.
Если число элементов в паттерне не совпадает с числом элементов в кортеже, то совокупный тип не будет совпадать и мы получим ошибку компилятора. Например, листинг 18.5 показывает попытку деструктурирования кортежа из трех элементов в две переменные, которая не будет работать.
Листинг 18.5. Неправильное построение паттерна, переменные которого не совпадают с числом элементов в кортеже
let (x, y) = (1, 2, 3);
Попытка компиляции этого кода приводит к ошибке типа:
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
Если бы мы хотели проигнорировать одно или несколько значений в кортеже, то могли бы использовать _ или .., как вы увидите в разделе «Игнорирование значений в паттерне». Если проблема состоит в том, что в паттерне слишком много переменных, то нужно сделать так, чтобы типы совпадали, удалив переменные, чтобы число переменных равнялось числу элементов в кортеже.
Параметры функций
Параметры функций также могут быть паттернами. Код в листинге 18.6, объявляющий функцию foo, которая берет один параметр x типа i32, теперь вам знаком.
Листинг 18.6. Сигнатура функции использует паттерны в параметрах
fn foo(x: i32) {
// здесь будет код
}
Часть x — это паттерн! Как и в случае с let, мы можем сопоставить кортеж в аргументах функции с паттерном. Листинг 18.7 разбивает значения в кортеже в момент, когда мы передаем его внутрь функции.
Листинг 18.7. Функция с параметрами, которые деструктурируют кортеж
src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Текущее местоположение: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Этот код выводит
Текущее местоположение: (3, 5)
Значения &(3, 5) совпадают с паттерном &(x, y), поэтому x равно 3, а y — 5.
Кроме того, мы можем использовать паттерны в списках параметров замыкания таким же образом, как и в списках параметров функций, поскольку замыкания похожи на функции, как описано в главе 13.
Вы уже увидели несколько способов использования паттернов, но они не работают одинаково всюду, где можно их применить. В некоторых ситуациях эти паттерны должны быть неопровержимы, в других — они могут быть опровержимы. Далее мы обсудим эти два понятия.
Опровержимость: возможность несовпадения паттерна
Паттерны бывают двух видов: опровержимые и неопровержимые. Паттерны, которые будут совпадать с любым возможным переданным значением, неопровержимые. Примером является x в инструкции let x = 5;, потому что x совпадает абсолютно со всем и, следовательно, не может не совпасть. Паттерны, которые не совпадают с некоторыми возможными значениями, являются опровержимыми. Примером служит Some(x) в выражении if let Some(x) = a_value, потому что, если значение в переменной a_value равно None, а не Some, то паттерн Some(x) не совпадет.
Параметры функций, инструкции let и циклы for могут принимать только неопровержимые паттерны, поскольку программа не сможет делать ничего значимого, когда значения не совпадают. Выражения if let и while let принимают только опровержимые паттерны, поскольку по определению они предназначены для обработки возможной ошибки: функциональность условного выражения заключается в его способности выполнять разные действия в зависимости от успеха или провала.
В целом вам не следует беспокоиться о различии между опровержимыми и неопровержимыми паттернами. Однако вам все-таки нужно знать о понятии опровержимости, чтобы реагировать при виде его в сообщении об ошибке. В этих случаях вам потребуется изменить либо паттерн, либо конструкцию, с которой вы используете паттерн, в зависимости от предполагаемого поведения кода.
Давайте рассмотрим, что происходит, когда мы пытаемся использовать опровержимый паттерн в месте, где Rust требует неопровержимый паттерн, и наоборот. Листинг 18.8 показывает инструкцию let, но для паттерна мы задали Some(x), опровержимый паттерн. Как и следовало ожидать, этот код не компилируется.
Листинг 18.8. Попытка использовать опровержимый паттерн с let
let Some(x) = some_option_value;
Если бы значение some_option_value было равно None, то оно не совпало бы с паттерном Some(x), то есть паттерн является опровержимым. Однако инструкция let может принимать только неопровержимый паттерн, поскольку код не может сделать ничего допустимого со значением None. Во время компиляции язык Rust будет жаловаться, что мы пытались использовать опровержимый паттерн там, где требуется неопровержимый паттерн:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
Поскольку мы не охватили (и не могли охватить!) каждое допустимое значение с паттерном Some(x), Rust по праву выдает ошибку компилятора.
Для устранения проблемы, когда у нас опровержимый паттерн вместо неопровержимого, мы можем изменить код, использующий паттерн: вместо let можно применить if let. Тогда, если паттерн не совпадает, то код в фигурных скобках будет пропущен и работа продолжится корректно. Листинг 18.9 показывает, как исправить код из листинга 18.8.
Листинг 18.9. Использование выражения if let и блока с опровержимыми паттернами вместо let
if let Some(x) = some_option_value {
println!("{}", x);
}
Код готов! Это абсолютно правильный код, хотя он означает, что мы не можем использовать неопровержимый паттерн без ошибки. Если мы дадим выражению if let паттерн, который всегда будет совпадать, например x, как показано в листинге 18.10, то он компилироваться не будет.
Листинг 18.10. Попытка использовать неопровержимый паттерн с выражением if let
if let x = 5 {
println!("{}", x);
};
Компилятор жалуется, что использовать выражение if let с неопровержимым паттерном не имеет смысла:
error[E0162]: irrefutable if-let pattern
--> <anon>:2:8
|
2 | if let x = 5 {
| ^ irrefutable pattern
По этой причине рукава выражения match должны использовать опровержимые паттерны, за исключением последнего рукава, который должен сопоставлять любые оставшиеся значения с неопровержимым паттерном. Rust позволяет использовать неопровержимый паттерн в выражении match только с одним рукавом, но этот синтаксис не особо полезен, и его можно заменить более простой инструкцией let.
Теперь, когда вы знаете, где используются паттерны и чем отличаются опровержимые и неопровержимые паттерны, давайте познакомимся с синтаксисом, который мы можем использовать для создания паттернов.
Об авторах
Стив Клабник возглавляет команду по документированию Rust и является одним из ключевых разработчиков языка. Часто выступает с лекциям и пишет много открытого исходного кода. Ранее работал над такими проектами, как Ruby и Ruby on Rails.
Кэрол Николс является членом команды разработчиков Rust Core и соучредителем Integer 32, LLC, первой в мире консалтинговой компании по разработке ПО, ориентированной на Rust. Николс является организатором конференции «Ржавый пояс» (Rust Belt) по языку Rust.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Rust
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
amarao
Это перевод the rust book?
Я (частично) по ней учил Rust. Основная претензия — слишком много и подробно о том, "как оно работать не будет". Это полезно читать когда уже пытаешься починить свой код, но читать такое с нуля — очень трудно. Я бы даже сказал, это почти stopper. Вместо этого я бы предпочёл больше объяснений "как надо" (без пространного "как не надо").
IMnEpaTOP
Нет, это перевод The Rust Programming Language от издательства No Starch Press (в том смысле, что это перевод конкретного печатного издания, а не абстрактного набора текстов с сайта на какой-то конкретный момент).
amarao
как это нет, когда да?
https://doc.rust-lang.org/book/ — ссылка на https://nostarch.com/Rust2018.
IMnEpaTOP
the rust book это в общем смысле обновляемый сайт. Мы не делали его аналог и не предлагаем его распечатку. Мы взяли за основу определённое печатное издание, которое и является в общем смысле книгой.
Т.е. формально вы наверно можете в веб-архиве найти такую копию сайта, содержание которой будет почти полностью соответствовать названной книге. Но вообще это не идентичные сущности. Тем более в данный момент времени.
amarao
Вы что-то странное говорите.
Автор сказал — дословно —
Короче, вы перевели The Rust book на русский. Молодцы. Зачем теперь делать вид, что это не The Rust book?
Siemargl
Я приводил пример, когда на сайте растбука нашлись два противоречащих примера для разных версий языка (видимо), причем одна — непонятно какая.
amarao
Ну да, в текст вносятся изменения. Это нормально.
Печатный текст вовсе не является святым граалем в котором нет опечаток, и вполне может быть, что на сайте сейчас текст без бага, а в бумажной книге — с багом. Может быть и наоборот.
Siemargl
Опечатки и errata для книги это одно, а вот меняющийся язык это другое.
Собственно, потому ссылаться на сайт это так себе идея (безотносительно Раста).
Ссылки меняются, пропадают, их удаляют с выходом новых версий. Меня Майкрософтовские сайты просто бесят этим.
amarao
С учётом, что сайт хранится в git'е, нет разницы между редакциями сайта (коммитами) и редакциями книги (с опечатками, исправлениями и обновлениями). И там и там есть понятие "редакция", и там и там есть понятие "самая свежая редакция". Просто в случае редакций из мёртвых деревьев это более медленный процесс, вот и всё.
sw0rl0k
Но вы перевели книгу, которая является распечаткой (может с дополнительной редактурой, я не сравнивал) сайта. Даже в предисловии об этом сказано почти прямым текстом.
HaVok
С другой стороны, книга ссылается на русский перевод :)
doc.rust-lang.org/stable/book/appendix-06-translation.html
hrls
В книге тоже такие вставки в листинг как (1), (2)? Может оформлять комментарии лучше как //комментарии ?
sw0rl0k
В книге чуть по-другому. Достаточно удобно, на мой взгляд.
scg
Хотел купить электронную версию… Но зачем вам для этого нужны: моя фамилия, телефон и е-маил? Вы книги продаете или персональные данные собираете? И почему я не могу купить вашу книгу в другом месте, где у меня уже есть аккаунт? На том же Литрес я могу купить книгу только по e-mail без регистрации.
ph_piter Автор
Это шаблон сайта такой, только переезжать на другой...
Можете указать только e-mail верный. Ваши данные третьим лицам не поступят.
anonymous
я тоже начал учить Rust по книге той Rust book, для меня было очень сложно и я все бросил, особенно когда дошел до того как работает random, для меня раст… сложный. можете рекомендовать другие книги?
amarao
Это не Rust сложный, это точка зрения автора книги немного специфичная. Ему хочется показать как хорошо Rust плохой код отлавливает, а начинающим хочется понимать как писать хороший код.
В целом — я бы предложил больше практики, и вот эти лекции. Они сложные, но более "конструктивные". https://www.youtube.com/watch?v=Oy_VYovfWyo&list=PLlb7e2G7aSpTfhiECYNI2EZ1uAluUqE_e
anonymous
Спасибо большое!
anonymous
хотел поставить плюсик, пишет что нет кармы, извини
ardraeiss
Спасибо
ArsenAbakarov
Изучаю по doc.rust-lang.ru/book, иногда сверяясь с англ. версией
Так же просмотрел курсы www.youtube.com/watch?v=Oy_VYovfWyo&list=PLlb7e2G7aSpTfhiECYNI2EZ1uAluUqE_e
Покупать книгу смысла не вижу, так как язык относительно молодой и книга может стать неактуальной
amarao
Зависит от знания языка. Мелкие изменения вы обнаружите и так (например, свежая версия разрешила массивы больше 32 элементов), а крупные — это будет новая редакция языка, и вы можете остаться на rust2018, не прыгая в новое море непонятного.
Rust уже достаточно стабильный язык, чтобы можно было брать книгу двухлетней давности не бояться, что с тех пор "Улиточка, возвращаем всё обратно" три раза.
blandger
Мда… может лучше было взять «Rust in Action», как вам советовали на почту, больше бы пользы было, т.к. русский перевод Раст-бука и так существует ?? Сейчас книга RiA уже готова в «последней версии MEAP».
konoplinovich
А меня вот, если честно, удивляет, почему для электронной книги не запустить по верстке скрипт, который раскрасит код? Это занимает минут десять. Понятно, что физическая версия в одну краску печатается, но электронная-то чем заслужила такое? Это природная лень или есть причины?
ph_piter Автор
Подскажите пожалуйста этот скрипт для InDesign.
konoplinovich
Простите, был напуган. И был уверен, чтот уж у вас он точно есть.
Последний раз пользовался вот таким: github.com/baltpeter/id-simple-syntax-highlighter. Еще делал через GREP styles, но это практически то же самое. И даже в этом случае, код получается более выразительным и понятным.
Или, теоретически, можно взять какой-то хайлайтер поудобнее, с поддержкой языков, и при помощи скрипта скармливать ему куски кода, чтоб он их покрасил. Давно хочу сделать такую штуку, но пока не понимаю как.
konoplinovich
В целом, я даже готов сделать вам стиль с грепами под rust для примера.
distrik
PayPal отклоняет операцию покупки:
«Эта операция отклонена, так как она не соответствует требованиям законодательства.»
ph_piter Автор
www.fontanka.ru/2020/07/31/69395993
Propieller
Изучение Rust по online-документации (как оригинальной, так и по переводу) — вполне нормальное явление. Rust меняется, online-документация меняется. Книга по Rust, да еще с упором на то, что компилятор не пропустит — слишком быстро устареет.
Но по Rust можно издать более интересную книгу — The Rustonomicon. The Dark Arts of Unsafe Rust или она же в переводе. Во всяком случае, вот ее я бы купил.