rust_1_入门指南与猜猜看游戏

在 Linux 或 macOS 上安装 rustup

$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh #下载
$ rustup update #更新
$ rustup self uninstall #卸载

使用 Cargo 创建项目

$ cargo new hello_cargo #创建项目

文件名: Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

[package],是一个片段(section)标题,表明下面的语句用来配置一个包

接下来的四行设置了 Cargo 编译程序所需的配置:项目的名称、版本、作者以及要使用的 Rust 版本。

[dependencies],是罗列项目依赖的片段的开始。

文件名:Cargo.lock

这个文件记录项目依赖的实际版本。这个项目并没有依赖,所以其内容比较少。你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。

cargo build #默认编译debug版本,要编译release版加上--release
cargo check #快速检查代码确保其可以编译,并不产生可执行文件
cargo run #一步构建并运行项目

编写 猜猜看 游戏

use std::io;

Rust 将 prelude 模块中少量的类型引入到每个程序的作用域中。如果需要的类型不在 prelude 中,你必须使用 use 语句显式地将其引入作用域。std::io 库提供很多有用的功能,包括接收用户输入的功能。

println!("Guess the number!");

println! 是一个在屏幕上打印字符串的宏

String::new();:: 语法表明 newString 类型的一个 关联函数,一些语言中把它称为 静态方法static method)。

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

stdin 函数返回一个 std::io::Stdin 的实例,这代表终端标准输入句柄的类型。.read_line(&mut guess),调用 read_line 方法从标准输入句柄获取用户输入,传递了一个参数:&mut guess& 表示这个参数是一个 引用reference),mut表示这个字符串参数应该是可变的,以便 read_line 将用户输入附加上去。

read_line 将用户输入附加到传递给它的字符串中,不过它也返回一个值——在这个例子中是 io::Result。Rust 标准库中有很多叫做 Result 的类型:一个通用的 Result 以及在子模块中的特化版本,比如 io::ResultResult 类型是 枚举enumerations,通常也写作 enums

Result 的成员是 OkErrOk 成员表示操作成功,内部包含成功时产生的值。Err 成员则意味着操作失败,并且包含失败的前因后果。

println!("You guessed: {}", guess); // {}占位符

文件名: Cargo.toml

[dependencies]
rand = "0.5.5"

0.5.5 事实上是 ^0.5.5 的简写,它表示 “任何与 0.5.5 版本公有 API 相兼容的版本”。

有了一个外部依赖,Cargo 从 registry 上获取所有包的最新版本信息,这是一份来自 Crates.io 的数据拷贝。Crates.io 是 Rust 生态环境中的开发者们向他人贡献 Rust 开源项目的地方。

在更新完 registry 后,Cargo 检查 [dependencies] 片段并下载缺失的 crate 。本例中,虽然只声明了 rand 一个依赖,然而 Cargo 还是额外获取了 libcrand_core 的拷贝,因为 rand 依赖 libcrand_core 来正常工作。下载完成后,Rust 编译依赖,然后使用这些依赖编译项目。

Cargo也是增量编译

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

这个问题的答案是 Cargo.lock 文件。它在第一次运行 cargo build 时创建,当第一次构建项目时,Cargo 计算出所有符合要求的依赖版本并写入 Cargo.lock 文件。当将来构建项目时,Cargo 会发现 Cargo.lock 已存在并使用其中指定的版本。

当你 确实 需要升级 crate 时,Cargo 提供了另一个命令,update,它会忽略 Cargo.lock 文件,并计算出所有符合 Cargo.toml 声明的最新版本。如果成功了,Cargo 会把这些版本写入 Cargo.lock 文件。

类似于gittag记录版本,但可以使用update更新Cargo.lock

Cargo 默认只会寻找大于 0.5.5 而小于 0.6.0 的版本。如果 rand crate 发布了两个新版本,0.5.60.6.0

update后使用的是0.5.6而不是0.6.0,想要使用 0.6.0 版本的 rand 或是任何 0.6.x 系列的版本,需要更新 Cargo.toml 文件:

[dependencies]
rand = "0.6.0"

下一次运行 cargo build 时,Cargo 会从 registry 更新可用的 crate,并根据你指定的新版本重新计算

use rand::RngRng 是一个 trait,它定义了随机数生成器应实现的方法,想使用这些方法的话,此 trait 必须在作用域中

rand::thread_rng 函数提供实际使用的随机数生成器:它位于当前执行线程的本地环境中,并从操作系统获取 seed。接下来,调用随机数生成器的 gen_range 方法。这个方法由刚才引入到作用域的 Rng trait 定义。gen_range 方法获取两个数字作为参数,并生成一个范围在两者之间的随机数。它包含下限但不包含上限,所以需要指定 1101 来请求一个 1 和 100 之间的数。

cargo doc --open

Cargo 有一个很棒的功能是:运行 cargo doc --open 命令来构建所有本地依赖提供的文档,并在浏览器中打开。

use std::cmp::Ordering;

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 也是一个枚举,不过它的成员是 LessGreaterEqual。这是比较两个值时可能出现的三种结果。

match 表达式由 分支(arms) 构成。一个分支包含一个 模式pattern)和表达式开头的值与分支模式相匹配时应该执行的代码。

Rust 有一个静态强类型系统,同时也有类型推断。当我们写出 let guess = String::new() 时,Rust 推断出 guess 应该是 String 类型,并不需要我们写出类型。另一方面,secret_number,是数字类型。几个数字类型拥有 1 到 100 之间的值:32 位数字 i32

$ cargo build
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
error[E0308]: mismatched types
  --> src/main.rs:23:21
   |
23 |     match guess.cmp(&secret_number) {
   |                     ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integer
   |
   = note: expected type `&std::string::String`
   = note:    found type `&{integer}`

error: aborting due to previous error
Could not compile `guessing_game`.

Rust 不会比较字符串类型和数字类型。

let mut guess = String::new();

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

let secret_number = rand::thread_rng().gen_range(1, 101);

let guess: u32 = guess.trim().parse().expect("Please type a number!");

Rust 允许用一个新值来 隐藏shadowguess 之前的值。这个功能常用在需要转换值类型之类的场景。它允许我们复用 guess 变量的名字,而不是被迫创建两个不同变量,诸如 guess_strguess 之类。

guess 绑定到 guess.trim().parse() 表达式上。表达式中的 guess 是包含输入的原始 String 类型。String 实例的 trim 方法会去除字符串开头和结尾的空白字符。u32 只能由数字字符转换,不过用户必须输入 enter 键才能让 read_line 返回,然而用户按下 enter 键时,会在字符串中增加一个换行(newline)符。例如,用户输入 5 并按下 enter,guess 看起来像这样:5 代表 “换行”,回车键。trim 方法消除 ,只留下 5

loop 关键字创建了一个无限循环。

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

loop、continue和break

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

如果 parse 能将字符串转换为一个数字,它会返回一个包含更多错误信息的 ErrErr 值不能匹配第一个 match 分支的 Ok(num) 模式,但是会匹配第二个分支的 Err(_) 模式:_ 是一个通配符值,continue 意味着进入 loop 的下一次循环

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

break;用于退出循环

完整程序:

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

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;
            }
        }
    }
}
原文地址:https://www.cnblogs.com/kuikuitage/p/14233344.html