Как сделать торговый автомат в раст

Добавил пользователь Алексей Ф.
Обновлено: 04.10.2024

Последнее время я много размышлял о шаблонах проектирования и приемах, которые мы используем в программировании. Это и в самом деле прекрасно — начать исследовать проект и видеть знакомые шаблоны и стили, которые ты уже не раз встречал. Это облегчает понимание проекта и дает возможность ускорить работу.

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

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

На протяжении всей статьи вы можете запускать все примеры на Rust Playground, я обычно использую Nightly версию по привычке.

В Интернете существует огромное количество ресурсов и тематических статей о конечных автоматах. Более того, существует множество их реализаций.

Итак, конечный автомат — это любой "автомат", который имеет набор "состояний" и "переходов" между ними.

Когда мы говорим о автомате, мы имеем в виду абстрактную концепцию того, что что-то делает. Например, ваша функция "Привет, мир!" — автомат. Он включается и в конечном итоге производит того, что мы ожидаем. Также ведут себя и модели, с помощью которых вы взаимодействуете с вашей базой данных. Мы рассмотрим наш базовый автомат как обыкновенную структуру, которую можно создать и уничтожить.

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

Главный вывод из этого состоит в том, что ни одно состояние не имеет никакой информации, которая относится к другим состояниям. Состояние "заполнение" не заботится о том, насколько долго автомат был в состоянии "ожидание". Состояние "выполнено" не заботится о степени заполненности бутылок. Каждое состояние имеет строго определенные обязанности и проблемы. Естественный способ рассмотрения этих вариантов — enum .

Использование enum таким образом означает, что состояния взаимоисключающие, вы можете находится только в одном состоянии в конкретный момент времени. "Fat enums" в Rust позволяют каждому состоянию хранить в себе необходимую информацию. До тех пор, пока наше определение объявлено таким образом, всё в полном порядке.

Но существует одна маленькая проблема. Когда мы описывали наш автомат выше, мы описали три перехода между состояниями: Ожидание → Заполнение , Заполнение → Выполнено и Выполнено → Ожидание . Мы не учитывали Ожидание → Выполнено или Выполнено → Заполнение , они просто не имеют смысла!

Это подводит нас к идеи о переходах. Одна из самых приятных особенностей истинного конечного автомата — это то, что нам никогда не придется заботиться о таких переходах как Выполнено -> Заполнение . Шаблон проектирования конечного автомата должен обеспечить невозможность такого перехода. В идеале это произойдет еще до того, как мы запустим наш автомат — в момент компиляции программы.

Давайте еще раз рассмотрим наши переходы в диаграмме:

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

Это означает, что переход между состоянием "Ожидание" в состояние "Заполнение" должен удовлетворять определенному правилу. В нашем примере это правило может иметь вид "Бутылка установлена на место". В случае TCP потока это будет "Мы получили FIN-пакет", что означает, что нам нужно завершить передачу, закрыв поток.

Теперь, когда мы знаем, что такое конечный автомат, как нам реализовать его в Rust? Для начала, давайте подумаем о том, чего мы хотим.

В идеале, мы хотели бы увидеть следующие характеристики:

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

С такой мощной и гибкой системой типов, как в Rust, мы должны быть способны реализовать это. Истина такова: есть несколько способов, каждый из которых предлагает нам определенные преимущества и преподает нам урок.

Вторая попытка с Enum

Как мы уже знаем, самым естественным способом являются enum , но мы уже замечали, что не можем запрещать переходы в этом случае. Но можем ли мы всего лишь обернуть их в структуру? Конечно можем! Взгляните:

На первый взгляд все в порядке. Но замечаете ли вы некоторые проблемы?

  • Ошибка из-за запрещенного перехода произойдет во время выполнения, что ужасно!
  • Это предотвращает только неверные переходы снаружи модуля, потому что приватные поля могут быть свободно изменены изнутри модуля. Например, state_machine.state = State::Done безусловно действует внутри модуля.
  • Каждая наша функция, которая работает с состояниями, должна иметь match выражение.

Однако этот подход имеет и некоторые преимущества:

  • Память, требуемая для представления конечного автомата — размер наибольшего состояния. Это результат использования fat enum , размер которого соответствует размеру наибольшего варианта.
  • Вся память выделяется в стеке, куча не задействована.
  • Переходы между состояниями имеют определенные правила. Это или работает или выдает ошибку!

Сейчас вы можете подумать: "Позвольте, Hoverbear, вы же можете обернуть вывод to_filling() в Result или добавить в enum опцию InvalidState !". Но давайте посмотрим правде в глаза: это не намного улучшит ситуацию, если вообще улучшит. Даже если мы избавимся от сбоев во время выполнения, нам все равно придется иметь дело с неуклюжими выражениями сопоставления с образцом, и наши ошибки по прежнему будут обнаружены только после запуска программы! Фу! Мы можем сделать лучше, я обещаю.

Так что продолжим поиски!

Структуры с переходами

Что, если мы просто будем использовать набор структур? Мы можем определить для каждой из них набор типажей, общих для каждого состояния. Мы можем использовать специальные функции, превращающие один тип в другой! Как это будет выглядеть?

Черт возьми, сколько кода! Таким образом, идея заключалась в том, что все состояния имеют как общие для всех состояний данные, так и свои собственные. Как вы можете заметить, функция to_filling() поглотит состояние "Ожидание" и совершит переход в состояние "Заполнение". Давайте кратко изложим все:

  • Ошибки переходов определяются во время компиляции! Например, вы не сможете даже создать состояние "Заполнение" случайно без предварительного создания состояния "Ожидание". (На самом деле вы можете, но это не относится к делу)
  • Обязателен переход между состояниями.
  • Во время перехода между состояниями старое значение поглощается вместо простого изменения.
    Правда, мы могли сделать то же и с помощью enum из нашей первой попытки.
  • Нам не нужны постоянные match .
  • Потребление памяти по прежнему не вызывает нареканий. Нам требуется только размер текущего
    состояния.

Существуют и некоторые недостатки:

  • Много повторяющегося кода. Вы должны определять одни и те же функции и типажи для каждой структуры.
  • Не всегда понятно, какие значения общие для состояний, а какие принадлежат только одному. Обновление кода в будущем может стоить вам дорого.
  • Так как размер состояния непостоянен, мы должны обернуть их в enum как и раньше, чтобы мы могли использовать конечный автомат как один из компонентов более сложной системы. Вот как это может выглядеть:

Как вы можете заметить, это не очень удобно. Мы все ближе к тому, чего хотим. Идея перехода между определенными типами кажется большим шагом вперед! Прежде чем мы попробуем что-нибудь совершенно иное, давайте поговорим о том, как изменить наш пример, который может упростить дальнейшие размышления.

Стандартная библиотека Rust предоставляет два очень важных типажа: From и Into , которые чрезвычайно полезны и заслуживают упоминания. Важно заметить, что реализация одного из них автоматически реализует другой. В целом реализация From предпочтительнее, так как она немного более гибкая. Мы можем реализовать их очень легко для нашего предыдущего примера:

Это не только дает нам общую функцию перехода, но еще и гораздо проще для чтения, когда встречаешь такое в исходном коде! Это уменьшает психологическую нагрузку и облегчает восприятие читателям. Вместо реализации собственных функций мы используем уже существующий шаблон. Основание наших шаблонов на базе уже существующих — отличное решение.

Так что это круто, на как нам справится с раздражающим повторением кода и shared_value повсюду? Давайте изучим еще немного!

Почти идеально

Сейчас мы соберем вместе уроки и идеи из первых двух способов, добавим немного новых идей, и получим нечто более приятное. Суть этого метода заключается в использовании силы обобщенных типов. Давайте рассмотрим довольно базовую структуру:

Мы можем продолжить и реализовать From для некоторых определенных вариантов, примерно вот так:

Определение исходного состояния автомата выглядит так:

А как же выглядит смена состояний? Вот так:

В случае, если вы делаете это внутри функции, сигнатура которой ограничивает выходной тип:

Итак, что же нам дает такой подход?

По прежнему есть недостатки:

  • Наши реализации From страдают от некоторой "захламленности типами". Однако, это достаточно мелкое неудобство.
  • Каждый BottleFillingMachine имеет разный размер, поэтому мы по прежнему должны использовать enum . И все же это не является серьезным недостатком из-за нашей структуры.

Можете поиграться с этим примером здесь

Грязные отношения с родителями

Примечание переводчика: перевод этого заголовка, любезно предоставленный Google Translator, настолько великолепен, что я предпочел оставить его именно таким.

Как же мы можем организовать родительскую структуру для хранения состояния конечного автомата без жутких проблем со взаимодействием? Ну, это откатит нас назад к первой идее с enum .

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

На данный момент ваша первая реакция скорее всего "Черт, Hoverbear, посмотри на эти длинные, ужасные объявления типов". Вы совершенно правы! Честно говоря, они действительно длинные, но я выбирал максимально понятные названия типов! Вы можете использовать все ваши любимые аббревиатуры и псевдонимы в вашем коде.
Смотрите!

Несмотря на то, что это несколько менее удобно и приятно для работы, мы по прежнему имеем обеспеченные системой типов переходы и все гарантии, которые приходят с ними.

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

Или вы можете просто вызвать panic!() , если действительно этого хотите. Но если вы хотите просто panic 'овать, то почему бы не использовать самый первый подход?

Вы можете увидеть полностью рабочий пример здесь

Это именно тот случай, когда примеры не бывают лишними. Так что я собрал пару рабочих примеров ниже и снабдил их комментариями.

Три состояния, два перехода

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

Если вы следили за записями в моем блоге в последнее время, вы, возможно, знаете, что я предпочитаю писать о Raft. Именно Raft, а также общение с @argorak подтолкнули меня к проведению этого исследования.

Raft несколько сложнее предыдущих примеров, потому что переходы между состояниями не линейны, как A->B->C . Вот диаграмма состояний и переходов для этого конечного автомата.

Я видел интересный комментарий от I-impv с Reddit, который показывал подход, основанный на наших предыдущих примерах. Вот что он сказал:

  • Я также смоделировал входные данные конечного автомата. Таким образом вы можете моделировать ваши переходы как сопоставление по (Состояние, Событие), где каждая недействительная комбинация обрабатывается шаблоном по умолчанию.
  • Вместо исключений при недействительных переходах я использовал состояние "Сбой", так что каждая недействительная комбинация переводила автомат в это состояние.

Мне в самом деле нравится идея представлять входные данные в переходах!

Rust позволяет нам представлять конечные автоматы красиво и удобно. В идеале мы могли бы создать enum с ограниченными переходами между вариантами, но это невозможно. Вместо этого мы можем использовать мощь обобщенных типов и систему владения, чтобы создать нечто выразительное, безопасное и понятное.

Если у вас есть любые комментарии или предложения по поводу этой статьи, я предлагаю вам посмотреть на нижний колонтитул для получения контактов. Я также тусуюсь в IRC Mozilla под ником Hoverbear.

Примечание переводчика:
Автор оригинала: Andrew Hobden
Ссылка на оригинал

Последнее время я много размышлял о шаблонах проектирования и приемах, которые мы используем в программировании. Это и в самом деле прекрасно — начать исследовать проект и видеть знакомые шаблоны и стили, которые ты уже не раз встречал. Это облегчает понимание проекта и дает возможность ускорить работу.

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

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

На протяжении всей статьи вы можете запускать все примеры на Rust Playground, я обычно использую Nightly версию по привычке.

В Интернете существует огромное количество ресурсов и тематических статей о конечных автоматах. Более того, существует множество их реализаций.

Итак, конечный автомат — это любой "автомат", который имеет набор "состояний" и "переходов" между ними.

Когда мы говорим о автомате, мы имеем в виду абстрактную концепцию того, что что-то делает. Например, ваша функция "Привет, мир!" — автомат. Он включается и в конечном итоге производит того, что мы ожидаем. Также ведут себя и модели, с помощью которых вы взаимодействуете с вашей базой данных. Мы рассмотрим наш базовый автомат как обыкновенную структуру, которую можно создать и уничтожить.

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

Главный вывод из этого состоит в том, что ни одно состояние не имеет никакой информации, которая относится к другим состояниям. Состояние "заполнение" не заботится о том, насколько долго автомат был в состоянии "ожидание". Состояние "выполнено" не заботится о степени заполненности бутылок. Каждое состояние имеет строго определенные обязанности и проблемы. Естественный способ рассмотрения этих вариантов — enum .

Использование enum таким образом означает, что состояния взаимоисключающие, вы можете находится только в одном состоянии в конкретный момент времени. "Fat enums" в Rust позволяют каждому состоянию хранить в себе необходимую информацию. До тех пор, пока наше определение объявлено таким образом, всё в полном порядке.

Но существует одна маленькая проблема. Когда мы описывали наш автомат выше, мы описали три перехода между состояниями: Ожидание → Заполнение , Заполнение → Выполнено и Выполнено → Ожидание . Мы не учитывали Ожидание → Выполнено или Выполнено → Заполнение , они просто не имеют смысла!

Это подводит нас к идеи о переходах. Одна из самых приятных особенностей истинного конечного автомата — это то, что нам никогда не придется заботиться о таких переходах как Выполнено -> Заполнение . Шаблон проектирования конечного автомата должен обеспечить невозможность такого перехода. В идеале это произойдет еще до того, как мы запустим наш автомат — в момент компиляции программы.

Давайте еще раз рассмотрим наши переходы в диаграмме:

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

Это означает, что переход между состоянием "Ожидание" в состояние "Заполнение" должен удовлетворять определенному правилу. В нашем примере это правило может иметь вид "Бутылка установлена на место". В случае TCP потока это будет "Мы получили FIN-пакет", что означает, что нам нужно завершить передачу, закрыв поток.

Теперь, когда мы знаем, что такое конечный автомат, как нам реализовать его в Rust? Для начала, давайте подумаем о том, чего мы хотим.

В идеале, мы хотели бы увидеть следующие характеристики:

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

С такой мощной и гибкой системой типов, как в Rust, мы должны быть способны реализовать это. Истина такова: есть несколько способов, каждый из которых предлагает нам определенные преимущества и преподает нам урок.

Вторая попытка с Enum

Как мы уже знаем, самым естественным способом являются enum , но мы уже замечали, что не можем запрещать переходы в этом случае. Но можем ли мы всего лишь обернуть их в структуру? Конечно можем! Взгляните:

На первый взгляд все в порядке. Но замечаете ли вы некоторые проблемы?

  • Ошибка из-за запрещенного перехода произойдет во время выполнения, что ужасно!
  • Это предотвращает только неверные переходы снаружи модуля, потому что приватные поля могут быть свободно изменены изнутри модуля. Например, state_machine.state = State::Done безусловно действует внутри модуля.
  • Каждая наша функция, которая работает с состояниями, должна иметь match выражение.

Однако этот подход имеет и некоторые преимущества:

  • Память, требуемая для представления конечного автомата — размер наибольшего состояния. Это результат использования fat enum , размер которого соответствует размеру наибольшего варианта.
  • Вся память выделяется в стеке, куча не задействована.
  • Переходы между состояниями имеют определенные правила. Это или работает или выдает ошибку!

Сейчас вы можете подумать: "Позвольте, Hoverbear, вы же можете обернуть вывод to_filling() в Result или добавить в enum опцию InvalidState !". Но давайте посмотрим правде в глаза: это не намного улучшит ситуацию, если вообще улучшит. Даже если мы избавимся от сбоев во время выполнения, нам все равно придется иметь дело с неуклюжими выражениями сопоставления с образцом, и наши ошибки по прежнему будут обнаружены только после запуска программы! Фу! Мы можем сделать лучше, я обещаю.

Так что продолжим поиски!

Структуры с переходами

Что, если мы просто будем использовать набор структур? Мы можем определить для каждой из них набор типажей, общих для каждого состояния. Мы можем использовать специальные функции, превращающие один тип в другой! Как это будет выглядеть?

Черт возьми, сколько кода! Таким образом, идея заключалась в том, что все состояния имеют как общие для всех состояний данные, так и свои собственные. Как вы можете заметить, функция to_filling() поглотит состояние "Ожидание" и совершит переход в состояние "Заполнение". Давайте кратко изложим все:

  • Ошибки переходов определяются во время компиляции! Например, вы не сможете даже создать состояние "Заполнение" случайно без предварительного создания состояния "Ожидание". (На самом деле вы можете, но это не относится к делу)
  • Обязателен переход между состояниями.
  • Во время перехода между состояниями старое значение поглощается вместо простого изменения.
    Правда, мы могли сделать то же и с помощью enum из нашей первой попытки.
  • Нам не нужны постоянные match .
  • Потребление памяти по прежнему не вызывает нареканий. Нам требуется только размер текущего
    состояния.

Существуют и некоторые недостатки:

  • Много повторяющегося кода. Вы должны определять одни и те же функции и типажи для каждой структуры.
  • Не всегда понятно, какие значения общие для состояний, а какие принадлежат только одному. Обновление кода в будущем может стоить вам дорого.
  • Так как размер состояния непостоянен, мы должны обернуть их в enum как и раньше, чтобы мы могли использовать конечный автомат как один из компонентов более сложной системы. Вот как это может выглядеть:

Как вы можете заметить, это не очень удобно. Мы все ближе к тому, чего хотим. Идея перехода между определенными типами кажется большим шагом вперед! Прежде чем мы попробуем что-нибудь совершенно иное, давайте поговорим о том, как изменить наш пример, который может упростить дальнейшие размышления.

Стандартная библиотека Rust предоставляет два очень важных типажа: From и Into , которые чрезвычайно полезны и заслуживают упоминания. Важно заметить, что реализация одного из них автоматически реализует другой. В целом реализация From предпочтительнее, так как она немного более гибкая. Мы можем реализовать их очень легко для нашего предыдущего примера:

Это не только дает нам общую функцию перехода, но еще и гораздо проще для чтения, когда встречаешь такое в исходном коде! Это уменьшает психологическую нагрузку и облегчает восприятие читателям. Вместо реализации собственных функций мы используем уже существующий шаблон. Основание наших шаблонов на базе уже существующих — отличное решение.

Так что это круто, на как нам справится с раздражающим повторением кода и shared_value повсюду? Давайте изучим еще немного!

Почти идеально

Сейчас мы соберем вместе уроки и идеи из первых двух способов, добавим немного новых идей, и получим нечто более приятное. Суть этого метода заключается в использовании силы обобщенных типов. Давайте рассмотрим довольно базовую структуру:

Мы можем продолжить и реализовать From для некоторых определенных вариантов, примерно вот так:

Определение исходного состояния автомата выглядит так:

А как же выглядит смена состояний? Вот так:

В случае, если вы делаете это внутри функции, сигнатура которой ограничивает выходной тип:

Итак, что же нам дает такой подход?

По прежнему есть недостатки:

  • Наши реализации From страдают от некоторой "захламленности типами". Однако, это достаточно мелкое неудобство.
  • Каждый BottleFillingMachine имеет разный размер, поэтому мы по прежнему должны использовать enum . И все же это не является серьезным недостатком из-за нашей структуры.

Можете поиграться с этим примером здесь

Грязные отношения с родителями

Примечание переводчика: перевод этого заголовка, любезно предоставленный Google Translator, настолько великолепен, что я предпочел оставить его именно таким.

Как же мы можем организовать родительскую структуру для хранения состояния конечного автомата без жутких проблем со взаимодействием? Ну, это откатит нас назад к первой идее с enum .

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

На данный момент ваша первая реакция скорее всего "Черт, Hoverbear, посмотри на эти длинные, ужасные объявления типов". Вы совершенно правы! Честно говоря, они действительно длинные, но я выбирал максимально понятные названия типов! Вы можете использовать все ваши любимые аббревиатуры и псевдонимы в вашем коде.
Смотрите!

Несмотря на то, что это несколько менее удобно и приятно для работы, мы по прежнему имеем обеспеченные системой типов переходы и все гарантии, которые приходят с ними.

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

Или вы можете просто вызвать panic!() , если действительно этого хотите. Но если вы хотите просто panic 'овать, то почему бы не использовать самый первый подход?

Вы можете увидеть полностью рабочий пример здесь

Это именно тот случай, когда примеры не бывают лишними. Так что я собрал пару рабочих примеров ниже и снабдил их комментариями.

Три состояния, два перехода

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

Если вы следили за записями в моем блоге в последнее время, вы, возможно, знаете, что я предпочитаю писать о Raft. Именно Raft, а также общение с @argorak подтолкнули меня к проведению этого исследования.

Raft несколько сложнее предыдущих примеров, потому что переходы между состояниями не линейны, как A->B->C . Вот диаграмма состояний и переходов для этого конечного автомата.

Я видел интересный комментарий от I-impv с Reddit, который показывал подход, основанный на наших предыдущих примерах. Вот что он сказал:

  • Я также смоделировал входные данные конечного автомата. Таким образом вы можете моделировать ваши переходы как сопоставление по (Состояние, Событие), где каждая недействительная комбинация обрабатывается шаблоном по умолчанию.
  • Вместо исключений при недействительных переходах я использовал состояние "Сбой", так что каждая недействительная комбинация переводила автомат в это состояние.

Мне в самом деле нравится идея представлять входные данные в переходах!

Rust позволяет нам представлять конечные автоматы красиво и удобно. В идеале мы могли бы создать enum с ограниченными переходами между вариантами, но это невозможно. Вместо этого мы можем использовать мощь обобщенных типов и систему владения, чтобы создать нечто выразительное, безопасное и понятное.

Если у вас есть любые комментарии или предложения по поводу этой статьи, я предлагаю вам посмотреть на нижний колонтитул для получения контактов. Я также тусуюсь в IRC Mozilla под ником Hoverbear.

Примечание переводчика:
Автор оригинала: Andrew Hobden
Ссылка на оригинал

лысик

И-так, у сервера произошёл вайп. Все игроки в бешенстве заходят на сервер, и начинают пытаться получить как можно больше ресурсов, что-бы побыстрее развиться. У меня же всё было иначе, я пытался убивать всех кого встречу и просто искать людей.

В общем для начала вам нужно побыстрее убраться с пляжа, и уйти куда подальше от него. По дороге собираем камушки, пеньки и рискованно ломаем бочки и собираем скрапа (потом он нам ещё понадобится).

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

Ищем место, где скалы смогут окружить ваш дом с одной или же несколько сторон, что-бы ваш дом был незаметнее.

Далее когда вы обустроились, вам обязательно нужно поставить шкаф, коль на вашей территории смогут строить дома и делать всё, что им захочется. Положите туда отчасть ресурсов (сколько необходимо), что бы дом не начал гнить и ломаться. Поставьте спальник. И тут задаётся вопрос, какие-же всё таки двери устанавливать в дом. Я конечно же люблю, и вам советую сразу ставить железные двери. А из замков, обязательно даже если очень мало ресов ставьте кодовой замок. Так же поставьте несколько спальников, что бы можно было справится сразу после смерти дома. И еще немало важный момент, как лучше прокачать стены. Стены прокачиваете до камня. И запомните, до железа в начале не качайте. Бродячие игроки заметят ваш дом, и подумают, что вы мажор и у вас там супер лут (калаши и.т.д), и попытаются вас зарейдить. Будет лучше, если у вас есть лишнее железо прокачать только пол и фундамент. Некоторые разумные люди пытаются сломать фундамент, что бы пролезть под ним и собрать весь ваш лут. Обязательно проверьте какой стороной стоят ваши стены и всё остальное, либо ваш дом опять-же будет легко зарейдить даже копьями. Так-же рекомендую для себя сделать деревянную броню которая даёт +15 защиты от снарядов, которая очень нужна в начальных войнах. Одежду делать необязательно, необходима только в том случае, если вы живёте в холодном биоме. Я её использую только для выпендросов. И забыл напомнить, если вы рисковый парень, то можете попробовать прохаживаться по айрдропам. Ибо если вы сможете взять с него лут, то вы будете реально красавчиком. Второй этап для вас всё ближе, и ближе!

Середина

целится

И-так, вы уже довольно нормально развились на старте. Середина будет очень сложным этапом для всех, яко там придётся катастрофически очень много крафтить, и добывать компоненты с бочек.

В этом этапе пытайтесь очень, очень много добывать скрапа (металлолома) из бочек. После того как вы добыли скрапа сразу крафтим первый верстак и исследовательский стол. Вслед за этим опять добываем много скрапа и изучаем в верстаке предметы. В первом верстаке за 75 скрапа может появиться рандомный чертёж.

Снизу изображён список предметов, которые могут выпасть с первого верстака:

серед

В первом верстаке, самые нужные вещи это: сачель (сумка с зарядом) c боб гранатами, Pipe Shotgun (пайпа), самодельная двухстволка, железная кирка и топор, револьвер, арбалет, гвоздемёт ну и ган-трап (на выбор). Все предметы из него изучать не нужно, это просто скрап на ветер. Ну может вы и скажете что некоторые предметы нужны. Да я согласен они не помешают, но нам первым делом нужны основные вещи. Задумайтесь зачем они вам нужны, если даже некоторые из них можно найти в ящиках и в айрдропах. В третьем этапе вы их можете изучить, если у вас будет достаточно много скрапа (если вам они вообще прям нужны).

После того как вы изучили основные чертежи из предшествующего верстака, вы можете приступать набирать скрап на крафт второго верстака. Этот верстак покажется вам очень необычным. В нём одно исследование стоит 300 скрапа. Это довольно много, но это того стоит. И в нём шмот уже получше чем в первом верстаке.

Снизу изображён список предметов, которые могут выпасть со второго верстака:

прев

Из него нам только нужны: патроны (картечь, 5.56, зажигательная-ракета), автомат-томпсона, смг, ледокол и самодельный топор, пешка (полуавтоматический пистолет), питон, весь сет брони из знаков, коффейная маска, шприц и автоматическая турель (на выбор). Из этого верстака нам требовательно нужны патроны и оружия. Из него будет полезны инструменты а именно: ледокол с которым будет добываться очень большое количество руды и других ископаемых, а с самодельным топором рубить деревья будет приносить большую добычу и с ним будет рубить намного приятней и проще. Броня из знаков, ну очень полезная штука! Нам она даёт 30 защиты от снарядов, что очень будет полезным в нечестных боях. А автоматическая турель будет нужна только в том случае, если в вашем доме много хорошого лута, а вот защиты от рейда никакой.

В этом этапе немало важно будет опять же добывать много ресурсов. Можете считать вы уже средне-развитый игрок в игре, и можете спокойно, с хорошей защитой и вооружённостью, путешествовать по заброшенным предприятиям. А именно по сферам, аэродромам, и ангарам. Можете уже рисковать и идти на айрдроп. Но не забываем про радиацию одевайте антирадиационный костюм, который защитит вас от излучения радиации. Можете атаковать странников на дороге (бомжов), ибо у них нет оружия и может оказаться довольно хороший лут. Не забываем осматриваться по сторонам, коль пройдет какой-нибудь калашист и убьёт вас.

Окончание

шлем

Что-ж начался самый окончательный третий этап. Теперь вы полностью развитый игрок. Будем считать начался информационный век. В этом этапе вы можете приступать и набирать 1200 скрапа на крафт третьего последнего верстака. Это очень долго… Вам нужно сломать около 300-ста бочек. Единственные способы как ускорить сбор скрапа это рейд и убивание бомжей. Рейдя дома часто игроки раста не заглядывают в исследовательские стола, в которых может ожидать джекпот. Я же максимум находил 1000 скрапа (хотя это и есть максимум в исследовательском столе). После того как вы набрали нужно количество скрапа вы можете приступать изучать предметы, но одно исследование стоит 1000 скрапа, и опять же вам придётся идти и доставать скрап.

Снизу изображён список предметов, которые могут выпасть со третьего верстака:

пули

И-так, после того как вы уже изучили все основные вещи с верстаков, вы можете приступать доставать еще скрапа на изучение желательных предметов, а именно: Прицелы, разные виды патрон и снарядов (Зажигательные, скоростные, разрывные), одежда и всякий строительный шмот.

После всех этапов + изучение всех предметов, вы можете начинать искать людей, что бы создать свой клан и получать большую прибыль ресурсов. Вы можете рейдить другие кланы. И воевать за свои районы. Теперь без страха вы можете ходить на айрдропы, и с спокойным сердцем можете начинать бахать вертолёты и танки. С них может выпасть пулемёты, патроны, калаши и другой нормальный шмот. Вы можете поджидать и убивать таких же игроков как вы, с калашами и топ бронёй.

Подведём итоги: вы норм развились, топ лут, топ дом, все чертежи, есть свой клан, имеются свои районы. И-так, я рассказал вам мои ошибки, и советы по игре, что лучше качать и.т.д. Скажу я честно, наиграл я в раст 150 часов. И попрошу вас пожалуйста не пишите, что это знают все игроки раста. Нет, это не так. Этот гайд был написан только для тех, кто вот-вот начал играть в раст и не может разобраться как развиваться.

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

Мы постоянно улучшаем сервис по вашим просьбам, а на любые вопросы ответит техподдержка и полный гайд по маркету

Описание принципа работы торговой площадки

выполняет функцию автоматизированного гаранта для обмена предметов продавцов покупателям на деньги покупателей. В отличии от обычной сделки вроде "ты мне кидай шмотку, а я тебе деньги" в 85% случаев одна из сторон не выполняет своих обязательств и кидает покупателя — наш маркет предоставляет 100% гарантию как получения денег за проданный предмет, так и получение купленного предмета покупателем.

Читайте также: