99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

猜謎游戲

2022-04-21 10:04 更新

猜謎游戲

我們的第一個項目,將實現(xiàn)一個典型的初學者編程的問題:猜謎游戲。下面介紹下它是如何工作的:我們的程序將生成一個從一到一百的隨機整數(shù)。然后它會提示我們輸入一個猜測值。依據(jù)我們的輸入的猜測值,它會告訴我們猜測值是否過低或者過高。一旦我們猜正確,它將祝賀我們。聽起來不錯吧?

設置

進入你的項目目錄,讓我們建立一個新項目。還記得我們必須為 hello_world 創(chuàng)建目錄結構和 Cargo.toml 嗎?Cargo 有一個命令能為我們做這些事。讓我們來試一試:

    $ cd ~/projects
    $ cargo new guessing_game --bin
    $ cd guessing_game

我們向命令 cargo new 傳遞了我們項目的名稱,然后添加了標記 --bin ,這是因為我們正在創(chuàng)建一個二進制文件,而不是一個庫文件。

查看生成的 Cargo.toml:

    [package]

    name = "guessing_game"
    version = "0.1.0"
    authors = ["Your Name <you@example.com>"]

Cargo 從你的環(huán)境中得到這些信息。如果它是不正確的,進入文件并且改正過來。

最后,Cargo 為我們生成一個“Hello,world!”。查看下 src/main.rs

    fn main() {
        println!("Hello, world!")
    }

讓我們嘗試編譯下 Cargo 給我們的東西:

    $ cargo build
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)

太好了!再次打開你的 src/main.rs。我們將在這個文件中編寫所有的代碼。

在我們繼續(xù)之前,讓我教你一個 Cargo 的命令: run.cargo run 是有點像 cargo build 的命令,但是它可以在生成的可執(zhí)行文件后運行此文件。試一試:

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/debug/guessing_game`
    Hello, world!

太棒了!當你需要在一個項目中快速迭代時,這個 run 命令可以派上用場。我們游戲就是這樣的一個項目,在下一次迭代之前,我們需要快速測試每個迭代。

處理一個猜測值

我們開始做吧!對于我們的猜謎游戲,我們需要做的第一件事就是允許玩家輸入一個猜測值。將下面的內容輸入到你的 src/main.rs

    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)
            .ok()
            .expect("Failed to read line");

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

這里面有很多代碼啊!讓我們一點一點地輸入它。

    use std::io;

我們需要獲取用戶的輸入,然后將打印結果作為輸出。因此,我們需要標準庫中的 io 庫。Rust 利用“序部”只要引入少量的東西到每一個項目,。如果在序部中沒有的話,那么你必須直接 use 它。

    fn main() {

正如之前你所見過的, main() 函數(shù)是程序的入口點。這個 fn 語法聲明了一個新函數(shù), ()表明沒有參數(shù), {表示函數(shù)的主體開始。因為我們不包含一個返回類型,它假定是 (),一個空元組。

       println!("Guess the number!");

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

我們之前學習到的 println!() 是一個打印字符串到屏幕上的宏。

    let mut guess = String::new();

現(xiàn)在越來越有趣了!有很多東西在這個小行上。首先要注意的是,這是一個let 語句,是用來創(chuàng)建變量綁定的。他們以這種形式存在:

    let foo = bar;

這將創(chuàng)建一個名為 foo 的新綁定,并將其綁定到值 bar 上。在許多語言中,這被稱為一個“變量”,但是 Rust 的變量綁定有其成熟的方法。

例如,他們在默認情況下是不可變的。這就是我們的例子中使用 mut 的原因:它使一個綁定是可變,而不是不可改變的。 let 的左邊不能添加名稱,實際上,它可接受一個“模式”。之后,我們將更多的使用模式。現(xiàn)在它很容易使用:

    let foo = 5; // 不可變的.
    let mut bar = 5; // 可變的

哦, // 之后將開始一個注釋,直到行的結束。Rust 編譯時,將會忽略所有的注釋。

所以現(xiàn)在我們知道了 let mut guess 將引入一個名為 gusss 可變的綁定,但我們必須看 = 另一邊的綁定: String::new()。

String 是一個字符串類型,由標準庫提供的。 String 是一個可增長,utf-8 編碼的文本。

::new() 語法使用 ::因為這是一個特定類型的關聯(lián)函數(shù)。也就是說,它與 String 本身相關,而不是一個特定的實例化的 String。一些語言稱之為“靜態(tài)方法”。

這個函數(shù)命名為 new(),因為它創(chuàng)建了一個新的,空的 String。你會在許多類型上發(fā)現(xiàn) new()函數(shù),因為它是創(chuàng)建某類型新值的一個普通的名稱。

讓我們繼續(xù)往下看:

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

這又有很多代碼!讓我們一點一點的看。第一行包含兩部分。這是第一部分:

    io::stdin()

還記得我們如何在程序的第一行使用 use std::io 嗎?我們現(xiàn)在調用一個與之相關的函數(shù)。如果我們不用 use std::io,我們可以這樣寫這一行 std::io::stdin()。

這個特別的函數(shù)返回一個句柄到終端的標準輸入。更具體地說,返回一個 std::io::Stdin。

下一部分將使用這個句柄獲取來自用戶的輸入:

    .read_line(&mut guess)

在這里,我們在句柄上調用 read_line() 方法。方法就像聯(lián)合的方法,但其只能在一個特定的實例類型中使用,而不是在類型本身。我們還傳遞了一個參數(shù)到 read_line(): &mut guess

還記得我們上面是怎么綁定 guess 的嗎?我們認為它是可變的。然而, read_line 不是將一個 String 作為一個參數(shù):它用 &mut String 做參數(shù)。Rust 有一個稱為“引用”的特性,對一個數(shù)據(jù)你可以有多個引用,這樣可以減少復制。引用是一個復雜的特性。如何安全和容易的使用引用是 Rust 的一個主要賣點?,F(xiàn)在我們不需要知道很多這些細節(jié)來完成我們的項目。現(xiàn)在我們所需要知道的是像 let 綁定,引用是默認是不可變的之類的事情。因此,我們需要寫代碼 &mut guess,而不是 &guess。

為什么 read_line() 采用一個可變的字符串引用做參數(shù)? 它的工作就是獲取用戶鍵入到標準輸入的內容,并將轉換成一個字符串。因此,以該字符串作為參數(shù),因為要添加輸入,所以它需要是可變的。

但是我們不確定這一行代碼會執(zhí)行正確。雖然它僅是一行文本,但是它是第一部分的單一邏輯行代碼:

        .ok()
        .expect("Failed to read line");

當你用類似 .foo() 的語法調用一個方法時,你可以用換行和空格來寫開啟新的一行代碼。這能幫助你分開很長的代碼行。我們之前的代碼是這樣寫的:

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

但是這樣的代碼閱讀性不好,所以我們將其分隔開。三個方法調用分成三行來寫。我們已經(jīng)講過 read_line() 了,那么 ok() 和 expect() 是什么呢?嗯,我們之前已經(jīng)提到了, read_line() 讀取了用戶輸入到 &mut String 中的內容。而且它也返回了一個值:在上面的例子中,返回值是 io::Result。Rust 在其標準庫中有很多類型命名為 Result:一個通用的 Result,然后用子庫指定具體的版本,比如 io::Result。

使用這些 Result 類型的目的是編碼錯誤處理信息。Result 類型的值,類似于任何類型,其有定義的方法。在上面的例子中,io::Result 就有一個 ok() 方法。這個方法表明,“我們想假定這個值是一個正確值。如果不是,那么就拋出錯誤的信息?!蹦敲礊槭裁匆獟伋鏊??對于一個基本的程序,我們只是想打印出一個通用的錯誤信息。任何這樣的基本問題的發(fā)生意味著程序不能繼續(xù)運行了。ok() 方法返回了一個值,在這個值上定義了另外一個方法: expect()。 expect() 方法調用時,會獲取一個值,如果這個值不是正確的值,那么會導致一個應急(panic?。┑陌l(fā)生,你要傳遞一個信息給這個應急。這樣的一個應急(panic!)會導致我們的程序崩潰,并且會顯示你傳遞的信息。

如果我們不調用這兩個方法,程序能編譯成功,但我們會得到一個警告信息:

    $ cargo build
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
    src/main.rs:10:5: 10:39 warning: unused result which must be used,
    #[warn(unused_must_use)] on by default
    src/main.rs:10     io::stdin().read_line(&mut guess);

Rust 警告我們,我們沒有使用 Result 值。這個警告來自 io::Result 所有的一個特殊的注釋。Rust 試圖告訴你,你沒有處理一個有可能的錯誤。避免這個錯誤的正確方法是寫好錯誤的處理部分的代碼。幸運的是,如果程序有問題,而我們只是讓程序自己崩潰就好,那么我們可以用這兩個方法。但是如果我們想讓程序能從錯誤中恢復,那么我們就要做點其他的事情了。這部分內容我們保留到未來的一個項目里再講。

第一個例子就剩下這一行代碼了:

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

這行代碼打印出我們輸入的字符串。{} 是一個占位符,所以我們把 guess 作為參數(shù)傳遞給它。如果我們有多個 {} ,那么我們將傳遞多個參數(shù):

    let x = 5;
    let y = 10;

    println!("x and y: {} and {}", x, y);

容易吧。

不管怎樣,這就是我們的學習過程啦。我們可以用我們所擁有的 cargo run 來運行代碼了:

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/debug/guessing_game`
    Guess the number!
    Please input your guess.
    6
    You guessed: 6

好吧!我們的第一部分已經(jīng)完成了:我們可以從鍵盤輸入,然后將它打印出來。

生成一個秘密數(shù)字

接下來,我們需要生成一個秘密數(shù)字。Rust 還未引入有隨機數(shù)方法的標準庫。然而,Rust 的團隊提供了一個隨機箱 rand crate。一個“箱”是一個 Rust 的代碼包。我們已經(jīng)構建了一個可執(zhí)行的“二進制箱”。 rand 是一個“箱庫”,其中包含的代碼可以在其他程序中使用。

使用外部箱是 Cargo 的一個閃光點。在我們使用 rand 寫代碼之前,我們需要修改 Cargo.toml。打開它,并在文件底部添加這幾行:

    [dependencies]

    rand="0.3.0"

Cargo.toml 的 [dependencies] 的部分就像 [package] 部分一樣:直到下一個部分開始之前,它下面的內容都是它所包含的。Cargo 通過使用依賴關系部分來知道你所擁有的依賴外部箱,以及你需要的版本。在本例中,我們使用的版本是 0.3.0。Cargo 能理解語義版本(Semantic Versioning),這是一個編寫版本號的標準。如果我們想使用最新的版本,我們可以使用 * ,或者我們也可以使用一個范圍的版本。Cargo 的文檔包含更多的細節(jié)內容。

現(xiàn)在,在不改變我們的任何代碼,讓我們重新構建我們的項目:

    $ cargo build
        Updating registry `https://github.com/rust-lang/crates.io-index`
     Downloading rand v0.3.8
     Downloading libc v0.1.6
       Compiling libc v0.1.6
       Compiling rand v0.3.8
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)

(你可以看到不同版本)。

有大量的新的內容出現(xiàn)了!現(xiàn)在我們有一個外部依賴,Cargo 從注冊表獲取所有依賴的最新版本,我們這里的注冊表是一個來自 Crates.io 的數(shù)據(jù)副本。這里的 Crates.io 是 Rust 生態(tài)系統(tǒng)中人們發(fā)布給他人使用的開源 Rust 項目的地方。

更新注冊表,Cargo 會檢查我們的 [dependencies] 并下載我們沒有的一些依賴。在本例中,盡管我們說我們只想依賴 rand,但是我們也獲取了一份 libc 的拷貝。這是因為 rand 是基于 libc 工作的。下載完成后,Cargo 會先編譯這些依賴項,然后再編譯我們的項目。

如果我們再次運行 cargo build,我們將會得到不同的輸出:

    $ cargo build

沒有錯,這里是沒有輸出的!Cargo 知道我們的項目已經(jīng)構建完成,并且項目所有的依賴項也構建完成,因此沒有理由將所有這些事情再做一遍。既然無事可做,它就退出了。如果我們再次打開 src/main.rs,做一個微不足道的改變,然后再保存它,再次運行,我們就會看到一行:

    $ cargo build
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)

所以,我們告訴 Cargo 我們想要任何 0.3.x 版本的 rand,因此它會獲取當時的最新版本,v0.3.8。但是當下周有重要錯誤修正的版本 v0.3.9 出現(xiàn)時會發(fā)生什么?雖然錯誤修正是很重要的,但是如果 0.3.9 版本包含會破壞我們代碼的一個回歸呢?

這個問題的答案是鎖文件 Cargo.lock,現(xiàn)在你可以在你的項目目錄中找到它。當你第一次構建你的項目時,Cargo 會找出所有的符合你的要求的版本,然后將他們寫到 Cargo.lock 文件中。當未來你構建項目時,Cargo將會發(fā)現(xiàn) Cargo.lock 文件的存在,然后使用文件指定的版本,而不是再次做找出版本的工作。這可以讓你有一個可重復的自動構建。換句話說,直到我們明確升級之前,我們將待在 0.3.8 版本。所以那些分享我們的代碼人,也會感謝鎖文件的存在。

那么當我們真的想使用 v0.3.9 版本時,怎么辦?Cargo 還有另外一個命令, update,它的意思是“忽略鎖文件,找出適合我們指定要求的所有最新版本。如果可行,那么將那些版本信息寫到鎖文件中?!钡?,默認情況下,Cargo 只會比尋找大于 0.3.0 并且小于 0.4.0 的版本。如果我們想升級到 0.4.x 版本,那我們必須直接更新 Cargo.toml 文件。如果我們這么做了,我們下次運行 cargo build 時,Cargo 將更新索引并重新評估我們對 rand 的需求。

這里提到了很多關于 Cargo 和它的生態(tài)系統(tǒng)的內容,對現(xiàn)在來說,這就是我們所需要知道關于他們的全部信息。Cargo 使 Rust 很容易重復使用庫,所以 Rustaceans 傾向于編寫當做子包組裝的小項目。

讓我們實際使用一下 rand。下面是我們要進行的下一個步驟:

    extern crate rand;

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

    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)
            .ok()
            .expect("failed to read line");

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

我們所做的第一件事就是改變了第一行代碼?,F(xiàn)在第一行是,extern crate rand。因為我們在我們的 [dependencies] 中聲明了 rand,所以我們可以使用 extern crate 讓 Rust 知道我們會使用它。這也相當于一個 use rand;,所以我們可以通過加前綴 rand::來利用 rand 箱中的所有東西。

接下來,我們添加了另一個 use 行: use rand::Rng。一會兒我們要用一種方法,它需要 Rng 在作用域內才運行。基本的思路是這樣的:方法是定義在一些所謂的“特征”上的,它需要特征在域內,才能使方法工作。更多的細節(jié)內容,請閱讀特征章節(jié)。

在中間,有我們添加的另外兩行:

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

    println!("The secret number is: {}", secret_number);

我們使用 rand::thread_rng() 函數(shù)獲取隨機數(shù)產(chǎn)生器的一個副本,這是我們在執(zhí)行的一個本地線程。我們上面使用 use rand::Rng,它有一個可用的 gen_range() 方法。這個方法有兩個參數(shù),生成一個在兩個參數(shù)范圍內的數(shù)字。它包括下界值,但不包括上界值,所以我們需要 1 和 101 兩個參數(shù),從而來獲取一個一到一百之間的數(shù)字。

第二行打印出了這個生成的秘密數(shù)字。這是在我們開發(fā)項目時是非常有用的,我們可以很容易地對其進行測試。但是在最終版本里我們會刪除它。如果一開始的時候就打印出來你要猜的數(shù)字,顯然這不是一個游戲!

嘗試運行幾次我們的程序:

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/debug/guessing_game`
    Guess the number!
    The secret number is: 7
    Please input your guess.
    4
    You guessed: 4
    $ cargo run
         Running `target/debug/guessing_game`
    Guess the number!
    The secret number is: 83
    Please input your guess.
    5
    You guessed: 5

太棒了!下一篇:讓我們用我們的猜測值和秘密數(shù)字比較一下。

比較猜測值

現(xiàn)在,我們已經(jīng)得到了用戶的輸入,讓我們將我們猜測值與隨機值比較下。下面是我們要進行的下一步,它現(xiàn)在還不能運行:

    extern crate rand;

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

        println!("The secret number is: {}", secret_number);

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

        let mut guess = String::new();

        io::stdin().read_line(&mut guess)
            .ok()
            .expect("failed 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!"),
        }
    }

這里的有幾個新部分。第一個是另外的一個 use。我們將叫做 std::cmp::Ordering 的類型放到了作用域內。然后是底部新的五行代碼:

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

cmp() 方法可以在任何可比較的地方被調用,但是它需要你想要比較東西的一個引用。它將返回我們之前 use 的 Ordering 類型。我們使用一個 match 語句來確定它到底是一個什么樣的 Ordering。 Ordering 是一個“枚舉”(enum,enumeration 的簡稱)。枚舉看起來像這個樣子的:

    enum Foo {
        Bar,
        Baz,
    }

利用這個定義,任何類型是 Foo 的類型,可以是一個 Foo::Bar 或者是一個 Foo::Baz。我們使用 :: 表明一個特定 enum 變量的命名空間。

Ordering 枚舉有三個可能的變量: Less, Equal 和 Greater。 match 語句需要有一個類型的值輸入,并允許你為每一可能的值創(chuàng)建一個“手臂”。因為我們有三種類型的 Ordering,因此我們要三個:

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

如果它是 Less,我們打印 Too small!,如果是 Greater,打印 Too big!,如果 Equal,打印 You win!。 match 是非常有用的,在 Rust 中經(jīng)常會使用它。

我之前提到了,項目現(xiàn)在還不能運行。讓我們來試一試:

    $ cargo build
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
    src/main.rs:28:21: 28:35 error: mismatched types:
     expected `&collections::string::String`,
        found `&_`
    (expected struct `collections::string::String`,
        found integral variable) [E0308]
    src/main.rs:28     match guess.cmp(&secret_number) {

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

這是一個很嚴重的錯誤。它核心的意思是,我們有不匹配的類型。Rust 有一個強壯的、靜態(tài)的類型系統(tǒng)。然而,它也有類型推導過程。當我們寫了 let guess = String::new(),Rust 能夠推斷出 guess 應該是一個 String,所以它不用讓我們寫出 guess 的類型。然而值從 1 到 100 的 secret_number 的類型就有很多了:i32,32 位的數(shù)字,u32,32 位無符號數(shù)字,或者 i64,一個 64 位的數(shù)字,或其的一些類型。Rust 的默認類型是 i32。到目前為止,還沒有什么關系。然而在比較代碼中,Rust 不知道如何比較 guess 和 secret_number。他們需要是相同的類型。最終,我們把讀入的 String 轉換成一個數(shù)字類型,來進行比較。我們用額外的三行來做這件事情。下面是我們的新程序:

    extern crate rand;

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

        println!("The secret number is: {}", secret_number);

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

        let mut guess = String::new();

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

        let guess: u32 = guess.trim().parse()
            .ok()
            .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!"),
        }
    }

新的三行代碼:

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

等一下,我想我們已經(jīng)有了一個 guess 了吧?對,我們確實有一個,但是 Rust 允許我們用一個新的 guess 來“遮住”前面的那一個。在具體情況中這經(jīng)常使用。 guess 開始時是一個 String,但我們想把它轉換成一個 u32。遮蔽功能能讓我們重復使用 guess 這個名字,而不是迫使我們使用兩個唯一的名字如 guess_str ,guess,或者其他之類的。

類似于我們之前寫的代碼那樣,我們將 guess 綁定到一個表達式:

    guess.trim().parse()

緊隨其后的是一個 ok().expect() 的調用。這里的 guess 指的是舊的那個 guess,也就是我們輸入 String 到其中那一個。String 的 trim() 方法是消除 String 前端和后端所有空白字符串的方法。這是很重要的,因為我們必須按“回車”鍵來結束 read_line() 方法的輸入。這意味著,如果我們鍵入 5 并且敲擊回車鍵,guess 看起來是這樣的:5\n。\n 表示“換行符”,回車鍵。trim() 方法就是用來去除類似這樣的東西的,讓我們的字符串只有 5。 字符串的 parse() 方法將字符串解析成某類數(shù)字。因為它可以解析各種數(shù)據(jù),所以我們需要給 Rust 提示我們想要轉化的確切數(shù)字類型。因此這樣寫,let guess: u32。guess 后面的冒號(:) 告訴 Rust 我們要轉化的類型。 u32 是一個無符號 32 位整數(shù)。Rust 有許多內置數(shù)字類型,這里我們選擇了 u32。對于一個小的正整數(shù),這是一個很好的默認選擇。

就像 read_line(),我們的 parse() 調用可能會導致一個錯誤。如果我們的字符串包含了 A% 呢?這就沒有辦法轉換成一個數(shù)字了。因此,我們會像 read_line() 做的那樣:用 ok() 和 expect() 方法去處理崩潰,如果有一個錯誤發(fā)生了的話。

讓我們來試試我們的新程序!

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/guessing_game`
    Guess the number!
    The secret number is: 58
    Please input your guess.
      76
    You guessed: 76
    Too big!

好了!你可以看到我甚至在猜的數(shù)字之前添加了空格,程序仍然發(fā)現(xiàn)我猜的數(shù)是 76。運行幾次程序,再猜一個比較小的數(shù)字,驗證猜測程序能正常運行。

現(xiàn)在我們已經(jīng)做了游戲的大部分工作,但我們只能做一個猜測。讓我們通過添加循環(huán)來改變這一情況!

循環(huán)

loop 關鍵字為我們提供了一個無限循環(huán)。讓我們添加如下的代碼并嘗試一下:

    extern crate rand;

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

        println!("The secret number is: {}", secret_number);

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

            let mut guess = String::new();

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

            let guess: u32 = guess.trim().parse()
                .ok()
                .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!"),
            }
        }
    }

先等一下,我們只是添加一個無限循環(huán)么?是的,沒錯。還記得我們關于 parse() 的講解嗎?如果我們給出了一個非數(shù)字的回答,它將會 return 并且退出。我們來看:

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/guessing_game`
    Guess the number!
    The secret number is: 59
    Please input your guess.
    45
    You guessed: 45
    Too small!
    Please input your guess.
    60
    You guessed: 60
    Too big!
    Please input your guess.
    59
    You guessed: 59
    You win!
    Please input your guess.
    quit
    thread '<main>' panicked at 'Please type a number!'

像其他非數(shù)字的輸入一樣,quit 確實是退出了。嗯,至少可以說這是一個次優(yōu)的解決方案。第一個方案:當你贏得比賽時,程序退出:

    extern crate rand;

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

        println!("The secret number is: {}", secret_number);

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

            let mut guess = String::new();

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

            let guess: u32 = guess.trim().parse()
                .ok()
                .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;
                }
            }
        }
    }

通過在打印 You win!的代碼后面添加一行 break,當我們贏了的時候就會退出循環(huán)。退出循環(huán)也意味著退出了程序,因為它是 main() 做的最后一件事。這里我們想做一個額外的調整:當有人輸入非數(shù)字時,我們并不退出,只是忽略它。我們可以這樣做:

    extern crate rand;

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

        println!("The secret number is: {}", secret_number);

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

            let mut guess = String::new();

            io::stdin().read_line(&mut guess)
                .ok()
                .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;
                }
            }
        }
    }

這些代碼改變了:

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

通過將使用 ok().expect() 換做使用一個 match 語句,這可以教會你是如何從“崩潰錯誤”轉變到“處理錯誤”的。parse() 返回的 Result 是一個像 Ordering 一樣的枚舉,但在這里,每個變量都有與之相關的一些數(shù)據(jù): Ok 是一個 success, Err 是一個 failure。它們每個又包含更多的信息:成功的解析成整數(shù),一個錯誤的類型。在這種情況下,我們將匹配 match 在 Ok(num),這里將 Ok 的內部值的名字設置為 num,然后我們在右側返回它的值。在 Err 的情況下,我們不關心它是什么樣的錯誤,所以這里我們只用一個下劃線 ‘_’,而不是一個名字。這樣會忽略掉錯誤,continue 會繼續(xù)進行下一個循環(huán)(loop)的迭代。

現(xiàn)在,問題應該都解決了!讓我們試一試:

    $ cargo run
       Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
         Running `target/guessing_game`
    Guess the number!
    The secret number is: 61
    Please input your guess.
    10
    You guessed: 10
    Too small!
    Please input your guess.
    99
    You guessed: 99
    Too big!
    Please input your guess.
    foo
    Please input your guess.
    61
    You guessed: 61
    You win!

太棒了!經(jīng)過最后一個小小的調整,我們已經(jīng)完成了猜謎游戲。你能想到什么?沒錯,我們不想打印出秘密數(shù)字。打印這個數(shù)字對于測試來說是很棒的一件事,但是當運行時,它會毀掉這個游戲的。下面是我們最終的代碼:

    extern crate rand;

    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)
                .ok()
                .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;
                }
            }
        }
    }

完成了!

此時此刻,你已經(jīng)成功地構建了猜謎游戲!恭喜你!

這第一個項目向你展示了很多內容: let, match,方法,相關函數(shù),使用外部箱等等。我們的下一個項目將展示更多的內容。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號