LoginSignup
2
3

More than 1 year has passed since last update.

【研修資料】Rustから始めるプログラミングの準備

Last updated at Posted at 2022-07-01

第一章 導入:dancer:

想定する読者像:dancer:

以下の方向けに、「情報科学系の大学1年生レベル」の説明をします。

  • SEじゃない
  • プログラミングって何
  • システム開発って何してるの
  • がっつり興味あるわけでもない
  • 何も知らんけどVBAってやつを書いてExcelマクロを作りたい

本資料の趣旨:dancer:

プログラミングそのものの解説に入る前に、プログラミングが何をする作業なのか説明します。
単にプログラミングと言っても一連の作業要素で構成されています。

それぞれの用語は留めなくてOKです。
設計のところまでをPMさんだったり上流SEさん、コーディングはプログラマさん、テストはテスタさんがそれぞれ担当されています。
色んなチームがいます。

この資料の説明範囲:dancer:

主にコーディングについて説明します。
用語についてはその都度解説します。

アプリケーションが動く仕組み:dancer:

アプリケーションソフトウェアとは

  • 使用者の業務に応じて開発された専用プログラム
  • (例)メールアプリ「Outlook」
  • PCだとソフト、スマホだとアプリ、といったように略し方が違ったりします。
  • 本資料では広義に解釈してアプリケーションという用語を使用します。

アプリケーションの種類

  • このページは「Qiita」というWebアプリによって作成されたものです。
  • OutlookやExcelとは異なり、ブラウザ上で稼働するアプリケーションをWebアプリといいます。
    • ブラウザはMicroSoft Edge やGoogle Chromeなどの"インターネットを見るやつ"です。
    • かたや、ローカル(自分自身のPC内のこと)で作動するOutlookのようなアプリケーションをネイティブアプリといいます。

Webアプリの成り立ち:dancer:

私たちが見ているブラウザの後ろでは、「サーバ」が頑張ってくれています。

  • 下に表示したアイコンは、プログラミング言語を示しています。
    • [Front] HTML / CSS はほぼ必ず使います。
    • [Back] 何かしらのプログラミング言語を1つ以上使います。
  • プログラミング言語がたくさんある理由は、それぞれ得意領域が異なるためです。

ネイティブアプリの成り立ち:dancer:

  • Webアプリでは、難しい処理は外部のサーバが担当していましたが、ネイティブアプリでは異なります。
  • 例えばOutlookの場合、「新しいメール」ボタンを押下したとき、その処理はインターネット上のどこかに任せるのではなく、アプリ内部で処理します。
  • 処理速度はインターネット速度に依存しません。
  • Webアプリと違って機能群をローカルにインストールする必要があります。:pray:

フロントエンドとバックエンド:dancer:

アプリケーションの開発は主に以下の2つで構成されます。

  • フロントエンド
    • ユーザインターフェース(UI)を司る領域です。
    • 利用者がアプリケーションと関わる接点全てをUIといいます。
  • バックエンド
    • 処理(ロジック)やデータ管理を司る領域です。

どのような処理をしてどのように表示するかを実装できたら、それはもうアプリケーションです。

第二章 プログラミング言語について:grapes:

準備:grapes:

開発(コーディング)作業を行うには、開発環境を調達する必要がありますが、便利なモノを使うので必要有りません。

開発環境

実行の仕方

  • Runをクリック
    Animation.gif

保存の仕方

  • ファイルとして保存できないので、URLを共有することで入力情報を保存します。
    Animation1.gif

これは何が起こっている?

  • Run は「プログラムを走らせて」います。
  • 人間がわかる「プログラミング言語」をコンピューターが分かる「機械語」に翻訳(コンパイル)することでいい感じに実行してくれます。

言語選定:grapes:

  • 実はプログラミング言語は数百種類もありますが、主要な言語は50にも満たないです。
  • いずれにせよ数は多いですが、「コンパイラ型」や「インタプリタ型」など大別できます。
  • 言語は言語から開発されることもあり、規則性や文法(シンタックス)が類似している言語は沢山あります。
  • つまり、母国語となるようなプログラミング言語を1つ習得できれば、他の言語もやんわりと読めるようになります。
    • 著者は学生時代に「C言語」に触れ、基本的な流れを汲みました。前職でJava、現職でVB.NETでどちらも基幹システム系の案件に携わっています。
  • 言語は案件の性格などから慎重に選定するものですが、本研修ではRustにしました。※興味があったので。

前提条件に関するFAQ:grapes:

  1. VBAでマクロが作れたら満足するのに、なぜわざわざRust?
    • VBAはExcelのために誕生したExcelのためのマクロ言語です。
    • 簡単な集計処理を自動化したいという要件で活躍しますが、それ以上の複雑な要件を実装するとなるとかなり心が折れます。
    • VBAの構文はかなりレガシーで、大文字小文字も区別しませんし、メンテナンス性も低いです。
      • 手軽に始められますが、出来ないことが多く、書きやすくはないです。
      • 実装する際のコーディング力でなんとかなりますが、その時点で初心者向きではないのでは?と個人的に感じます。
    • もっと感動する素敵な言語は沢山あります。Rustの素敵さはこれから感じてください。
    • Rustだとこう書くんだけど、VBAだとどう書くんだろうという思考回路になると個人的にうれしいです。
  2. RustだけじゃWebアプリ作れないでしょ?
    • その通りです。本資料ではUIが伴うアプリケーションの実装まではたどり着けません。
    • 「来年の今日は何曜日」だとか「大量のデータの足し算の答え」を出したりして終わります。
  3. この資料って何がゴール?
    • 第五章の最終問題が実装できたらゴールです。
    • そこまで行った時には、「VBAの文法を調べる」だけでマクロが書けるようになっています。たぶん。

第三章 基礎の構文:fried_shrimp:

例題1 文字列の出力:fried_shrimp:

(ⅰ) Hello, World! の文字を出力しましょう。

// 例題1
fn main(){
    println!("Hello, World!");
}
  • ほかの言語でも、最初に行う伝統的慣習です。

fn -> 関数を定義するキーワード
main -> mainという名前の関数だよ
main() -> main関数は何も入力を受け取ったりしてないよ
main(){} -> 波括弧の中にmain関数が何をするのか書くよ
println! -> ビックリマークは関数ではなくマクロ。printlnという名前のマクロを呼んでる。
println!("Hello, World!") -> printlnマクロに「Hello, World」の文字列を"(ダブルクォート)で囲って入力として渡してる。
; -> 文章終わりますって意味。「。」と同じ。

  • 表示させたい「文字」は""で囲ってあげる必要があります。
  • "{}"の中が空白でも動きます。
  • mainという名前の関数を定義してあげたことが重要です。

予備知識:fried_shrimp:

(ⅰ) 関数??

  • 数学でf(x) = 2xという関数にx=2を代入したら4という答えが返ってきたのを思い出してほしい。
  • fという関数に、x=2という入力を渡すと、4と出力される。
  • mainという関数は、入力無しで、Hello, World!と出力される。

(ⅱ) VBAだとどう書くの?(余談)

VBAだと以下となります。あまり変わらないですね。

Sub HelloWorld()
    Debug.Print "Hello, World!"
End Sub

(ⅲ) インデント

  • 行頭にスペースを入れて{}の中であることを視覚的に示す表現をインデントといいます。
  • 要するにただの字下げです。Rustの世界では、インデントは半角スペース4つです。
  • インデントが乱れているソースは見づらく、以下のソースは「汚い」とされます。
    • エラーにはなりませんが、学生の場合は再提出を求められます。社会人の場合は信用を失います。
fn         main()
               {
   println!("Hello, World!")
    ;                    }

(ⅳ) コメント

  • 超重要です。
  • //の次に文字を書くと日本語でもコメントが書けます。
  • 自分が書いたプログラムでも、どんな処理を書いたのか忘れちゃいます。また書きながらでも、1000行も書いてるとこの関数なんだっけ、、ってなったりします。
  • プログラムを書く際は必ずコメントを書きましょう。

演習1:fried_shrimp:

(ⅰ)Hello, world に改行コード「\n」を使用して2行に分けましょう。

Hello
World!

例題2 演算:fried_shrimp:

(ⅰ) 1 - 2 を計算して表示しましょう。

fn main() {
    // 整数の引き算
    println!("1 - 2 = {}", 1_i32 - 2);
}

表示なので、またprintln!マクロの出番ですね。
,(カンマ)の後ろに式を書くことで、{}の中に結果が格納されます。
ここで、1の後ろに何かついているのを不思議に思うかもしれません。

i32は「マイナスがある整数ですよ。」ということを教えてくれる「データ型」です。
ちなみに、u32は「マイナスがない整数(自然数)ですよ。」の「データ型」です。
ちなみに、f32は「実数(小数点あり)ですよ。」の「データ型」です。

ソースコード中で「1」は「整数の1」なのか「実数の1.0」なのか分かりません。
このように記述されたものを文字列リテラルといいます。
1が何の文字列リテラルなのかプログラムに教えてあげるために「_i32」と明記しています。

_を数値と型の間に入れることで可読性を高めていますが、_は無くてもエラーは発生しません。

(ⅱ) 命題「真または偽」は「真」 を計算して表示しましょう。

fn main() {
    // 単純な論理演算
    println!("true OR false is {}", true || false);
}

論理演算はご存知でしょうか。数学の集合で習ったかなと思います。
このtruefalseはプログラミングの世界で頻出ですので、今一度おさらいしましょう。
以下は真偽表です。

P Q P かつ Q P または Q
True True True True
True False False True
False True False True
False False False False

Rustの世界では、または=||、かつ=&&となります。

予備知識:fried_shrimp:

(ⅴ) リテラル1には型を明記してるのにリテラル2には書かなくていいの?

Rustは優秀なので何のリテラルなのか推論できます。1がi32なら2もi32だろう、といった具合です。

演習2:fried_shrimp:

(ⅰ) -43 / 2 を計算して表示しましょう。

-43 / 2 = -21.5

(ⅱ) 真かつ偽または偽 を計算して表示しましょう。

true AND false OR false is false

例題3 変数の宣言:fried_shrimp:

(ⅰ) 1.5 - 2.0 を計算して表示しましょう。

fn main() {
    // 変数を宣言
    let number_x: f64 = 1.5;
    let number_y: f64 = 2.0;
    // 実数の引き算
    println!("1.5 - 2.0 = {}", number_x - number_y);
}

いよいよプログラミングっぽくなってきましたね。
変数というのは値を所有している所有者です。
letは変数を宣言するキーワードです。
宣言された変数のnumber_xnumber_yは適当に名付けました。konna_kanji_で単語を繋げる形をスネークケースと呼びます。Rustの変数名はスネークケースで定義してあげてください。
: f64はそれぞれの変数のデータ型です。

number_xは1.5を、number_yは2.0を所有しています。

ちなみに、f32を宣言しておいて整数リテラル10000を所有しようとするとエラーになります。
実数リテラル10000.0を使いましょう。

(ⅱ) 命題「真または偽」は「真」 を計算して表示しましょう。

fn main() {
    // 変数を宣言
    let number_x: bool = true;
    let number_y: bool = false;
    // 真または偽
    println!("true || false = {}", number_x || number_y);
}

boolは真理値型を示します。実数型のf32と同じ仲間です。

(ⅲ) 変数の値を変更してみましょう。

fn main() {
    // 変数を宣言
    let mut number_x: u32 = 2;
    let number_y: u32 = 3;
    
    // 値の更新
    number_x = 4;
    
    // 2(じゃなくて4)× 3
    println!("{}", number_x * number_y);
}

変数は基本的に値の更新は許可されていません。
しかし、宣言時にmut(ミュートと発音します)をつけておくとことで、途中の値更新を許可します。

演習3:fried_shrimp:

(ⅰ) 変数を使って4163411.0 * -963642.0を計算して表示しましょう。また、途中で値を更新して変化を確認しましょう。

-4012037600000

例題4 タプル・配列・ベクタ:fried_shrimp:

(ⅰ) タプルに値を持たせましょう。

fn main() {
    // 様々な型を値に持つタプル
    let long_tuple = (1_u8, 2_u16, 3_u32, 4_u64,
                      -1_i8, -2_i16, -3_i32, -4_i64,
                      0.1f32, 0.2f64,
                      'a', true);
    // インデックス(何番目かを示す添え字)を用いて、タプル内の要素を参照できる。
    println!("long tuple first value: {}", long_tuple.0);
    println!("long tuple second value: {}", long_tuple.1);
    println!("{:?}", long_tuple);
}

型の紹介をするのが面倒だったので、タプルついでに列挙してみました。
u -> 符号ナシ整数(Unsigned Integer)
i -> 符号アリ整数(Integer)
f -> 浮動小数点数(Floating Point)
の頭文字の後ろにビット数を付けたのがデータ型名の仕組みです。
ビット数??と気になるかもしれませんが、スルーで行きます。
沢山あるので基本的に、小数点がある数字には「f64」、整数には「i32」を使ってください。

タプルの説明全然してませんね。
1つの変数に色んな値を詰め込みたいときがあります。
それがタプルです。
変数名の後ろに.をつけて、何番目かを書いてあげると中身を取れます。
中身1つずつじゃなくて全部見たい!ってときはprintlnマクロの{}の中を{:?}としてあげると見せてくれます。こういう小細工をフォーマットと言います。

疲れたら休憩しましょう。

(ⅱ) 配列に値を持たせましょう。

fn main() {
    // 整数を5個持たせる場合
    let array_x: [i32; 5] = [1, 2, 3, 4, 5];
    // 0を500個持たせる場合
    let array_y: [i32; 500] = [0; 500];

    // インデックスは0から
    println!("first element of the array: {}", array_x[0]);
    println!("second element of the array: {}", array_y[1]);
}

タプルの時と違って、()ではなく[]で囲ってください。
書き方が違うだけで複数の値を扱うタプルと同じじゃないか?と疑問を持たれても仕方ないです。
大きな違いは、持たせるリテラルのデータ型は統一する必要があるという点です。True入れた後に10.5を入れたりはできません。

また、各要素へのアクセスが.0ではなく[0]ですね。
実はここが大切で、例えば変数i=0があった場合、
タプルtに対して、t.iはエラーになり、
配列aに対して、a[i]はエラーになりません。覚えておきましょう。

よくあるケースですが、「5個までって宣言したけど6個目を追加したい...」場合は対応不可です。予め確保された領域に対して有効な構文です。

(ⅲ) ベクタに値を持たせましょう。

fn main() {
    // ベクタの初期化には`vec!`マクロが使用できる。
    let mut v = vec![1i32, 2, 3];
    println!("Initial vector: {:?}", v);

    // 新しい要素をベクタの最後に挿入することができる。
    v.push(4);
    println!("Vector: {:?}", v);
}

前セクションで「配列の要素数変えられないの不便じゃない?」と感じましたか?「上限数が変わってはならない」場合に使いたいときは配列が適当ですが、追加したい場合のためにRustは素敵なマクロvec!を用意してくれました。

変更を加えたいため、宣言時にmutを付け加えています。
v[4]はまだ存在しないため、ベクタ型変数vのメソッドを使います。
メソッドとは、型そのものに備わった関数です。便利なメソッドが型にはたくさん実装されていて、変数名の後ろに.をつけると呼び出せます。
ここではpushを使うので、v.push()を使います。
「ベクタ型変数vのメソッドpushに4を渡します」という処理ですね。

ベクタはたくさん使います。

演習4:fried_shrimp:

(ⅰ) 以下のエラーになるプログラムを修正しましょう

fn main() {
    // 様々な型を値に持つタプル
    let x = (1_u8, 2_u16, 3_u32, 4_u64,
                      -1_i8, 0.2f64,
                      'a', true);
    // 8つめを表示
    println!("long tuple first value: {}", x.8);
    // 全部表示
    println!("{}", x);
}

(ⅱ) 以下のエラーになるプログラムを修正しましょう

fn main() {
    // 実数を3つ
    let array_x: [3] = [1, 2.4, 3.0];
    // インデックスは0から
    println!("first element of the array: {}", array_x[3]);
}

(ⅲ) 以下のエラーになるプログラムを修正しましょう

fn main() {
    // ベクタの初期化には`vec!`マクロが使用できる。
    let v = vec![1, 2, true, 3];
    println!("Initial vector: {:?}", v);

    // 新しい要素をベクタの最後に挿入することができる。
    v.push(2);
    println!("Vector: {:?}", v);
}

例題5 条件分岐:fried_shrimp:

(ⅰ) if 文を使って数値の大小を比較しましょう。

fn main() {
    let n = 5;

    if n < 0 {
        print!("{} is negative", n);
    } else if n > 0 {
        print!("{} is positive", n);
    } else {
        print!("{} is zero", n);
    }
}

if -> もしもこうだったらこう、を書き始める最初の言葉。ifの次は (半角スペース)の後に「式」を書きます。
n < 0 -> 変数nが0より小さいのか?を表現するのに数学の不等式を使います。
else if -> ifの条件に合わないときの、次に評価される式を書く。この場合、n < 0じゃないなら、という意味です。else ifは何個書いてもいいです。
else -> 全部違うならという意味です。

この例示ソースコード、コメント行を消してみたんですがどうでしたか?少しだけ頭に入りずらいのではないかなと思います。書いといて損はないです。

(ⅱ) match 文を使ってパターンマッチングをしましょう。

fn main() {
    let number = 13;
    
    match number {
        // 単一の値とのマッチをチェック
        1 => println!("いち!"),
        // いくつかの値とのマッチをチェック
        2 | 4 | 6 | 8 => println!("ぐうすう!"),
        // 特定の範囲の値とのマッチをチェック
        11..=15 => println!("11から15の間っぽい"),
        // その他の場合の処理
        _ => println!("Ain't special"),
    }
}

超大切ですので1つ1つと説明します。

match 変数 {}という構文ですね。「変数」が何だったら何をするの?を書いていきます。
1 => -> 1だったら
2 | 4 | 6 | 8 => -> 2または4または6または8だったら
11..=15 => -> 11から15の間だったら
_ => それ以外なら
それぞれの式は,で繋ぎましょう。

予備知識:fried_shrimp:

(ⅵ) 式

言い忘れていましたが、{}の中をブロックと言います。
そして今まで、ずっと文末に;をつけていましたね。
ブロックの中で;を付けない場合、それは文ではなく式になります。
if/elseの中で;を付けず、その値を変数に渡すことが出来ます。
以下の例では、if/elseの結果をbig_nに渡しています。

fn main() {
    let n = 5;

    let big_n =
        if n < 10 && n > -10 {
            println!(", and is a small number, increase ten-fold");
            // セミコロン(`;`)をつけず、返り値を返す
            10 * n
        } else {
            println!(", and is a big number, halve the number");
            n / 2
        };
    //   ここにセミコロンを付けるのを忘れないように!
    println!("{} -> {}", n, big_n);
}

文および式は、意識しましょう。

演習5:fried_shrimp:

(ⅰ) 以下のエラーになるプログラムを修正しましょう

fn main() {
    let name = "tyco";

    if name == "tyco" {
        print!("{} is tyco", name);
    } else if {
        print!("{} is not tyco", name);
    }
}

(ⅱ) 変数xに14928を持たせて7で割り切れないならNice、割り切れたらVery Niceと表示しましょう。

「割り切れるかどうか」 は「余りが0かどうか」です。
余りを出す演算子は % です。
(例) 20 % 5 = 0
(例) 10 % 3 = 1

例題6 ループ処理:fried_shrimp:

(ⅰ) while 文を使って3の倍数を表示しましょう。

fn main() {
    // カウンタとなる変数
    // 数えるためだけの変数のことをカウンタと慣習的に呼ぶ
    let mut n = 1;

    // `n`が101以下である場合のループ
    while n < 101 {
        // 3のときだけ
        if n % 3 == 0 {
            println!("{}は3の倍数じゃ", n);
        }
        // カウンタをインクリメント
        // 1を足して次に進めることをインクリメントと呼ぶ
        n += 1;
    }
}

if文と組み合わせてみました。
while 式 {}の式が満たされる間、{}の中を継続的に周回します。
この場合、{}の中でnをインクリメントするため、100回目で終了します。

(ⅱ) for 文を使ってベクタから値を取り出しましょう。

fn main() {
    let numbers = vec![1,2,3];

    for n in numbers.iter() {
        match n {
            2 => println!("マッチ!!"),
            // 2じゃないなら挨拶
            _ => println!("Hello"),
        }
    }
}

ベクタ型変数と組み合わせてみました。
前のセクションではpushメソッドを使いましたね。iterメソッドは一個ずつ取り出してくれます。イテレータといいます。

for n in 何かという構文では、「何か」の中の要素を1つずつ変数nに持たせます。
nなんて宣言してないけど、、」と心配された方、大丈夫です。for文で使う変数は宣言せずに使えます。

イテレータによって「何か」の中から値を取り出せなくなったらループが終わります。回数を書かなくていいのは楽ですね。

ちなみに、ベクタ関係無く先ほどの(ⅰ)と同じケースを処理したい場合、以下のように書きます。nは勝手にインクリメントされていきます。

for n in 1..=100 {
}

演習6:fried_shrimp:

(ⅰ) 1から100までの平均値を求めましょう。

505

例題7 関数の定義:fried_shrimp:

(ⅰ) 新しい関数を定義して処理を書き分けましょう。

fn main() {
    let x = 1_u32;
    // 適当な関数を呼ぶ
    tekitona_kansu(x);
}

// xを受け取る適当な関数を定義
fn tekitona_kansu(x : u32) -> (){
    println!("{}", x);
}

今まで、全ての処理をmain関数に書いていましたね。
一般的に、そのようなことは無いです。
main関数はとても特別な関数で、プログラムを実行すると一番最初に実行されます。ここから他の自作関数を呼ぶようにしましょう。

fn 関数名(引数の名前 : 型名) ->(返り値の型名){処理}で関数が定義できます。

main関数でx=1tekitona_kansuに渡しています。
tekitona_kansuは受け取ったxを処理に使い、()を返します。つまり何も返してません。

返り値があるパターンは以下みたいな感じです。

fn main() {
    let x = 2_u32;
    // 適当な関数を呼ぶ
    if tekitona_kansu(x) {
        println!("{}は1だ。",x)
    } else {
        println!("{}は1じゃない。",x)
    }
}

// xを受け取る適当な関数を定義
fn tekitona_kansu(x : u32) -> bool {
    if x == 1 {
        true
    } else {
        false
    }
}

演習7:fried_shrimp:

(ⅰ) 以下のプログラムの処理を別の関数に切り分けましょう。

fn main() {
    // カウンタとなる変数
    let mut n = 1;

    // `n`が101以下である場合のループ
    while n < 101 {
        if n % 15 == 0 {
            println!("fizzbuzz");
        } else if n % 3 == 0 {
            println!("fizz");
        } else if n % 5 == 0 {
            println!("buzz");
        } else {
            println!("{}", n);
        }
        // カウンタに1を追加
        n += 1;
    }
}

例題8 構造体と列挙型:fried_shrimp:

(ⅰ) 構造体をつかって経理業務を表現しましょう。

// 月次仕訳の構造体
struct ShiwakeGetsuji{
        getsudo: u32,
        uriage: u32,
        genka: u32,
        arari: u32,
    }
// 構造体に便利なモノを追加するよ
impl ShiwakeGetsuji{
    // 関連関数は引数にselfをとらない
    fn new(month: u32) -> ShiwakeGetsuji {
        ShiwakeGetsuji{
            getsudo: month,
            uriage: 1300,
            genka: 300,
            arari: 0,
        }
    }
    // メソッドは引数にselfをとる
    fn cal_arari(&self) -> u32 {
        self.uriage - self.genka
    }
}

fn main() {
    let mut getsuji = ShiwakeGetsuji::new(202207);
    println!("月度={0}, 売上={1}, 粗利={2}", getsuji.getsudo, getsuji.uriage, getsuji.arari);
    getsuji.arari = getsuji.cal_arari();
    println!("月度={0}, 売上={1}, 粗利={2}", getsuji.getsudo, getsuji.uriage, getsuji.arari);
}

急に難しくなりましたか?
今までの流れから大きく外れてはないので、一つ一つ見てみましょう。

fnとは異なるキーワードが2つもありますね。
structは関数ではなく構造体を宣言します。
struct 名前 {フィールド名: フィールドの型}という形です。フィールドというのは、その構造体を組成する部品だと思ってください。

構造体はただのデータの形を定義しているだけなので、それをもとにimpl(実装)を定義します。newは貰った月度を使って構造体を生成します。
let mut getsuji = ShiwakeGetsuji::new(202207);は、「202207のShiwakeGetsuji型の構造体getsujiを作ります。」という意味になります。

(ⅱ) Enumを使って入試を表現しましょう。

// 「いずれかの科目の点数ですよ」という意味の列挙型
enum ShikenKamoku {
    Kokugo(u32),
    Sugaku(u32),
    Rika(u32),
    Shakai(u32),
    Eigo(u32),
}
fn main(){
    // 各点数を入れておく
    let score_japanese = ShikenKamoku::Kokugo(143);
    let score_math = ShikenKamoku::Sugaku(121);
    let score_science = ShikenKamoku::Rika(179);
    let score_history = ShikenKamoku::Shakai(60);
    let score_english = ShikenKamoku::Eigo(155);
    // 国数理英社の配列
    let total = [score_japanese, score_math, score_science, score_history, score_english];
    // for文とmatch式でループ
    for x in total.iter() {
        match x {
            ShikenKamoku::Kokugo(tensu) => println!("国語は{}点",tensu),
            ShikenKamoku::Sugaku(tensu) => println!("数学は{}点",tensu),
            ShikenKamoku::Rika(tensu) => println!("理科は{}点",tensu),
            ShikenKamoku::Shakai(tensu) => println!("社会は{}点",tensu),
            ShikenKamoku::Eigo(tensu) => println!("英語は{}点",tensu),
        }
    }
}

構造体の「フィールド」は全部に値を入れていましたね。
列挙型の「列挙子」はそれ単体で使います。それぞれの変数をグループ化してるだけですね。
こういう風なグループ化をしていると、match式などで使いやすいです。

あとしれっと配列のイテレータを使っていますね。配列に入れた順番で1つずつxに入れてくれてます。

予備知識:fried_shrimp:

(ⅶ) 構造体と列挙型の名前がスネークケースじゃない

ここだけCamelCaseを使います。メリハリがつきますね。ちょっとだけ復習しましょう。

  • 変数や関数に命名する(スネークケース)
    • hoge_fuga, tadaima_okaeri
  • 構造体や列挙型に命名する(キャメルケース)
    • HogeFuga, TadaimaOkaeri

演習8:fried_shrimp:

(ⅰ) 3人分の適当な名前、年齢、性別を表示しましょう。また、構造体と列挙型のパターンで書き分けてみましょう。

第四章 おしゃれな構文:dolphin:

例題9 クロージャ:dolphin:

fn main() {
    // クロージャは関数だが、変数に所有させられる
    let c  = |i|i * i;
    // クロージャにiを渡して呼び出す
    println!("closure_inferred: {}", c(5));
}

|入力|出力というスマートな構文ですね。
cという変数はletで宣言されていますね。
これまで扱っていた変数は、わかりやすい文字や数値を所有していました。
今回は、iがまだ何かも分からない状態で「iを受け取ったらi*iを返す」という「処理」が格納されています。

「どんな使い方できるの?」の問いにお答えします。
今まで、関数に渡してきたものは必ず値だったり値が複数入った配列でしたね。
クロージャも、関数に渡すことが出来ます。

fn call_me<T: Fn()>(hello: T) {
    hello();
}
fn main() {
    // Define a closure satisfying the `Fn` bound
    // `Fn`境界を満たすクロージャを定義
    let c = || println!("Hello, World!");
    call_me(c);
}

厳密には違う説明をするのですが、わかりやすく以下のように読んでください。

  1. 入力なしで「Hello, World!」と叫ぶクロージャを定義
  2. そのクロージャを所有する変数c
  3. 関数call_mecを渡す
  4. Fn()という型でT型を作る
  5. クロージャが入ったcはT型の変数helloとして受け取られる
  6. helloを実行する

予備知識:dolphin:

(ⅷ) ジェネリック境界

このセクションは、本資料のレベルからすると発展的なので読み飛ばして構いませんが、先ほどの「厳密には違う」という説明でモヤっとされた方のために書いておきます。

fn hoge<T>(fuga: T) {
    println!("piyo");
}

<T>ってなに?と疑問を持たれるかと思います。
Tは、あらゆる型です。これを「ジェネリック型」といいます。
ジェネリック型を受け取ることが出来る関数hogeを「ジェネリックな関数」といいます。

// `Fn`トレイトを実装している`T`という型の変数`t`を引数として取る`printer`という関数を定義
fn printer<T: Fn()>(t: T) {
    println!("{}", t);
}

Tをあらゆる型ではなく、一定条件を満たす型に対してのみ適用したいケースがあります。
つまり、ジェネリックに境界を設けます。<T: Fn><T>の部分集合のようなものです。

クロージャを受け取りたい場合

クロージャに型はありません。
そのため、受け取る際にはに境界を設けて明示的にしてあげる必要があります。
クロージャの場合はFnトレイトの実装が妥当です。

(ⅸ) トレイト

ジェネリクス境界の話で「トレイト」ってなんだよ、と思われたかと思います。
トレイトとは、あらゆる型となりうるものに対して定義されたメソッドの集合のことです。
抽象的な表現でしたが、トレイトそのものが抽象的なものです。
「トレイトを実装する」という言葉があるように、肉付けされる骨みたいなものです。
人間トレイトにアランキャサリンを実装するイメージで覚えておきましょう。
人間トレイトには、以下のようなことしか書いてません。

trait 人間{
    // 処理の中身まで書かない
    fn 話す() -> ;
    fn 聞く() ->();
    // デフォルトの挙動を決めることもできる。あとから付け足す。
    fn 歩く() ->{
        右足から出す;
    }
}

ジェネリックやトレイトは、まだ分からなくても構いません。
オブジェクト指向の考え方を理解する必要があります。興味が出てからでいいので、いつか調べてみてください。

例題10 所有権と借用:dolphin:

wip

予備知識:dolphin:

(ⅹ) スライス

wip

例題11 エラーハンドリング:dolphin:

wip

第五章 最終問題:crab:

開発環境の調達:crab:

ここからは、Playgroundでは実装できません。
全て無料なので以下を準備してください。

  • rustup
    • 64ビット版を選択してください。
  • VSCode
    • 最高のメモ帳です。
  • Build Tools
    • コンパイラです。何も気にすることなく入れてください。

開発プロジェクトフォルダの作成:crab:

  1. 適当なフォルダをデスクトップに作って、右クリックで「Codeで開く」を選択します。
    ds.png

  2. Ctrl + Shift + @ キーを同時に押します。
    出てきた黒い窓は「ターミナル」と言います。コマンドを打つ場所です。

  3. 次のコマンドを打ちましょう。

cargo new black_jack
cd black_jack
cargo build
cargo run

image.png

注意
VSCodeのUIがカスタマイズされているため、このキャプチャの外観と違っても安心してください。

分かりにくい場合は、公式参照をお願い致します。

以下の要件を満たすプログラムを作成しなさい。:crab:

ブラックジャックゲーム

  • カードは52枚
    • ジョーカー2枚を抜いたトランプ
    • もちろん同じカードはナシ
  • プレイヤー(実行者側)とディーラー(プログラム側)の1対1の勝負
  • 開始時、プレイヤーとディーラーはそれぞれカードを2枚引く
  • プレイヤーの1枚目と2枚目、ディーラーの1枚目を表示
  • プレイヤーは3枚目を引いた場合に3枚の合計が「21」を超えそうだと思うなら「スタンド」を選択
  • 次を引いても「21」を超えなさそうなら「ヒット」を選択
    • JとQとKは「10」として扱う
    • Aは「1」もしくは「11」どちらか、合計点が21に近づくように扱う
    • スタンド ... カードを引かずに勝負する / ヒット ... もう一枚引く
    • ヒットを選択して合計値が22以上ならバースト(プレイヤーの敗北)
    • ヒットを選択して合計値が20以下なら再度ヒット or スタンドを選択(ヒットは何回でもOK)
  • プレイヤーが合計値21以下で勝負を待っている状態になったらディーラーは合計値が17以上になるまで無条件にカードを引く
    • バーストしたらプレイヤーの勝利
  • プレイヤーとディーラーが引き終えたら勝負。より21に近い方の勝ち

入力の受けつけ方
こんな感じです。

fn main() {
    println!("何か文字を入力して下さい:");
    let mut word = String::new();
    std::io::stdin().read_line(&mut word).ok();
    let answer = word.trim().to_string();
    println!("{}", answer);
}
  • 書き終わったら私の実装例を見てミスを指摘しまくりましょう。
  • 私も初学者の1人なので、調べながら半日以上かかりました。

実装例:crab:

保険をかけるようでちょっとアレですが、以下の実装例の問題点はかなりあります。

  1. main関数が60行もあり、各関数を適切に分けられてない
  2. match文の使い方が美しくない
  3. エラーハンドリングしてない
    優しい方がいらっしゃったらプルリク下さい。

UIが伴うアプリケーション:crab:

  • 最終問題で作っていただいたアプリケーションは、UIがありませんのでCUIアプリ(見た目無し)と呼びます。
  • かたや、皆さんが普段目にするアプリケーションは、GUIアプリと呼びます。
  • GUIアプリを作るためには、全く感覚が異なりますが、この研修の経験は無駄にはなりません。
  • というわけで次回は簡単なWebアプリを作ってみましょう。

まとめ :couch:

お疲れ様でした。

最終問題まで実装できた方は、公式リファレンスが読めるようになっています。
「ExcelVBAが書ければそれでよかったのに、、」といった方には、今ではVBAが若干窮屈に感じるかも知れません。
第三章はどの言語にもあり、他の言語でも対応する構文を調べることが出来ますが、第四章はRust固有です。

Rustでは、ありがたい人たちが作ってくれたクレートによって、Excelも操作できます。
Excelの中でしか動かないマクロではなく、保守性の高いネイティブアプリケーションを開発してくれると幸いです。

Rustプログラマは Rustacean と呼ばれます。
最後に、Rustacean として成長するための良著を紹介します。

以上、プログラミングへの招待と見せかけたRustへの誘導でした。
これからもよろしくお願いします。

2
3
1

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
2
3