RCIE-ジャンクのコード屋

主に自分のためにコーディングのTIPSを蓄積しています。

Rust 練習問題「ループなしのナベアツ」 map, collect

概要

  • 前回、ナベアツ処理を記述する際には「for」文で繰り返し処理を行った。
  • 範囲(1..=99)に対して「map」を使えば for 文が不要になるのでは、という試み。
  • for, loop, while を使わずに、ナベアツを実現してみよう。
仕様
  • 1 からカウントアップしていく。「1」「2」
  • 3 の倍数のときアホになる。「3🤪」...「11」「12🤪」
  • 数字に 3 が含まれるときもアホになる。「13🤪」
  • 100 になったら「オモロー」と言って終わる。「100😲オモロー!」
プログラム例
fn main() {
    print!("{}", (1..=99).map(ナベアツ変換).collect::<String>());
    println!("100😲オモロー!");
}

fn ナベアツ変換(n: i32) -> String {
    n.to_string() +
    match n % 3 == 0 || n % 10 == 3 || n / 10 == 3 {
        true => "🤪\n",
        false => "\n"
    }
}
解説
fn main() {
    print!("{}", (1..=99).map(ナベアツ変換).collect::<String>());
    // └ 1から99までを「ナベアツ変換」して文字列を作り出し、
    // └ それを collect でひとつの文字列に結合し、
    // └ それをprint!で出力する。
    println!("100😲オモロー!");
}

fn ナベアツ変換(n: i32) -> String {
    n.to_string() +    // 数値を文字列に変換
    match n % 3 == 0 || n % 10 == 3 || n / 10 == 3 {
        true => "🤪\n",    // 3で割り切れる・一の位が3・十の位が3
        false => "\n"    // それ以外
    }
}

Rust 配列の操作 iter, cloned, collect, filter, map

概要

  • 配列の中身をひとつずつ取り出すには iter() を使う。
  • 取り出した結果を新しい配列にまとめるには cloned(), collect() を使う。
  • 取り出したい中身を限定するには filter() を使う。
  • 取り出した中身に変更を加えるには map() を使う。
プログラム例
  • 配列を入力にして、全く同じ配列を出力する。つまりコピー。
  • iter().cloned(), collect::<Vec<_>>() を使う。
fn main() {
    let 配列 = vec![1, 2, 3, 4];
    let 配列2 = 配列.iter().cloned()    // ひとつずつ中身を並べる
        .collect::<Vec<_>>();     // それらを集めて配列にする
    println!("{:?}", 配列2);
}
プログラム例2
  • 配列を入力にして、0 を取り除いた配列を出力する。
  • filter(|&入力変数| bool値) を使う。
fn main() {
    let 配列 = vec![1, 0, 2, 0, 3, 0, 0, 4];
    let 配列2 = 配列.iter().cloned()    // ひとつずつ中身を並べる
        .filter(|&i| i != 0)    // 0でないものだけ残す
        .collect::<Vec<_>>();    // それらを集めて配列にする
    println!("{:?}", 配列2);
}
プログラム例3
  • 配列を入力にして、100未満のものは0にした配列を出力する。
  • map(|入力変数| 出力する値) を使う。
fn main() {
    let 配列 = vec![1, 2, 300, 100, 200, 3];
    let 配列2 = 配列.iter().cloned()    // ひとつずつ中身を並べる
        .map(|i| {   // iを入力にして
            if i < 100 {0}    // 100未満なら0
            else {i}    // それ以外ならiに変換(マッピング)する
        })
        .collect::<Vec<_>>();    // それらを集めて配列にする
    println!("{:?}", 配列2);
}
プログラム例4
  • 1から10の範囲を入力にして、3の倍数をゼロにした配列を出力する。
fn main() {
    let 配列 = (1..=10)
        .map(|i| {   // iを入力にして
            if i % 3 == 0 {0}    // 3の倍数なら0
            else {i}    // それ以外ならiに変換(マッピング)する
        })
        .collect::<Vec<_>>();    // それらを集めて配列にする
    println!("{:?}", 配列);
}

Rust 配列の使い方 vec!, iter, enumerate

概要

  • 同じ型の変数を大量に作る代わりに、配列を使うと便利。
  • 配列には固定配列と伸長可能な配列の2種類がある。
  • for文で今何番目を処理しているか知りたいときは「iter().enumerate()」を使う。

配列を使う場面

  • 5つの変数に入った数値を足し合わせるプログラムを考える。
fn main() {
    let0 = 3;
    let1 = 1;
    let2 = 4;
    let3 = 1;
    let4 = 5;
    println!("{}", 数0 +1 +2 +3 +4);
}
  • 今回は5つ変数を用意したが、配列を使うとスッキリする。
fn main() {
    let 配列 = [3, 1, 4, 1, 5];
    println!("{}", 配列[0] + 配列[1] + 配列[2] + 配列[3] + 配列[4]);
}
  • 大量の数の合計を計算する場合も、ループで回せば問題ない。
fn main() {
    let 配列 = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9];
    let mut 合計 = 0;
    for n in 配列 {
        合計 += n;
    }
    println!("{}", 合計);
}
伸長可能な配列 vec!
  • 上で説明した配列は、途中で長さを変えることができない。
  • 伸長可能な配列を作るには「vec!」を使う。
  • よほど速度に制約がない限りはこちらを使うとよさそう。
fn main() {
    let mut 配列 = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9];
    // └ あとで編集するので mut をつける。
    配列.push(3); // push(...)で末尾に追加できる
    let mut 合計 = 0;
    for n in 配列 {
        合計 += n;
    }
    println!("{}", 合計);
}
配列の繰り返し処理で「今、何番目?」
  • 繰り返し文「for n in 配列 {...}」では、今何番目の値を処理しているのか分からない。
  • 何番目なのかを得るためには「iter().enumerate()」を使う。
fn main() {
    let 配列 = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8 ,9, 7, 9];
    for i in 配列.iter().enumerate() {
        println!("{}番目は{}", i.0, i.1);
        // └ i.0 に何番目かという情報が入っている。
    }
}

Rust 文字列の長さ・分割・切り出し

概要

  • 文字列の操作方法を紹介する。
文字列の長さを求める
  • 文字列のまま操作する場合は chars().count()
  • 文字配列について操作する場合は len()
fn main() {
    let 文字列 = "あいうえおかきくけこさしすせそ";
    println!("{}", 文字列.chars().count());

    let 文字配列: Vec<char> = 文字列.chars().collect();
    println!("{}", 文字配列.len());
}
文字列を指定文字で分割する
  • つまり「aaa@bbb」を「@」で分割して「aaa」と「bbb」を得る操作。
  • 配列を得る場合には、すこし記述が長くなる。
fn main() {
    let 文字列 = "あいうえおかき-くけこさしすせそ";

    // 配列にする必要がある場合
    let 配列 = 文字列.split('-').fold(Vec::new(), |mut s, i| {
        s.push(i.to_string());
        s
    });
    println!("{:?}", 配列);

    // 配列にする必要がない場合
    for 分割 in 文字列.split('-') {
        println!("{}", 分割);
    }
}
文字列を切り出す
  • 「アイウエオ」の1~3を切り出すと「イウエ」
  • 短く表現したが、結構複雑なので関数にまとめた。
fn main() {
    let 文字列 = "あいうえおかきくけこさしすせそ";
    println!("{}", 切り出し(&文字列, 3, 5));
}

fn 切り出し(文字列: &str, 開始: usize, 終了: usize) -> String {
    let mut s = String::new();
    for i in 文字列.chars().enumerate() {
        match i.0 {
            n if n < 開始 => {continue}
            n if n > 終了 => {break}
            _ => {s.push(i.1)}
        }
    }
    s
}
補足
  • 1文字が1バイトだったり4バイトだったりするので、単純な操作もけっこう面倒である。

Rust 文字列のアクセス chars, nth, collect, []

概要

  • 文字列のn番目にアクセスするには nth(数) を使う。
  • 長い文字列ほどアクセスに時間がかかる。
  • char の配列にするという手もある。
プログラム例
  • 文字列「あいうえおかきくけこさしすせそ」について。
  • 10番目の文字を表示する。
fn main() {
    let 文字列 = "あいうえおかきくけこさしすせそ";
    match 文字列.chars().nth(10) {
        Some(文字) => println!("{}", 文字),
        _ => ()
    }
}
解説
  • nth(10) で文字列の10番目を辿っている。
  • 10番目というと「こ」になりそうだが、実際には「さ」。
  • 0番目が先頭なので、添え字と文字の関係は以下のようになる。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
(無し)
  • 文字によってデータの長さが違う。英数字=1、ロシア語=2、日本語=3(バイト)。
  • そのため、n文字目が何かを調べるたびに、先頭から調べていく必要がある。
  • n文字目が先頭から遠いほど、アクセスが遅くなる。
ランダムな場所にアクセスする場合
  • すべての文字を char(長さ8バイト)の配列に入れなおす。この作業は遅い。
  • 一度やってしまえば、どの文字も長さ8なので、指定の場所に一瞬でアクセスできる。
fn main() {
    let 文字列 = "あいうえおかきくけこさしすせそ";
    let 文字配列: Vec<char> = 文字列.chars().collect();
    println!("{}", 文字配列[10]);
}
先頭から順にアクセスする場合
  • わざわざすべての文字を配列に入れなおす必要はない。
  • for を使って、先頭から1文字ずつ取り出していく方法が最も効率的。
fn main() {
    let 文字列 = "あいうえおかきくけこさしすせそ";
    for 文字 in 文字列.chars() {
        println!("{}", 文字);
    }
}

Rust スレッドに終了命令を送る try_recv

概要

  • スレッドに終了命令を送るには channel を使う。
  • 受信の有無を確認するには try_recv を使う。
  • 受信内容を確認するには match を使う。
プログラム例
  • 本スレッドで1秒待機している間に、別スレッドはカウンタを回す。
  • 本スレッドは別スレッドを停止させて、カウンタを表示する。
use std::sync::mpsc::channel;
use std::thread::{sleep, spawn};
use std::time::Duration;

fn main() {
    let (送信機, 受信機) = channel();
    let 別スレッド = spawn(move || {
        let mut カウンタ = 0 as i64;
        loop {
            match 受信機.try_recv() {
                Ok("停止") => break,
                _ => (),
            }
            カウンタ += 1;
        }
        カウンタ
    });
    sleep(Duration::from_secs(1)); // 1秒停止
    送信機.send("停止").unwrap_or(()); // 停止命令
    let 結果 = 別スレッド.join().unwrap(); // 結果を取り出す
    println!("{}", 結果);
}
結果
  • 1秒間にカウンタを回せる数なので、環境によって差がある。
57502901
解説
fn main() {
    let (送信機, 受信機) = channel();
    let 別スレッド = spawn(move || {
        let mut カウンタ: i64 = 0;
        // └ i32だと桁が足りなくなる可能性を考えてi64にした。
        loop {
            match 受信機.try_recv() {
            // └ 受信していればOk(~)、なければErr(~)が得られる。
                Ok("停止") => break,
                // └ 停止命令を受けたら脱出する。
                _ => (),
                // └ それ以外なら何もしない。
            }
            カウンタ += 1;
        }
        カウンタ
        // └ 別スレッドの結果は「カウンタ」になる。
    });
    sleep(Duration::from_secs(1)); // 1秒停止
    送信機.send("停止").unwrap_or(()); // 停止命令
    let 結果 = 別スレッド.join().unwrap(); // 結果を取り出す
    println!("{}", 結果);
}
補足
  • 既に終了しているスレッドに対して send で送信すると、Err が返ってくるため、unwrap() ではプログラムが停止してしまう。
  • 今回のプログラムは、unwrap() の代わりに unwrap_or(()) を書いて、失敗時にそのまま処理を継続するようにした。

Rust スレッド間の通信 channel, send, recv

概要

  • 別のスレッドにデータを送受信するには channel を使う。
  • データを送信するには send を使う。
  • データを受信するには recv を使う。
プログラム例
  • 1から50までの和をスレッド1で計算する。
  • 51から100までの和をスレッド2で計算する。
  • 本スレッドでそれらの合計を表示する。
use std::thread::{spawn};
use std::sync::mpsc::{channel};

fn main() {
    let (送信機1, 受信機) = channel();
    let 送信機2 = 送信機1.clone();
    spawn(move || {
        送信機1.send(合計を求める(1, 50)).unwrap();
    });
    spawn(move || {
        送信機2.send(合計を求める(51, 100)).unwrap();
    });
    let 結果1 = 受信機.recv().unwrap();
    let 結果2 = 受信機.recv().unwrap();
    println!("{}", 結果1 + 結果2);
}

fn 合計を求める(開始: i32, 終了: i32) -> i32 {
    (開始..=終了).fold(0, |sum, i| sum + i)
}
解説
fn main() {
    let (送信機1, 受信機) = channel();
    // └ スレッド間の通信に使える送信機と受信機のセットが得られる。
    let 送信機2 = 送信機1.clone();
    // └ 送信機はコピーできる。
    spawn(move || {
    // └ move ◁「受信機1」をスレッド内で使えるようにする。
        送信機1.send(合計を求める(1, 50)).unwrap();
        // └ スレッド1からは、1から50の合計を送信する(エラー処理を省略)。
    });
    spawn(move || {
        送信機2.send(合計を求める(51, 100)).unwrap();
        // └ スレッド2からは、51から100の合計を送信する(エラー処理を省略)。
    });
    let 結果1 = 受信機.recv().unwrap();
    let 結果2 = 受信機.recv().unwrap();
    // └ 2回受信するまで待ち続ける(エラー処理を省略)。
    println!("{}", 結果1 + 結果2);
}
補足
  • join() でデータを受け取れるのは一回きりだが、recv() では何回でも受け取れる。
  • send() でエラーになるのは、相手のスレッドがすでに終了している場合。
  • recv() でエラーになるのは、受信機に対応する送信機がすべて破棄されていた場合。
  • 受信機が生きている間は、recv() すると無限待ち状態になるので注意。
  • 受信に時間制限を設けるには recv_timeout(Duration::from_secs(1)) のようにする。

Rust スレッドの生成と合流 spawn, join

概要

  • 複数の処理を並行して実行するにはスレッドを生成する。
  • スレッドを生成するには「spawn」を使う。
  • スレッドの終了を待つには「join」を使う。
  • 処理をある時間だけ停止させるには「sleep」を使う。
プログラム例
  • 別スレッドで0.5秒間隔で5回「▲」と表示する。
  • 本スレッドで0.3秒間隔で5回「○」と表示する。
use std::thread::{spawn, sleep};
use std::time::{Duration};

fn main() {
    let 別スレッド = spawn(|| {
        for i in 1..=5 {
            sleep(Duration::from_millis(500));
            println!("▲ {:.1}", (i as f64) * 0.5);
        }
    });
    for i in 1..=5 {
        sleep(Duration::from_millis(300));
        println!("○ {:.1}", (i as f64) * 0.3);
    }
    別スレッド.join().unwrap();
}
結果
  • 1.5のところは、ほぼ同時に実行されているので、前後することがある。
○ 0.3
▲ 0.5
○ 0.6
○ 0.9
▲ 1.0
○ 1.2
▲ 1.5
○ 1.5
▲ 2.0
▲ 2.5
解説
use std::thread::{spawn, sleep};
use std::time::{Duration};

fn main() {
    let 別スレッド = spawn(|| {
    // └ 以下のような処理のスレッドを生成し実行する。
        for i in 1..=5 {
            sleep(Duration::from_millis(500));
            // └ 500ミリ秒、停止する。
            println!("▲ {:.1}", (i as f64) * 0.5);
            // └ {:.1} ◁小数点以下1桁表示する。
            // └ i as f64 ◁整数を実数に変換する。実数同士は計算できる。
        }
    });
    for i in 1..=5 {
        sleep(Duration::from_millis(300));
        println!("○ {:.1}", (i as f64) * 0.3);
    }
    別スレッド.join().unwrap();
    // └ 別スレッドが終了するまで待機する。
}

Rust 関数で処理に名前をつける fn

概要
  • 関数を作ると、処理に名前をつけることができる。
  • 関数を作成するには「fn」を使う。
  • 入れる値と出る値の種類(型)を指定する必要がある。

プログラム例

  • 関数の名前は「ワンワン」とする。
  • 関数に数値(i32)を渡すと、文字列(String)が出てくる。
  • 渡した数値の数だけ「ワン!」を繰り返す。
fn main() {
    println!("{}", ワンワン(10)); // 数値を変えてもよい
}

fn ワンワン(回数: i32) -> String {
    let mut 文字列 = String::new();
    for _ in 1..=回数 {
        文字列 += "ワン!";
    }
    文字列
}
解説
  • 関数「ワンワン」に 3 を与えると、文字列「ワン!ワン!ワン!」が出てくる。
  • 処理に名前を付けて関数を作ると、プログラムの意味が分かりやすくなる。
  • それぞれの行の意味は以下の通り。
fn ワンワン(回数: i32) -> String {
    // └ fn ワンワン ◁関数を作る。
    // └ 回数: i32 ◁変数「回数」を入力として受け取る。型は i32(整数)。
    // └ -> String ◁この関数は String(文字列)を出力する。
    let mut 文字列 = String::new();
    // └ let mut ◁編集可能な変数を作る。
    // └ String::new() ◁空っぽの文字列を変数に入れる。
    for _ in 1..=回数 {
    // └ for ◁繰り返し処理
    // └ _ in 1..=回数 ◁1~回数の間、繰り返す。
        文字列 += "ワン!";
        // └ 文字列に「ワン!」を連結する。
    }
    文字列
    // └ セミコロン(;)がないので、この関数の出力は「文字列」となる。
}
変数の型
  • 変数にはいくつかの種類がある。
fn 関数(引数: i32) -> String { }
// └ ここでいう i32 や String が変数の種類を表す。「型」という。
いろいろな型(抜粋)
型の名前 保持できる値 使いかた
i8 整数 -128~127 let 変数: i8 = -99;
i16 整数 -32,768~32,767 let 変数: i16 = -999;
i32 整数 -2,147,483,648~2,147,483,647 let 変数: i32 = -99999;
i64 整数 約-9.2×1018~9.2×1018 let 変数: i64 = -9999999999;
i128 整数 約-1.7×1038~1.7×1038 let 変数: i128 = -999999999999999999;
u32 正の整数 0~4,294,967,295 let 変数: i32 = 99999;
f64 実数 約-1.7×10308~1.7×10308 let 変数: f64 = 3.14;
bool true(真=1)・false(偽=0) let 変数名: bool = true;
String 文字列(長さはいくらでも) let 変数名: String = "あいうえお";

Rust 分岐についてもっと詳しく if, else

概要

  • 「if」「else」でプログラムの流れを分岐できる。
  • 「match」よりも単純な分岐に向いている。
プログラム例
  • 持ち金が5000円以上なら「n円も持っている」と表示する。
  • 持ち金が5000円より少なければ「n円しか持っていない」と表示する。
fn main() {
    let 持ち金 = 4000; // 数値を変えてよい
    if 持ち金 >= 5000 {
        println!("{}円も持っている", 持ち金);
    } else {
        println!("{}円しか持っていない", 持ち金);
    }
}
解説
  • 「if 変数 >= 5000 { ~ }」 で、変数が5000かそれより大なら~する、という意味になる。
  • 当てはまらなければ「else { ~ }」が実行される。
  • 「else { ~ } 」は無くても問題ない。
  • 比較式の書き方は以下の通り。
比較式の書き方 意味 数学的にいえば
ア>イ アがイより大きい ア>イ
ア>=イ アがイより大きいか同じ ア≧イ
ア<イ アがイより小さい ア<イ
ア<=イ アがイより小さいか同じ ア≦イ
ア==イ アがイと等しい ア=イ
ア!=イ アがイと異なる ア≠イ
プログラム例2
  • 「match」で同じことは表現できる。
  • この場合は「if」「else」を使ったほうが短く書ける。
  • シンプルに書けるほうを選択すればよい。
fn main() {
    let 持ち金 = 7000; // 数値を変えてよい
    match 持ち金 {
        数 if>= 5000 => println!("{}円も持っている", 持ち金),
        _ => println!("{}円しか持っていない", 持ち金)
    }
}

Rust 練習問題「3がつくとアホになる」

概要
  • 2010年ごろに流行した「世界のナベアツ」のネタを Rust で再現する。
  • 分岐の「match」や「if」、繰り返しの「for」を使えばできるはず。
  • 以下の仕様を満たすようなプログラムを Rust で組んでみよう。

仕様

  • 1 からカウントアップしていく。「1」「2」
  • 3 の倍数のときアホになる。「3🤪」...「11」「12🤪」
  • 数字に 3 が含まれるときもアホになる。「13🤪」
  • 100 になったら「オモロー」と言って終わる。「100😲オモロー!」

プログラム例

  • 処理ごとに関数に分割した。
  • 記述は長いが、分かりやすい。
fn main() {
    for i in 1..=99 {
        ナベアツ処理(i);
    }
    オモロー();
}

fn ナベアツ処理(n: i32) {
    match 三の倍数か(n) || 三を含むか(n) {
        true => アホになる(n),
        false => 普通に言う(n)
    };
}

fn 三の倍数か(n: i32) -> bool {
    n % 3 == 0
}

fn 三を含むか(n: i32) -> bool {
    一の位が3か(n) || 十の位が3か(n)
}

fn 一の位が3か(n: i32) -> bool {
    n / 10 % 10 == 3
}

fn 十の位が3か(n: i32) -> bool {
    n % 10 == 3
}

fn アホになる(n: i32) {
    println!("{}🤪", n)
}

fn 普通に言う(n: i32) {
    println!("{}", n)
}

fn オモロー(){
    println!("100😲オモロー!");
}

プログラム例

  • 同じ処理を、より短く表現した。
  • 暗号めいている。
fn main() {
    for i in 1..=99 {
        let s = match i / 10 % 10 == 3 || i % 10 == 3 || i % 3 == 0 {
            true => "🤪", false => ""
        };
        println!("{}{}", i, s);
    }
    println!("100😲オモロー!");
}

Rust 繰り返しループ for, loop, break

概要

  • 処理の繰り返し(ループ)をするには for を使う。
  • 繰り返し回数が読めないときは loop を使う。
プログラム例
  • 1 から 10000 までの数の合計を表示する。
fn main() {
    let mut 合計 = 0; // 後で変更されるので mut が必要
    for 回数 in 1..=10000 {
        合計 += 回数;
    }
    println!("{}", 合計);
}
解説
  • 「for 変数 in 1..=10000」で、1~10000 の繰り返しができる。
  • 繰り返しカウンタは、変数に格納される。
プログラム例2
  • さっきと同じで、1 から 10000 までの数の合計を表示する。
fn main() {
    let mut 合計 = 0;
    let mut 回数 = 1;
    loop {
        合計 += 回数;
        match 回数 {
            10000 => break, // 10000なら脱出
            _ => 回数 += 1 // そうでなければカウンタを増やす
        }
    }
    println!("{}", 合計);
}
解説
  • 「loop」のブロック内は無限に実行され続ける。
  • 無限ループだと困るので、回数が10000の時点で分岐して脱出した。
  • 記述が長くなるので、普段は「for」を使っておけばいい。

Rust 数値で分岐する match, ..=, |

概要

  • 数値で分岐するには match を使う。
  • 数値の範囲で処理を分けたい場合は「..=」を使う。
  • いくつかの数値の場合に対応する場合は「|」を使う。
プログラム例
  • 文字列の入力を要求する。
  • 0~59 なら「不合格」を表示する。
  • 60~99 なら「合格」を表示する。
  • 100 なら「満点」を表示する。
  • それ以外なら「範囲外」を表示する。
use std::io::*;

fn main() {
    let 入力 = コンソール入力();
    let 数値 = 入力.parse::<i32>().unwrap(); // 変換エラー対策は省略
    let 出力 = match 数値 {
        0..=59 => "不合格",
        60..=99 => "合格",
        100 => "満点",
        _ => "範囲外"
    };
    println!("{}", 出力);
}

fn コンソール入力() -> String {
    let mut バッファ = String::new();
    stdin().read_line(&mut バッファ).unwrap();
    バッファ.trim().to_string()
}
解説
  • 「0..=100」は、0から100まで(100を含む)の範囲を表す。
プログラム例2
  • 文字列の入力を要求する。
  • 1, 3, 5, 7, 8, 10, 12 なら「31日」を表示する。
  • 4, 6, 9, 11 なら「30日」を表示する。
  • 2 なら「28日」を表示する。
  • それ以外なら「範囲外」を表示する。
use std::io::*;

fn main() {
    let 入力 = コンソール入力();
    let 数値 = 入力.parse::<i32>().unwrap(); // 変換エラー対策は省略
    let 出力 = match 数値 {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => "31日",
        4 | 6 | 9 | 11 => "30日",
        2 => "28日",
        _ => "範囲外"
    };
    println!("{}", 出力);
}

fn コンソール入力() -> String {
    let mut バッファ = String::new();
    stdin().read_line(&mut バッファ).unwrap();
    バッファ.trim().to_string()
}

Rust 文字列で分岐する match, as_str

概要

  • 分岐するには match を使う。
  • 文字列を比較対象にするには as_str を使う。
コード
  • 入力されたのが「いぬ」なら「ワン」と出力する。
  • 入力されたのが「ねこ」なら「ニャー」と出力する。
  • 入力されたのが「ひと」なら「オギャア」と出力する。
  • それ以外が入力されたら「?」と出力する。
use std::io::*;

fn main() {
    let 入力 = コンソール入力();
    let 出力 = match 入力.as_str() {
        "いぬ" => "ワン",
        "ねこ" => "ニャー",
        "ひと" => "オギャア",
        _ => "?"
    };
    println!("{}", 出力);
}

fn コンソール入力() -> String {
    let mut バッファ = String::new();
    stdin().read_line(&mut バッファ).unwrap();
    バッファ.trim().to_string()
}
解説
fn main() {
    let 入力 = コンソール入力();
    // └ コーンソール入力された文字列を「入力」に入れる。

    let 出力 = match 入力.as_str() {
    // └ 入力.as_str:伸ばせる文字列を、固定文字列に変換する。
    // └ match:固定文字列の内容で場合分けする。

        "いぬ" => "ワン",
        "ねこ" => "ニャー",
        "ひと" => "オギャア",
        // └ "いぬ" に一致すれば "ワン" を「出力」に入れる。

        _ => "?"
        // └ 一致しなかった場合 "?" を「出力」に入れる。
    };
    println!("{}", 出力);
}

Rust 入力した文字列を数値に変換する parse, match

概要

  • 文字列を数値に変換するには parse を使う。
  • 単純な実装だと、変換失敗時にプログラムが落ちる。
  • 変換失敗時の処理を書くには match を使う。
コード
  • 文字列の入力を要求し、99 を掛け算する。
use std::io::*;

fn main() {
    let 入力 = コンソール入力();
    let 数値: i32 = 入力.parse().unwrap();
    println!("{} × 99 = {}", 数値, 数値 * 99);
}

fn コンソール入力() -> String {
    let mut バッファ = String::new();
    stdin().read_line(&mut バッファ).unwrap();
    バッファ.trim().to_string()
}
  • 7ケタ程度の整数を入力した場合は成功する。
  • ケタの大きすぎる整数や、数値以外の入力をするとプログラムが落ちてしまう。
改善
  • 数値以外の入力をされたら「無効な値」と表示して終了する。
use std::io::*;

fn main() {
    let 入力 = コンソール入力();
    let 数値 = match 入力.parse::<i32>() {
        Ok(n) => n, // 成功時
        Err(_) => { // 失敗時
            println!("{}", "無効な値");
            return
        }
    };
    println!("{} × 99 = {}", 数値, 数値 * 99);
}

fn コンソール入力() -> String {
    let mut バッファ = String::new();
    stdin().read_line(&mut バッファ).unwrap();
    バッファ.trim().to_string()
}
解説
  • 各行の意味は以下の通り。
fn main() {
    let 入力 = コンソール入力();
    // └ 変数「入力」にはコンソール入力した文字列が入る。

    let 数値 = match 入力.parse::<i32>() {
    // └ 入力.parse::<i32>():変数「入力」を i32(整数)に変換する。
    // └ match:変換の結果によって処理が分岐する。

        Ok(n) => n,
        // └ 変換に成功したら、値を取り出して「数値」に入れる。

        Err(_) => {
        // └ 変換に失敗した場合の処理が続く。

            println!("{}", "無効な値");
            return
            // └ return:関数「main」から脱出する。以下は処理されない。
        }
    };
    println!("{} × 99 = {}", 数値, 数値 * 99);
}
  • 文字列.parse::<i32>()で、文字列を整数に変換する。
  • i32 は、二進法の32ケタで表せる整数を表す。-2,147,483,648 ~ +2,147,483,647
  • 桁数の異なるいくつかの変数の種類がある。
    • i8(二進法 8 ケタ:-128 ~ +127)
    • i16(二進法 16 ケタ:-32,768 ~ 32,767)
    • i64(二進法 64 ケタ:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)