0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rustの基本

Posted at

勉強の備忘録
開発環境は整っているものとします。
基本タメ口ですが、敬語を意識してそっちにリソースが割かれるのが嫌なので許してください。

https://doc.rust-jp.rs/book-ja/
ここと並行して進めていくつもりです。
(最初に開発環境は整っているものとしているので1.は省略)

2. 数当てゲームのプログラミング

プロジェクトの立ち上げ

cargo new guessing_game

新規プロジェクトを作成すると、Hello worldプログラムが生成される

src/main.rs

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

プロジェクトの実行

cargo run

予想を処理する

use std::io;

fn main() {
    println!("数当てゲーム");
    println!("予想を入力してください");
    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("行の読み込みに失敗しました");
    
    println!("右のように予想しました:{}", guess);
}
use std::io;

標準入出力を受け取るために、stdというライブラリ内のioライブラリをインポート

fn main(){

プロジェクトのエントリポイント
cargo runするとmain関数がエントリポイントになる(エントリポイントを変更する方法などは不明)

println!("数当てゲーム");

コンソールに文字列を表示
!マークがついているのはマクロという意味

let mut guess = String::new();

変数の定義は以下

let guess = String::new();     //イミュータブル
let mut guess = String::new(); //ミュータブル 

変数名の前にmutを入れなければミュータブルにならない(デフォルトではイミュータブル)

String::new();

String型のインスタンスを新たに作成している

  • ユーザーの入力を受け取る

    io::stdin();
    

    ioライブラリのstdin(標準入力受け取り)を実行

    std::io::stdin();
    

    このようにすれば最上位のuse句がなくても動く

    .read_line(&mut guess)
    

    ここでユーザーからの入力を得ている
    &mut guessここであらかじめ定義したguessの中に受け取ったデータを入れている
    &をつけることで参照(メモリアドレス)であることを表す
    また、参照自体もデフォルトではイミュータブルであるため、&guessではなく&mut guessのようにしている

    .expect("行の読み込みに失敗しました");
    

    ioの中にはResult型というものがあり、処理が成功したか失敗したかがわかるらしい
    そのResult型の中にはexpectメソッドがあり、ここではそれを呼び出している

  • println!マクロのプレースホルダーで値を表示する

    println!("右のように予想しました:{}", guess);
    

    {}の部分にguessの値が入る

    let x = 5;
    let y = 10;
    
    println!("x = {} and y = {}", x, y);
    

    複数の場合はこう

乱数を生成する

  • クレートを使用して機能を追加する
    Rustの標準ライブラリには乱数を作成するものはないので外部のクレートを使う
    ※ クレートとはpythonでいうpipみたいなもの?(認識が違ったら教えてください)
    Cargo.tomlファイルにrandクレートを追加
    • Cargo.toml
      [dependencies]
      rand = "0.8.3"
      
    • ビルド
      cargo build
      
      この状態でbuildするとクレートをダウンロードしてコンパイルしてくれる
          Blocking waiting for file lock on package cache
          Updating crates.io index
          Blocking waiting for file lock on package cache
          Blocking waiting for file lock on build directory
      Compiling cfg-if v1.0.0
      Compiling ppv-lite86 v0.2.17
      Compiling getrandom v0.2.8
      Compiling rand_core v0.6.4
      Compiling rand_chacha v0.3.1
      Compiling rand v0.8.5
      Compiling tutorial v0.1.0 (C:\git\rust-practice\tutorial)
          Finished dev [unoptimized + debuginfo] target(s) in 1m 44s
      
    • 乱数を生成する
      use std::io;
      use rand::Rng;
      
      fn main() {
          println!("数当てゲーム");
          let secret_number: u32 = rand::thread_rng().gen_range(1..101);
          println!("秘密の数字は{}です", secret_number);
          println!("予想を入力してください");
          let mut guess = String::new();
      
          io::stdin()
              .read_line(&mut guess)
              .expect("行の読み込みに失敗しました");
          
          println!("右のように予想しました:{}", guess);
      }
      
      use句でrandクレートをインポート
      let secret_number: u32 = rand::thread_rng().gen_range(1..101);
      
      この行で秘密の数字を乱数で作成
      1..101
      
      は範囲を表す
      この場合は1~100になる
      仮に1..=101とすると1~101の範囲になる

予想と秘密の数字を比較する

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

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    println!("予想を入力してください");
    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("行の読み込みに失敗しました");
    
    println!("右のように予想しました:{}", 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;

という型をスコープに追加
Orderingはenumの一種でLess、Greater、Equalという列挙子を持っている

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

cmpメソッドは値を比較できるものならなんでも使える
guessとsecret_numberの参照で比較をとる
matchに関しては6章で詳しく解説するらしいのでここでは触れない

  • 型の不一致の修正
    このままだとコンパイルができない
    match guess.cmp(&secret_number) で比較する際に、guessはStringなのに対し、secret_numberはu32型であるため、型の変更が必要

        .expect("行の読み込みに失敗しました");
    
    let guess: u32 = guess.trim().parse()
        .expect("Please type a number!");  
    

    この2行を追加する
    guessという変数名で、guessをu32型に変換したものを格納している
    これは「シャドーイング」といい、同じ変数名を再利用することができる

    guess.trim().parse()
    

    guessはこの時点ではString型である
    Stringオブジェクトのtrim関数を実行(先頭と末尾の空白を削除する)
    Stringオブジェクトのparseメソッドは文字列をなんらかの数値にする
    → 手前でlet guess: u32と型を明示しているので、この場合はparseメソッドはu32型に変換すればいいと認識をしてくれる
    また、parseメソッドはread_lineメソッド同様Result型を返すので、.expect("Please type a number!"); でエラー時のメッセージを書く

出力

cargo run
数当てゲーム
秘密の数字は7です
予想を入力してください
50
右のように予想しました:50
Too big!

ループで複数の予想を可能にする

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

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    loop{
        println!("予想を入力してください");
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("行の読み込みに失敗しました");
        
        let guess: u32 = guess.trim().parse()
            .expect("Please type a number!");  
        
        println!("右のように予想しました:{}", guess);

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

loopの中に予想して比較する部分を入れると無限にループするようになる

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

Orderingの各値は{}で囲むと複数行の処理が記述できるらしい

不正な入力をキャッチする

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

fn main() {
    println!("数当てゲーム");
    let secret_number: u32 = rand::thread_rng().gen_range(1..101);
    println!("秘密の数字は{}です", secret_number);
    loop{
        println!("予想を入力してください");
        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("行の読み込みに失敗しました");
        
        let guess: u32 = match guess.trim().parse(){
            Ok(num) => num,
            Err(_) => continue,
        };
        
        println!("右のように予想しました:{}", 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,
}

ここを書き換えた
先ほどparseメソッドはResult型を返すと書いたが、Result型はOkとErrの列挙子を持つ

  • Okの場合は引数に入力した値が入っている
  • Errの場合はどのエラーとマッチするかを(_)で表している
    • _の場合はすべてをマッチマッチさせている
    • これで実質敵にparseメソッドが遭遇しうるエラーを回避することができる

最後に

とりあえずやってみたが、まず日本語ドキュメントがあることがびっくり
しかもそのドキュメントがかなり細かく書かれているので、習得が難しい言語と言われているが少し頑張ってみようと思う
まだまだ書き方は全然わからないのでその辺を今後とも勉強していきたい
ほんとにRustを触ったのは初めてなので、間違った認識があったらどんどん言ってもらえるとうれしいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?