Эта статья была впервые опубликована вОфициальный сайт Луожу, добро пожаловать на станцию.
Угадай числовой код игры вguessing_game, добро пожаловать на клонирование.
Эта статья является третьей в серии Pomodoro Rust, которая на данный момент включает:
За последние два дня Луожу был опрыскан слишком большим количеством овощей, а статьи все еще публикуются повсюду😭. Я извиняюсь перед всеми здесь. Смысл моих извинений в том, что я не должен отправлять его в различные группы переднего плана для разоблачения. Я все равно буду настаивать на написании этой заметки и настаивать на том, чтобы разослать ее для всеобщего обозрения. —— Восхитительно, но увлекательно
После двух дней размышлений и объяснений здоровяка я понял, что настоящий смысл таков:Лучший способ освоить новый навык — это иметь интерес, место, где можно поделиться, и сеть единомышленников.В конце статьи есть QR-код для группы обмена Rust Добро пожаловать к нам.
Для того, чтобы облегчить всем обучение, нужно бежать (
cargo run
) Я делал скриншоты гифок везде, где видел эффект. Если вам это нравится, вы можете поощрить Луо Чжу 👍🏻.
окрестности
ржавчина:
1.50.0 (cb75ad5db 2021-02-10)
груз:
1.50.0 (f04e7fab7 2021-02-04)
rand
ящик:0.8.3
vscode plugins
- Better TOML
- CodeLLDB
- crates
- rust-analyzer
о-мой-зш тема: ys
Программа для скриншотов:Kap
Обработка пользовательского ввода
Первая часть игры в угадайку попросит пользователя ввести приведенный выше код:
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
println!("You guessed: {}", guess);
}
декларативный импорт
-
use std::io;
Заявление: поставить стандартную библиотеку (std)серединаio
Модули импортируются в текущую область. - По умолчанию Rust автоматически вводит в область видимости каждой программы записи в модулях prelude, которые содержат небольшой набор довольно распространенных типов. Если нужного вам модуля нет в предварительно импортированном модуле, то мы должны использовать
use
Оператор для явного объявления импорта. -
std::id
Библиотека содержит множество полезных функций, которые мы можем использовать для получения входных данных пользователя.
Используйте переменные для хранения значений
-
let mut guess
: создаетguess
переменные переменные, переменные неизменяемы по умолчанию,mut
Ключевое слово представляет изменяемую переменную, а концепции переменных и изменчивости находятся вЗаметки по изучению грамматики Rust для двух ПомидоровЕсть вводные. -
String::new
: возвращает новыйString
Пример-
::
грамматика показываетnew
даString
ассоциированная функция типа — известная в других языках как статический метод -
String
Это строковый тип в стандартной библиотеке, который использует внутреннюю кодировку UTF-8 и может увеличивать свой размер по мере необходимости.
-
-
io::stdin
:возвращениеstd::io::Stdin
, который используется в качестве дескриптора для обработки стандартного ввода в терминале. -
.read_line
Метод используется для получения пользовательского ввода.-
read_line
параметры&mut guess
Представляет ссылку на изменяемую переменную. -
&
Указывает, что текущий параметр является ссылкой, которая также существует в Go. Потому что большинство этих языков программирования системного уровня предоставляют разрешения на манипулирование памятью, а JavaScript не дает пользователям этих функций.
-
Дескрипторы: дескрипторы широко использовались в управлении памятью операционных систем в 1980-х годах, таких как дескрипторы Mac OS и Windows. Файловые дескрипторы в системах Unix тоже в основном являются дескрипторами. Как и в других средах рабочего стола, Windows API широко использует дескрипторы для идентификации объектов в системе и для создания каналов связи между операционной системой и пользовательским пространством. Например, форма на рабочем столе состоит из
HWND
Дескриптор типа для идентификации.
Обработка возможных сбоев
Давайте сначала посмотрим на обработку ошибок в Go:
func main() {
result,err := getResult();
if err != nil {
panic(err)
}
fmt.Printf("The result is %s", result);
}
func getResult() string,(string, err error) {
// some codes
}
Как правило, если функция может сообщить об ошибке, она в то же время вернет результат.error
. Это не специфично для Go, это то же самое в Rust, просто реализовано по-другому.
В предыдущем кодеread_line
сохранит пользовательский ввод в строку, которую мы передали, но в то же время он также возвращаетio::Result
ценность. В стандартной библиотеке Rust вы можете найти множествоResult
именованные типы, которые обычно находятся в различных подмодуляхResult
конкретные версии дженериков, как здесьio::Result
.
Result
является типом перечисления. Тип перечисления состоит из фиксированного ряда значений, которые называются вариантами перечисления. (Как мы все знаем, в Javascript нет типа перечисления)
заResult
, она имеетOk
иErr
два варианта. один из нихOk
Варианты указывают на то, что текущая операция выполнена успешно, а результирующее значение создается кодом. Соответственно,Err
Варианты указывают на сбой текущей операции с указанием конкретной причины сбоя.
expect
даResult
Один из серии методов для значений типа. еслиio::Result
Стоимость экземпляраErr
,Такexpect
Метод прервет текущую программу и отобразит входящий строковый параметр.
read_line
Метод может возвращатьErr
результат.Соответственно, если
io::Result
Стоимость экземпляраOk
,Такexpect
будет извлеченOk
и вернуть его пользователю в качестве результата. В нашем случае это значение — количество байтов пользовательского ввода.
даже если мы не вызываем в конце оператораexpect
, эту программу также можно скомпилировать, но в процессе компиляции вы увидите предупреждающее сообщение, показанное ниже. Это в основном то же самое, что и Go.
Компилятор Rust напоминает намread_line
метод возвращенResult
Значение еще не обработано, что обычно означает, что наша программа не обработала возможную ошибку.
Самый правильный способ избавиться от предупреждения — это, конечно, написать соответствующий код обработки ошибок.Для простоты мы решили использоватьexpect
метод, который приведет к прямому завершению программы и выходу при возникновении ошибки.
пройти черезprintln!
Заполнитель в выводе соответствует значению
println!("You guessed: {}", guess);
Пользовательский ввод, который мы сохранили, можно распечатать. Первым параметром этого вызова макроса является строка, используемая для форматирования, а фигурные скобки в строке{}
является заполнителем.
узнать по аналогии:
{}
Поскольку заполнители не являются специфическими для Rust, в оболочках есть похожие приложения, о которых я знаю, напримерls | xargs -I {} tar zcvfm {}.tar.bz {}
Этот скрипт означает, чтоls
Выходные значения передаются в последующие команды одна за другой,{}
Это заполнитель для получения значения, переданного конвейером. Кстати, этот скрипт у меня есть вtuya-panel-demoЕсть польза. Выкопайте здесь яму, и последует серия руководств по сценариям оболочки.
попробуй запустить код
Теперь воспользуемсяcargo run
команда, чтобы попробовать запустить этот код:
сгенерировать секретный номер
Решив получение числа, введенного пользователем, нам нужно сгенерировать секретный номер, чтобы игрок мог его угадать. Чтобы обеспечить определенную степень играбельности и сделать каждую игру уникальной, этот сгенерированный секретный номер будет случайным.
Представить ранд пакет
Вместо того, чтобы встраивать аналогичную функциональность генерации случайных чисел в стандартную библиотеку, команда Rust решила сделать ее доступной для пользователей в виде крейта rand.
Примечание. Крейт в Rust представляет собой набор файлов с исходным кодом. Проект, который мы сейчас создаем, представляет собой бинарный крейт для создания исполняемых программ, и мы представляем
rand
Пакет — это библиотечный пакет (libray crate, code package), используемый для повторного использования функций.
Чтобы использовать третью сторонуcrate
, мы должныCargo.toml
Добавьте зависимости в файл:
...
[dependencies]
rand = "0.3.14"
существуетCargo.toml
В файле все, от одного заголовка до другого, относится к одной и той же области. здесь[denpendencies]
Область используется для объявления всех зависимостей и их номеров версий, которые необходимо использовать в проекте.
Номер версии в книге относительно старый, и плагин vscode crates подсказывает версию❌
Мы можем щелкнуть любую версию, чтобы выбрать эту версию в качестве версии, от которой мы зависим, и щелкнуть документы в скобках, чтобы перейти к документации библиотеки.
Давайте пересоберем проект напрямую, без изменения кода:
Теперь, когда наша программа имеет внешнюю зависимость, Cargo может получить информацию о последней версии всех доступных библиотек из реестра, обычно изcartes.ioСкопировано сверху.
cates.io в экосистеме Rust — это веб-сайт, на котором люди могут делиться различными проектами Rust с открытым исходным кодом.
Теперь, если вы не внесли никаких изменений, немедленно перезапуститеcargo build
, то появится только сообщение Готово. Cargo автоматически проанализирует, что в данный момент загружено или скомпилировано, и пропустит шаги, которые не нужно повторять.
генерировать случайное число
+ use rand::Rng;
use std::io;
fn main() {
println!("Guess the number!");
+ let secret_number = rand::thread_rng().gen_range(1..101);
+ println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
println!("You guessed: {}", guess);
}
- Мы добавили дополнительную строку
use
Заявление:use rand::Rng
. Здесь Rng — трейт, определяющий набор методов, которые должен реализовать генератор случайных чисел. Чтобы использовать эти методы, нам нужно явно ввести его в текущую область видимости. -
rand::thread_rng()
вернет определенный генератор случайных чисел. Затем мы вызываем генераторgen_range
метод. -
gen_range
Метод был только что введен в область примененияRng
trait, который принимает тип Range (1..100
) в качестве аргумента и генерирует случайное число в диапазоне, предшествующем обоим.
Напоминание: в книге
gen_range
Он получает два параметра, не удивляйтесь, когда прочтете, версия другая.
Стоит отметить, что,
gen_range
Сгенерированное пространство случайных чисел содержит нижнюю границу, но не содержит верхнюю границу. —— Как человек, то же самое верно, должен быть практический результат, но не устанавливайте для себя верхний предел.
Со сторонними пакетами неизбежно часто проверять документацию. Вы можете перейти к официальной документации, чтобы увидеть, нет никаких проблем. Но вы также можете использоватьcargo doc --open
Команда создает локальную документацию по всем зависимостям и автоматически открывает документацию в вашем браузере, чтобы вы могли ознакомиться с ней:
В приведенном выше коде мы распечатываем секретный номер только для отладки, а затем удаляем этот код.
Сравнение угаданных чисел с секретными числами
Теперь у нас есть случайно сгенерированный секретный номер и предполагаемый номер, введенный пользователем. Далее мы сравним два числа.
use rand::Rng;
+ use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..101);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
println!("You guessed: {}", guess);
+ match guess.cmp(&secret_number) {
+ Ordering::Less => println!("Too small!"),
+ Ordering::Greater => println!("Too big!"),
+ Ordering::Equal => println!("You win!"),
+ }
}
- Мы импортировали из стандартной библиотеки
std::cmp::Ordering
тип. Как и Result, Ordering также является типом перечисления, который имеетLess
,Greater
,Equal
Эти 3 варианта. (cmp для сравнения) -
match
Выражение состоит из нескольких ветвей (ветвей), каждая из которых содержит шаблон для сопоставления и соответствующий код, который должен быть выполнен после успешного совпадения.
в ржавчине
match
Структуры и шаблоны представляют собой очень мощный класс инструментов, обеспечивающих возможность выполнения различного кода в зависимости от различных условий (аналогично другим языкам).switch
) и быть в состоянии убедиться, что вы не пропустите ни одного условия ветвления.
Приведенный выше код в настоящее время не может скомпилироваться:
Причина ошибки в том, что мы сохранилиguess
Тип переменной — String, секретное число — целочисленный тип, и разные типы не могут сопоставляться (в статических языках нет неявного преобразования типов).
Чтобы операция сравнения работала правильно, нам нужно преобразовать ввод, прочитанный в программе, из типа String в числовой тип:
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..101);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
+ let guess: i32 = guess.trim().parse().expect("Please type a number");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
-
Здесь мы создаем новую неизменяемую переменную
guess
, у которого то же имя, что и у предыдущего, но это допустимо, и Rust позволяет новой переменной с тем же именем скрывать значение старой переменной. Эта функция часто используется в сценариях, где необходимо преобразовать типы значений, и в этом случае позволяет нам повторно использовать предположение имени переменной без необходимости создавать другое имя, такое как Guess_str . -
guess.trim()
заключается в удалении всех пробелов в начале и в конце. -
нить
parse
Метод попытается преобразовать текущую строку в числовое значение. Поскольку этот метод может обрабатывать различные числовые типы, нам нужно передать операторlet guess: i32
для явного объявления нужных нам числовых типов.
Теперь снова запустим нашу программу:
Игра в основном сформирована, но игрок может сделать только одно предположение, чего явно недостаточно.Далее мы добавим цикл для улучшения игры.
Используйте циклы для реализации нескольких предположений
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
// `..` 语法是标准库中提供的 Range
let secret_number = rand::thread_rng().gen_range(1..101);
+ loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
let guess: i32 = guess.trim().parse().expect("Please type a number");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
+ Ordering::Equal => {
+ println!("You win!");
+ break;
+ }
}
+ }
}
- Мы переместили все после того, как предложили пользователю принять угадывающее решение
loop
середина. В ржавчинеloop
ключевое слово создает бесконечный цикл. - Мы также добавили
break
оператор, чтобы игрок мог нормально выйти из игры после правильного угадывания числа.
Запустите программу, игрок правильно угадает число, после вывода **You Win!** программа завершит работу:
Обработка недопустимого ввода
в преобразованииguess
Строкаnumber
тип, мы используемexpect("Please type a number")
Для обработки возможных ошибок мы пытаемся ввести не-number
Запустите значение типа:
Если пользователь хочет перезапустить игру, ему нужно перезапустить программу, что, очевидно, плохо для пользователя. Чтобы улучшить играбельность игры, мы можем просто игнорировать угадывание, когда пользователь вводит нечисловые данные, и позволить пользователю продолжать угадывать, тем самым избегая сбоя программы.
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
// `..` 语法是标准库中提供的 Range
let secret_number = rand::thread_rng().gen_range(1..101);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failded to read line");
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please type a number!");
continue;
}
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
- мы использовали
match
выражение для замены предыдущегоexpect
метод, который является полезным инструментом для нас, чтобы иметь дело с плохим поведением. -
parse
вернет тип результата иResult
тип включаетOk
иErr
два варианта.
В общем, давайте запустим этот проект и попробуем:
История не окончена
Оглядевшись, я не нашел группу по обмену Rust, поэтому создал ее сам.
У Tuya Smart большое количество качественных НС, присоединяться могут все желающие, вам нужно добавить меня в WeChat yang_jun_ning, или отправить свое резюме прямо на почтуyoungjuning@aliyun.com
После возобновления работы после окончания года нет возможности обновляться с такой высокой частотой.Надеюсь, все поставят лайк и призовут меня к настойчивости!