Rust学习笔记——基于官网和Rust语言圣经(二、猜数游戏)

发布时间 2023-06-25 13:22:34作者: 重拾初心的青年人

前面的hello world项目还是太old school了,这样用一个猜数字的游戏来快速了解下rust语言,以及为啥cargo那么好用的原因。不要拘束新的语法点,后面都会详细介绍!

2.1猜数游戏:一次猜测

-本节您将学会:

·let、match等方法

·相关的函数

·外部的crate

·...

猜数游戏-目标

-生成一个1到100间的随机数

-提示玩家输入一个猜测

-猜完之后,程序会提示猜测是太小了还是太大了

-如果猜测正确,那么打印出一个庆祝信息,程序退出

代码1

use std::io; // prelude
​
fn main() {
    println!("猜数!");
​
    println!("猜测一个数");
​
    let mut guess = String::new(); //设置guess为可变的 rust默认情况下变量不可变
​
    io::stdin().read_line(&mut guess).expect("无法读取行"); // 引入一个stdin库,通过read_line获得用户输入,并放入guess中,guess也需要可变的(随着用户的输入而改变),&取地址符号:表示引用,在代码不同地方访问同一个数据。expect:若是错误就会终止运行
​
    println!("你猜测的数是:{}",guess); //{},按照后面顺序,显示
​
}

代码说明:

默认变量不可变如:

    let foo = 1;
    let bar = foo; // immutable
    foo = 2;

read_line()返回一个io::Result

io::Result Ok,Err

ok表示操作成功

Err表示操作失败,还包括失败的原因

运行结果:

2.2猜数游戏:生成神秘数字

库的网站:https://crates.io/

比如我们要用生成随机数的库及其介绍

如何添加包

在cargo.toml的dependencies下添加需要的包

由于我们装了vs插件所以自己都安装了,不然是需要自己cargo build更新的

这里更新下,改成rand = "0.7.3"

更新完后,可以打开cargo.lock中可以看到更新好的包。类似于npm思想

这样的好处:如果有新的包会对项目产生冲突,但是这个lock记录了开发者用的包,这样所有人拿到你的项目都可以直接运行。cargo lock不要自己手动修改!

代码

use std::io; // prelude
use rand::Rng; // trait
​
fn main() {
    println!("猜数游戏!");
​
    let secret_number = rand::thread_rng().gen_range(1, 101);
​
    println!("神秘数字是:{}",secret_number);
​
    println!("猜测一个数");
​
    let mut guess = String::new(); 
​
    io::stdin().read_line(&mut guess).expect("无法读取行");
​
    println!("你猜测的数是:{}",guess);
​
}

运行结果

2.3猜数游戏:比较猜测数字与神秘数字

引入std::cmp::Ordering: 他有三个枚举类型,分别是大小等于。

use std::io; // prelude
use rand::Rng; // trait
use std::cmp::Ordering;
​
fn main() {
    println!("猜数游戏!");
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("神秘数字是:{}",secret_number);
    println!("猜测一个数");
    let mut guess = String::new(); 
    io::stdin().read_line(&mut guess).expect("无法读取行");
    println!("你猜测的数是:{}",guess);
​
    match guess.cmp(&secret_number){
        std::cmp::Ordering::Less => println!("Too small!"),
        std::cmp::Ordering::Equal => println!("You win!"),
        std::cmp::Ordering::Greater => println!("Too big!"),
    }
}
​

若直接运行会报错,因为比较两个值的类型不一样。

代码

use std::io; // prelude
use rand::Rng; // trait
​
​
fn main() {
    println!("猜数游戏!");
    let secret_number = rand::thread_rng().gen_range(1, 101); //i32 u32 i64
    println!("神秘数字是:{}",secret_number);
    println!("猜测一个数");
    let mut guess = String::new(); 
    io::stdin().read_line(&mut guess).expect("无法读取行");
    println!("你猜测的数是:{}",guess);
​
    // shadow
    let guess:u32 = guess.trim().parse().expect("Please type a number!"); //trim把前后空格去掉(包括回车\n) parse把字符串解析成某种数字类型
​
    match guess.cmp(&secret_number){
        std::cmp::Ordering::Less => println!("Too small!"),
        std::cmp::Ordering::Equal => println!("You win!"),
        std::cmp::Ordering::Greater => println!("Too big!"),
    }
}

rust为静态的强类型语言

需要把guess从string变成u32,这里修改后,本来是i32的secret_number也会变成u32,此外我们可以看到,rust中可以使用重复变量,来减少损耗,且智能上下文推理的能力。

运行结果

猜数游戏:允许多次猜测

如要无限猜测需要加入无限循环

代码

use std::io; // prelude
use rand::Rng; // trait
​
​
fn main() {
    println!("猜数游戏!");
    let secret_number = rand::thread_rng().gen_range(1, 101); //i32 u32 i64
    println!("神秘数字是:{}",secret_number);
​
    loop {
        println!("猜测一个数");
        let mut guess = String::new(); 
        io::stdin().read_line(&mut guess).expect("无法读取行");
        println!("你猜测的数是:{}",guess);
​
        // shadow
        let guess:u32 = guess.trim().parse().expect("Please type a number!");
​
        match guess.cmp(&secret_number){
            std::cmp::Ordering::Less => println!("Too small!"),
            std::cmp::Ordering::Equal => println!("You win!"),
            std::cmp::Ordering::Greater => println!("Too big!"),
        }
    }
   
}

将猜测部分代码放入loop循环中,即可完成。

运行结果

我们可以看到可以实现,但是还有一点点不健壮性。比如我们输入的不是数字呢?还有猜测都成功了,应该退出程序啊!

修改

先处理不能退出循环的问题:

use std::io; // prelude
use rand::Rng; // trait
use std::cmp::Ordering;
​
​
fn main() {
    println!("猜数游戏!");
    let secret_number = rand::thread_rng().gen_range(1, 101); //i32 u32 i64
    println!("神秘数字是:{}",secret_number);
​
    loop {
        println!("猜测一个数");
        let mut guess = String::new(); 
        io::stdin().read_line(&mut guess).expect("无法读取行");
        println!("你猜测的数是:{}",guess);
​
        // shadow
        let guess:u32 = guess.trim().parse().expect("Please type a number!");
​
        match guess.cmp(&secret_number){
            Ordering::Less => println!("Too small!"),
            Ordering::Equal => {
                println!("You win!");
                break; //rust退出循环也是break
            },
            Ordering::Greater => println!("Too big!"),
        }
    }
   
}

在判断等于的时候加入break跳出循环!

修复非数字输入:

我们可以利用expect返回ok和err来进行一个match选择。

use std::io; // prelude
use rand::Rng; // trait
use std::cmp::Ordering;
​
​
fn main() {
    println!("猜数游戏!");
    let secret_number = rand::thread_rng().gen_range(1, 101); //i32 u32 i64
    // println!("神秘数字是:{}",secret_number);
​
    loop {
        println!("猜测一个数");
        let mut guess = String::new(); 
        io::stdin().read_line(&mut guess).expect("无法读取行");
        println!("你猜测的数是:{}",guess);
​
        // shadow
        let guess:u32 = match guess.trim().parse(){
            Ok(num) => num, // ok就正常赋值
            Err(_) => continue, // 错误就重新输入,_表示不关心错误信息
        };
​
        match guess.cmp(&secret_number){
            Ordering::Less => println!("Too small!"),
            Ordering::Equal => {
                println!("You win!");
                break; //rust退出循环也是break
            },
            Ordering::Greater => println!("Too big!"),
        }
    }
   
}

未完待续