iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 6
0
自我挑戰組

WebAssembly + Rust 的前端應用系列 第 6

[Day 6] Rust Programming a Guessing Game 終極密碼

大家好今天會帶各位來玩玩官網的第一個範例,實作一個終極密碼的小遊戲。

終極密碼想必大家都很熟悉了吧?我就不多做介紹了直接開始吧!

新增一個專案出來

$ cargo new guessing_game && cd guessing_game

接著我們看到專案裡面有一個 cargo.toml 這個檔案就是 cargo 用來管理依賴的檔案,類似 npm 的 package.json。

我們首先加一個依賴待會會用到

[dependencies]

rand = "0.3.14"

首先我們來試著輸入一些資料並且把他印出來。

檔案路徑: 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)
        .expect("Failed to read line");

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

上面的程式碼簡單來說就是用了 io 的 library 然後把輸入的字串印出來。這邊值得一提的是 ::(double colon) 這個符號在這邊是兩個不同的意思。

use std::io;

這裡單純就是 std 裡面的 module 的路徑

let mut guess = String::new();

而這裡的是 associated function 根據官網的說明

The :: syntax in the ::new line indicates that new is an associated functionof the String type. An associated function is implemented on a type, in this case String, rather than on a particular instance of a String. Some languages call this a static method.

簡單來說我們在編譯完檔案之後就給了這個變數 String 類型的記憶體位置而且他跟 runtime 的變數沒有任何關係。
詳細可以看這個問答裡面有很詳細的說明。

另外 println! 不是一般的函式而是 macro 這個我也還不是很了解是什麼有興趣的可以到這邊看看。

其他還有一些重要的觀念例如變數預設是 immutable 的以及 reference 的 ownership 這些會在後面的章節進一步介紹這邊就先點一下有個印象。

為了完成這個遊戲接下來就讓我們產生隨機介於 1 ~ 100 的數字吧

檔案路徑: src/main.rs

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

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

這邊我們引入剛剛加入的 rand dependency 然後從 1 ~ 100 中隨機產生一個數字然後執行。

cargo run

這樣輸入跟數字都產生好了最後讓我們來比對數字正確與否,但是在做這件事情之前要先把我們輸入的字串型態轉換成 Interger。

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

這邊我們用到了 Shadowing 這也是 rust 一個特別的地方我們可以重複宣告一個變數並且改變他的型態我在後面章節會有更詳細的說明。

最後就是比對了 Rust 提供了一個 library 可以用來比對數值

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

…

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::io;
use std::cmp::Ordering;
use rand::Rng;

…

loop {

  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;
    },
  }
}

下面是完整的程式碼

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

pub fn guess_number() {
  println!("Guess the number!");

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

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

    // 宣告 guess 是 String type 的 associated function
    // The :: syntax in the ::new line indicates that new is an associated function of the String type
    // Associated functions are functions associated with a type.
    // To summarize, the let mut guess = String::new(); line has created a mutable variable that is currently bound to a new, empty instance of a String. Whew!
    let mut guess = String::from("test");

    // The & indicates that this argument is a reference, which gives you a way to let multiple parts of your code access one piece of data without needing to copy that data into memory multiple times. 
    // 使用指標減少記憶體耗損
    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");

    // 轉換型態
    // shadowing 讓我們可以不必重新命名一個變數名稱
    let guess: u32 = match guess.trim().parse() {
      Ok(num) => num,
      // 錯誤時的處理
      Err(_) => {
        println!("Please type a number!");
        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;
      },
    }
  }
}

最後一樣有問題歡迎發問

/images/emoticon/emoticon07.gif

參考網站

Programming a Guessing Game

Why double colon rather that dot

Rust Operators

Difference between Static methods and Instance methods


上一篇
[Day 5] Rust Hello World!
下一篇
[Day 7] Rust Variables and Mutability 變數和可變性
系列文
WebAssembly + Rust 的前端應用30

尚未有邦友留言

立即登入留言