読者です 読者をやめる 読者になる 読者になる

冥冥乃志

ソフトウェア開発会社でチームマネージャをしているエンジニアの雑記。アウトプットは少なめです。

follow us in feedly

Rustの勉強(その5)

英語がね、体調によって読める精度がかなり変わるんですよ。。。

というわけで前回の続き。

mao-instantlife.hatenablog.com

処理のループ

前回までの状態だと一回の試行でプログラムが終わってしまうので、正解するまで帰れま10にします。

とりあえず無限ループ

とりあえずループさせたいときは、処理対象を loop で囲んでしまえばいいようです。

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 = 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!"),
  }
}

もちろん無限ループです。上記の状態で動かした場合は、数値以外入力して panic! 起こして止めようぜ、とのこと。雑でいいですね。

勝利条件のハンドリング

勝利した時にループを抜けるようにします。パターンマッチの Ordering::Equal の場合の処理を以下のように変更。

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

これは他の言語でもよくある break ですね。自分の知識とマッピングできる領域は安心します。

入力エラーのハンドリング

現状、入力が数値ではなく、パースできない時にはエラーでプログラムが落ちます。非数値の入力で止めたくないので、エラーハンドリングしてまた入力してもらうようにしましょう。

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

parse の戻り値は Enum なので、パターンマッチでエラーハンドリング可能みたいですね。 Ok の時の数値を返す、 Err の場合は continue で再度入力させるようにしています。

これでnumber guessing gameはできたぜ、とのこと。

食事する哲学者の問題

次のサンプルは、「食事する哲学者の問題」を解決するプログラムを作る、というもの。

食事する哲学者の問題 - Wikipedia

デッドロックを回避するマルチプロセスの古典的な問題っぽいですね。

型を定義する

まずは哲学者をモデリングしてみます。

struct Philosopher {
  name: String,
}

impl Philosopher {
  fn new(name: &str) -> Philosopher {
    Philosopher {
      name: name.to_string(),
    }
  }
}

struct が文字通り構造(フィールドの宣言?)の宣言で impl が型の実装定義です。これって型定義に必ずインターフェイスが必要という理解でいいんですかね?このあたりは正直まだ違和感があります。

型の実装定義では、コンストラクタに相当する association function である new を定義しています。チュートリアルに「コンストラクタ」という言葉が出てこなかったのでRustではコンストラクタと言ってないのかもしれません。

引数と戻り値を指定する関数の定義

関数の定義を見てみます。

fn new(name: &str) -> Philosopher

関数名の後に引数リスト、その後に -> で戻り値の型 *1 を指定するようです。割と馴染みのある表記なので安心。

String&str

Philosopher 型のフィールドにはString型を使っています。データの参照を使うより型を使ったほうが扱いやすいからという理由が書かれていましたが、メソッドが用意されていたり他のメソッドとのハンドリングがしやすいとかそんな理由でしょう。

それよりもわかりづらいのは new の引数を &str で受けていること。まだ全く理解できていないのですが、JavaintInteger とかと同じようにリテラルとそれをラップする型があると考えればいいのかな?上記コードを見る限り、そのまま文字列リテラルを定義すると &str になるっぽいです。

new の中で使っている to_string メソッドは、文字列のコピーを作成して新しいString インスタンスを作っています。

ちょっとキリ悪いけど

以下次号。

*1:正確にはどのstructのインスタンスかというところっぽい