从零开始的 Rust 学习笔记(1)

于是跟着「The Rust Programming Language」这本书,首先是一个很常见的语言入门的例子——猜数字。

这里一般会涉及随机数、标准输入输出、if 语句 / 函数匹配、字符串转整数、循环,然后作为入门的例子,也相对比较有趣233333

那这里顺着书的流程,首先是熟悉一下 print 这样的语句,然后接着是 new 对象,以及从标准输入读取数据~

use std::io;

fn main() {
    println!("Guess Your Number!");
    println!("Input your number:");
    // new 一个 String 对象 guess
    // guess 是一个可变对象
    let mut guess = String::new();

    // 从标准输入中读取一行
    // io::stdin().read_line()
    //
    // guess 作为可变对象传入
    // 因为读取到的行的内容会放在 guess 
    // &mut guess
    //
    // io::stdin().read_line() 返回一个 io::Result
    // 要么是 Err, 要么是 Ok
    // -  io::Result  Err 时(即有错误发生时)
    //   程序会崩溃并显示传入 expect 中的字符串作为报错信息
    // -  io::Result  Ok 
    //   expect() 会返回上一个(这里即 read_line())的返回值
    //   read_line() 返回一共收到了多少个字节的数据
    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");
    
    // 这里与 Python 类似
    // 使用 {} 作为 placeholder
    // 后面传入变量的值会依次序替换 placeholder
    println!("You guessed: {}", guess);
}

这些跟 C/C++/Swift 等等基本上差别不大~可以运行一下看看效果

然后就是怎么产生随机数了,这里「The Rust Programming Language」使用了 rand 这个包,于是在 Cargo.toml 中加入这个依赖~

加入完依赖之后则是实际调用

use std::io;
use rand::Rng;

fn main() {
    // 使用 rand 生成一个 [1, 101) 的数字
    let secret_number = rand::thread_rng().gen_range(1, 101);
    // 并且输出它
    println!("The secret number is: {}", secret_number);

    println!("Guess Your Number!");
    println!("Input your number:");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");
    println!("You guessed: {}", guess);
}

现在已经有了 secret number 和用户的输入了,就差比较是否相等了~

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("The secret number is: {}", secret_number);

    println!("Guess Your Number!");
    println!("Input your number: ");

    let mut guess = String::new();

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

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

    // 比较大小
    // 两个值的比较结果有三种可能
    // - std::cmp::Ordering::Less
    // - std::cmp::Ordering::Greater
    // - std::cmp::Ordering::Equal
    //
    // 接下来我们使用 match 表达式
    // 紫色部分的结果会与下面列举的从上到下依次进行匹配
    // 列举的部分在 Rust 中被称为 arm(红色部分)
    // 这里有三个 arm,对应于比较结果的三种可能
    // 在 arm 中,左侧为 pattern
    // 即与紫色部分的结果进行匹配的东西
    // 右侧则为匹配成功之后会运行的代码
    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

当然,话是这么说,不过现在 guess 还是 String 类型的,需要转为 u32 类型的整数才可以与 secret_number 比较~否则的话,现在编译会因为 cmp 前后的变量类型不同报错。而 Rust 语言又是强静态类型的,所以 Rust 编译器检查到这里时就会报错( ;´Д`)

而我们在声明变量时可以不用显式注明变量类型则是因为 Rust 编译器根据上下文帮我们做了类型推理。这一点与 Swift 是一样的

其实将字符串尝试转为整数挺简单的,只需要加一两行即可~

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("The secret number is: {}", secret_number);

    println!("Guess Your Number!");
    println!("Input your number: ");

    let mut guess = String::new();

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

    // 尝试将从标准输入的 guess 转为 u32 类型的整数
    // 如果转换失败
    // 则说明用户输入的不是数字
    // 提示 "Please type a number!"
    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!"),
    }
}

现在编译的话就已经基本上没问题了(>^ω^<)

不过只能一次一次的运行,也就是说每次猜数字只有一次的机会(嘛,当然现在因为输出了 secret_number,所以要作弊的话一次也足够了233333)

那么要做成猜对了就退出的话,显然我们需要在 Greater / Less 的情况下继续,在 Equal 的时候可以退出循环,于是我们需要循环~

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("The secret number is: {}", secret_number);

    println!("Guess Your Number!");
    // 无限循环
    loop {
        println!("Input your number: ");

        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!");
                // 在 Equal 时退出循环
                break;
            }
        }
    }
}

现在编译的话,就是按我们的想法执行了(*/ω\*)

其实对于用户输入的 guess 来说的话,我们在 parse 的时候还可以再做得好一点,不要直接让程序崩溃报错,而是尝试跳到循环开始的地方,让用户重新输入(⁎⁍̴̛ᴗ⁍̴̛⁎) (顺便我们接下来也把输出 secret_number 的语句给删掉~)

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    println!("Guess Your Number!");

    loop {    
        println!("Input your number: ");

        let mut guess = String::new();

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

        let guess: u32 = match guess.trim().parse() {
            // 在可以正确转换为 u32 整数时返回转化的值
            Ok(num) => num,
            // 在不能转换时
            // 用 continue 回到循环开始处
            // 让用户重新输入
            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;
            }
        }
    }
}

现在即使输入非数字,也可以不用像之前一样让程序崩溃了~ヽ(;▽;)ノ

Leave a Reply

Your email address will not be published. Required fields are marked *

eight − 6 =