2. 猜数字游戏

目录

创建一个新项目

处理一次猜测

生成一个秘密数字

使用crate来增加更多功能

Cargo.lock 文件确保构建是可重现的

更新crate到一个新版本

生成一个随机数

比较猜测的数字和神秘数字

使用循环来允许多次猜测

猜测正确后退出

处理无效输入

最终代码


程序会随机生成一个 1 到 100 之间的整数。接着它会提示玩家猜一个数并输入,然后指出猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。

创建一个新项目

projects目录创建一个新项目:

$ cargo new guessing_game
$ cd guessing_game

cargon run 编译运行,打印出:

PS D:\code\Rust\project\guessing_game> cargo run
   Compiling guessing_game v0.1.0 (D:\code\Rust\project\guessing_game)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.62s
     Running `target\debug\guessing_game.exe`
Hello, world!

处理一次猜测

  • 获取用户猜测并打印
use std::io;

fn main(){
    println!("猜猜数字是几!");
    println!("请输入你的答案");
    let mut guess=String::new();    //mut(可变性,添加后让变量可变)
                                    //String::new()函数会返回一个String的新实例
                                    //::语法表明new是String类型的一个关联函数
    let num=0;  //rust中,变量默认不变
    io::stdin()
        .read_line(&mut guess)  //.read_line方法获取用户输入,并将&mut guess作为参数传递给read_line()
        .expect("无法读取");
    println!("你猜的是{}",guess);
}

运行结果

PS D:\code\Rust\project\guessing_game> cargo run
   Compiling guessing_game v0.1.0 (D:\code\Rust\project\guessing_game)
warning: unused variable: `num`                                        
 --> src\main.rs:9:9
  |
  |         ^^^ help: if this is intentional, prefix it with an underscore: `_num`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: `guessing_game` (bin "guessing_game") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.57s     Running `target\debug\guessing_game.exe`
猜猜数字是几!
请输入你的答案
1
你猜的是1

生成一个秘密数字

使用crate来增加更多功能

crate:一个Rust代码包。

rand crate :是一个库crate

库crate:可以包含任意被其他程序使用的代码,但是不能独自执行

Cargo 对外部 crate 的运用:使用rand编写代码需要修改Cargon.toml文件,引入rand依赖。打开此文件并将下面这一行添加到 [dependencies] 表块标题之下。

rand = "0.8.3"

在 [dependencies] 表块中,你要告诉 Cargo 本项目依赖了哪些外部 crate 及其版本。

Cargo.lock 文件确保构建是可重现的

2. 猜数字游戏_第1张图片

更新crate到一个新版本

Cargo 默认只会寻找大于或等于 0.8.3 而小于 0.9.0 的版本

$ cargo update
    Updating crates.io index
    Updating rand v0.8.3 -> v0.8.4

2. 猜数字游戏_第2张图片

生成一个随机数

使用 rand 来生成一个猜测的数字

use std::io;
use rand::Rng;  //Rng 是一个 trait,它定义了随机数生成器应实现的方法

fn main() {
    println!("Guess the number!");

    /*
    1. 调用 rand::thread_rng 函数来为我们提供将要使用的特定随机数生成器:
       它位于当前执行线程的本地环境中,并从操作系统获取 seed。
    2. 调用随机数生成器的 gen_range 方法:
        由我们刚才使用 use rand::Rng 语句引入的 Rng trait 定义。
        gen_range 方法获得一个区间表达式(range expression)作为参数,并在区间内生成一个随机数。
    3. rand::thread_rng().gen_range(start..end) //包括起始端,但排除终止端
    */
    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("Failed to read line");

    println!("You guessed: {}", guess);
}

运行结果

PS D:\code\Rust\project\guessing_game> cargo run
   Compiling guessing_game v0.1.0 (D:\code\Rust\project\guessing_game) 
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.71s     Running `target\debug\guessing_game.exe`
Guess the number!
The secret number is: 90
Please input your guess.
3
You guessed: 3

PS D:\code\Rust\project\guessing_game> cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
     Running `target\debug\guessing_game.exe`
Guess the number!
The secret number is: 30
Please input your guess.

注:运行 cargo doc --open 命令来构建所有本地依赖提供的文档,并在浏览器中打开。例如,假设你对 rand crate 中的其他功能感兴趣,你可以运行 cargo doc --open 并点击左侧导航栏中的 rand

比较猜测的数字和神秘数字

现在有了用户输入和一个随机数,我们可以比较它们。这个步骤如示例 2-4 所示。注意这段代码还不能通过编译,我们稍后会解释。

use rand::Rng;
//标准库引入了一个叫做 std::cmp::Ordering 的类型到作用域中:
//Ordering 也是一个枚举,不过它的成员是 Less、Greater 和 Equal。
use std::cmp::Ordering; 
use std::io;
 
fn main() {
    // --snip--
    println!("Guess the number!");
    println!("The secret number is: {}", secret_number);
    println!("Please input your guess.");
    let mut guess = String::new();
     o::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
    println!("You guessed: {}", guess);

    //new
    /*
    1.  cmp 方法用来比较两个值并可以在任何可比较的值上调用:
        它获取一个被比较值的引用:这里是把 guess 与 secret_number 做比较。 
        然后它会返回一个刚才通过 use 引入作用域的 Ordering 枚举的成员。
        使用一个 match 表达式,根据 Ordering 成员来决定接下来做什么。
    2.  match 表达式由分支(arm) 构成:
        一个分支包含一个用于匹配的模式(pattern),
        给到 match 的值与分支模式相匹配时,应该执行对应分支的代码。
     */
    match guess.cmp(&secret_number) {   
         Ordering::Less => println!("Too small!"),
         Ordering::Greater => println!("Too big!"),
         Ordering::Equal => println!("You win!"),
    }
}

编译错误:这里有不匹配的类型。guess是String类型,二secret_number是数字类。

Rust 中有好几种数字类型拥有 1 到 100 之间的值:32 位数字 i32、32 位无符号数字 u32、64 位数字 i64。

所以我们必须把从输入中读取到的 String 转换为一个真正的数字类型,才好与秘密数字进行比较。这可以通过在 main 函数体中增加如下两行代码来实现:

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!("The secret number is: {}", secret_number);
    println!("Please input your guess.");
    // --snip--
    let mut guess = String::new();
    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");
    
    //------------------new-------------------
    /*1.创建了一个叫做 guess 的变量:
        Rust 允许用一个新值来遮蔽 (shadow) guess 之前的值.
        允许我们复用 guess 变量的名字,而不是被迫创建两个不同变量.
    2.  将这个新变量绑定到 guess.trim().parse() 表达式上:
        表达式中的 guess 是指原始的 guess 变量,
        String 实例的 trim 方法会去除字符串开头和结尾的空白字符,
        用户必须输入 enter 键才能让 read_line 返回,并输入他们的猜想,这会在字符串中增加一个换行符
    3.  字符串的 parse 方法 :将字符串解析成数字。
        通过 let guess: u32 指定需要解析的数字类型
    4. 使用 Result 类型来处理潜在的错误”:
    */
    let guess: u32 = 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!"),
    }
}

即便是在猜测之前添加了空格,程序依然能判断出用户猜测了 76。多运行程序几次,输入不同的数字来检验不同的行为:猜一个正确的数字,猜一个过大的数字和猜一个过小的数字。

使用循环来允许多次猜测

loop 关键字创建了一个无限循环。我们会增加循环来给用户更多猜数字的机会:

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);
    // --snip--
    println!("The secret number is: {}", secret_number);
    
    //new:loop循环
    loop {
        println!("Please input your guess.");
        // --snip--
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        let guess: u32 = 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!"),
        }
    }
}

用户总能使用 ctrl-c 终止程序。不过还有另一个方法跳出无限循环,就是“比较猜测的数字和秘密数字”部分提到的 parse:如果用户输入的答案不是一个数字,程序会崩溃。我们可以利用这一点来退出。

猜测正确后退出

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);
    // --snip--
    println!("The secret number is: {}", secret_number);
    
    //new:loop循环
    loop {
        println!("Please input your guess.");
        // --snip--
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        let guess: u32 = 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;
            }
        }
    }
}

使用break

处理无效输入

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!("The secret number is: {}", secret_number);
    loop {
        println!("Please input your guess.");
        let mut guess = String::new();

        // --snip--
        //将 expect 调用换成 match 语句:实现遇到错误就崩溃转换成处理错误
        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
        println!("You guessed: {}", guess);
        // --snip--

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

运行结果

Guess the number!
The secret number is: 56
Please input your guess.
j
Please input your guess.
1
You guessed: 1
Too small!
Please input your guess.
56
You guessed: 56
You win!

最终代码

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

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => 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;
            }
        }
    }
}

你可能感兴趣的:(Rust小白初学,rust,开发语言,后端)