【5日目】Rust入門者に贈るRust入門者がRustと闘う物語 -Enumとmatchとif let-
最近、巷で話題のRust。システムプログラミング言語などの低レイヤー言語は触ったことがない筆者が、裸一貫で闘っていく、そんな物語です。
構造体があまりにもあっさりなので5日目もやっちゃいます。寝てました。
Enumとパターンマッチングについてです。URL
はじめに
※物語共通で使用するので不要な方は飛ばしてください。
私は、今業務で使用している言語や趣味でしようしている言語を含め、使用している全ての言語を独学で勉強してきました(せざるを得なったためですが)。 そして、毎回のことのように何度も壁にぶつかり、やっとの思いで扱えるようになるため、プログラミング言語の取得は、修行のようなものだと考えています。 これは私自身の能力不足なのかもしれませんが、大小はあるにしても誰しもが共通して感じるものなのかなと思いまして、後にRustに入門する方々に参考になればと思い、物語として記録しておきます。 ・初心者としてありがちな凡ミスやくだらないエラーを赤裸々に記録し、後続する方々の参考にして頂くため ファイル名:わかりやすく日本語で書いていますが、自らの環境で真似するのはお勧めできません。 表現の置換:私の最も得意として楽に記述できる言語がJavaScript(特にES6の書き方が好きです)のため、多くの部分でJavaScriptで置換して表現します。多くの人がわかるようにPythonで置換しようと思ったのですが、Pythonの書き方は(個人的に)独特なので、これまた知名度の高いJavaScriptで置換しようと考えました。 エラーやつまづきポイント:敢えて全てのエラーの記録をお伝えしますし、遠回りの手法をお伝えすることがあります。これは私を反面教師にして同じ轍を進まなくて済むためと考えています。 ※なお、全体を共通して「厳密性」より「なんとなく」を優先することが多々あるかと思います。その点ご了承ください。 ・今からRustに入門しようとしている方 ・業務でWebAssemblyが必要になりそうだった 再現性は不明ですが、念のため記しておきます。 基本的にWebフロントエンドとソフトウェア開発(金融系など)がメインですが、、 主にWebのお仕事をもらいつつ、頼まれたら業務に必要なアプリ作ったりしています。 ちなみに、よく聞かれるので、JavaとRubyの使用経験についても記載しておきます。 そもそも私自身Rustを学ぶ目的というか必要がありまして、実は金融系ソフトウェアの開発にあたりWebAssemblyを使うかもしれない可能性があり、そのため最終的にWebAssembly系で何かできたらと思っています。 恥ずかしながら、こうしてスケジュールを組んで学習というのが初でして、いまいち書き方がわかりませんが、以下のようにしております。 こちらを参考にして、スケジュールを組んでいます。 基本的にセクション毎を学んでいきます。量などにムラがあると思いますが、どうにかやり切る覚悟でいきましょう。 こちらはプロジェクト学習で最終的な復習をする前提で、一旦込み入ったことなどはスルーするかもしれないです。個人的に、80%くらいの精度で学習して、最後に実践的にボロボロになって、20%の総和を回収するのがなんだかんだ効率的だと思いますし、何より前に進んでいる実感からモチベが湧くので(ただ筋というかコアの部分は理解する必要があると思いますが)。 これらは1日でやり切れたらいいですが、文量なども多いので頑張れたらとしましょう。また、日数ではなくて、わからない点や疑問点をこのプロジェクトを通してゼロにしたいと考えているため、1つに1週間くらいかかることも想定されます。 一旦スルーします。必要に応じて見ます。 未定ですが、WebAssemblyで何かしたい。開発環境や参考サイト、学習フローチャートなどの概要について話します。
本物語について
本物語の目的
・自分用のメモ
本物語での記述について
対象読者
・プログラミング自体はある程度できるけど、組み込み系やOS設計などの低レイヤーの部分は触れたことがないという方
・開発自体は好きだが、言語がある程度使えるまでの道のりがしんどい方
Why Rust
・何か1つの言語を1から丁寧に学んでみたかった
・流行っていて今後本格的に低レイヤー言語としての地位を獲得しそうな雰囲気がした
・調べると、難しこと以外でデメリットが見当たらなかった
・名前が良い響き
開発環境とスキルセット
開発環境
PC: macOS Catalina 10.15.2
開発エディタ: VSCode
Rust: 1.41.0
スキルセット
業務で使用
Javascript(デフォルト)(Node.js, Vue.js, React.js, Jquery, etc)
Python(主にAPIなどのバックエンド)(あまりフレームワークは使わない)
Swift(単純な業務アプリ等)
PHP(Wordpressカスタマイズとか頼まれたら)(比較的苦手)
趣味程度
Golang(速度が必要な処理でAPI作成)
Julia(計量経済学を学習した際に)
R(計量経済学を学習した際に)
Swiftが一応システムプログラミング言語ですが、そこまで複雑じゃない業務管理アプリ程度しか作ったことがないので、システムプログラミングエンジニアとしては初心者です。
Golangは、Node.jsで速度処理に困った際に比較で学んだのがきっかけで業務での使用はないです。いつかお仕事できればいいな。
・Java(Kotlin等も)は無茶振りで超単純なアプリを作ったくらいで、趣味にも入らないくらいです。
・Rubyは、変なセミナーに誘われることが多かったので怖くて触れてません。
学習スケジュール(予定)
基礎学習
n日目
やること
1日目
Rustと周辺ツールのインストールとセットアップ
2日目
3. 普遍的なプログラミング概念
3日目
4. 所有権を理解する
4日目
5. 構造体を使用して関連のあるデータを構造化する
5日目
6. Enumとパターンマッチング
6日目
7. モジュール
7日目
8. 一般的なコレクション
8日目
9. エラー処理
9日目
10. ジェネリック型、トレイト、ライフタイム
10日目
11. テスト
11日目
13. 関数型言語の機能: イテレータとクロージャ
12日目
14. CargoとCrates.ioについてより詳しく
13日目
15. スマートポインタ
14日目
16. 恐れるな!並行性
15日目
17. Rustのオブジェクト指向プログラミング機能
16日目
18. パターンは値の構造に合致する
17日目
19. 高度な機能
プロジェクト学習
n日目
やること
1つ目
2. 数当てゲームをプログラムする
2つ目
12. 入出力プロジェクト: コマンドラインプログラムを構築する
3つ目
20. 最後のプロジェクト: マルチスレッドのWebサーバを構築する
付録
応用学習
参考サイト等(仮)
何をやったか(5日目)
Enum
定義
一見して構造体と似ている。
enum IpAddrKind {
V4,
V6,
}
使用方法
「::」で呼び出し。
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
より便利な定義
構造体では独立して定義しないと表現できないデータ型。
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
自由すぎるEnum
とても自由で好きです。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
・Quitには紐付けられたデータは全くなし。
・Moveは、中に匿名構造体を含む。
・Writeは、単独のStringオブジェクトを含む。
・ChangeColorは、3つのi32値を含む。
structだと分解する必要がある上にごちゃごちゃしてしまう。
implもあるよ
以下のようにstruct同様メソッドを作り出せます。
注意点は、&selfが登場するのに「.」ではなくて「::」記法を使用していることでしょう。
impl Message {
fn call(&self) {
// method body would be defined here
// メソッド本体はここに定義される
}
}
let m = Message::Write(String::from("hello"));
m.call();
NullがないRustとOptionの誕生
これは驚きでした。Rustにはnullがないようです。この節は結構大切そうな雰囲気がするので読み込みます。
プログラミング言語のデザインは、しばしばどの機能を入れるかという観点で考えられるが、 除いた機能も重要なのです。Rustには、他の多くの言語にはあるnull機能がありません。 nullとはそこに何も値がないことを意味する値です。nullのある言語において、 変数は常に二者択一どちらかの状態になります: nullかそうでないかです。
最強のOption<T>
nullがない代わりに、 値が存在するか不在かという概念を表現するのにこんなものがあるようです。<T>
というのは、ジェネリック型引数と言うらしく後に深く学べるようです。
最初に簡潔に説明すると、
・Option<T>
の列挙子はそのまま使える
・<T>
を持つ場合はあらゆるデータ型を一つ持てる
・<T>
を持たない場合はコンパイラにどういう型が入るか明示する必要がある
・Option<T>
を通常のデータ型のように処理する際にT
に変換する必要があったりする
enum Option<T> {
Some(T),
None,
}
Option<T>
は有益すぎて、初期化処理(prelude)にさえ含まれています。つまり、明示的にスコープに導入する必要がないのです。 さらに、列挙子もそうなっています: SomeとNoneをOption::の接頭辞なしに直接使えるわけです。 ただ、Option<T>
はそうは言っても、普通のenumであり、Some(T)とNoneもOption<T>
型のただの列挙子です。
<T>
という記法は、まだ語っていないRustの機能です。これは、ジェネリック型引数であり、ジェネリクスについて詳しくは、 第10章で解説します。とりあえず、知っておく必要があることは、<T>
は、Option enumのSome列挙子が、 あらゆる型のデータを1つだけ持つことができることを意味していることだけです。こちらは、 Option値を使って、数値型や文字列型を保持する例です。
Optionの呼び出し不要で自由に使える
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
ちなみに僕は以下のようにしてエラーが出て1時間くらい悩みました。
enum Option<T> {
None,
Some(T),
}
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}
ググった結果以下のようにenumの定義を外すことでエラーが無事実行できました。
fn main() {
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
}
以下のように標準ライブラリに定義されています。
というのは、既に内蔵されているという意味なのですね。勘違いしていました。要は、既に定義されているのに再定義しようとして名前競合が起きたようです。反省です。
明示的にOption<T>
とT
を変換する必要があるコード例
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y; //エラーが出る
match
複雑なif/else構文をスマートに書ける。他言語でもあるswitchなどのRust版。Enumが一つの値しか取らないので、matchはbreak書かなくてもいい雰囲気です。
コード例
Coinの列挙子のどれか一つに該当して、それぞれの場合で=>の先の数字を返すコード。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
JavaScriptのアロー関数のようですね。
引数を扱う時
以下のようにかけるようです。
#[derive(Debug)] // すぐに州を点検できるように
enum UsState {
Alabama,
Alaska,
// ... などなど
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn main() {
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
value_in_cents(Coin::Quarter(UsState::Alabama));
}
完全アロー関数ですね。ここまで自由だと表現できることが多そうな感じがします。
Option<T>
とのmatch
コード例
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
こちらは動きます。ただRustでは、None => None
を削除してしまうとエラーになるようです。
エラー例.sherror[E0004]: non-exhaustive patterns: `None` not covered (エラー: 包括的でないパターン: `None`がカバーされてません) --> | 6 | match x { | ^ pattern `None` not covered
matchは包括的でないといけないようです。
Option<T>
以外で、いちいち列挙するのが面倒な時
matchは包括的ではない場合ではエラーが出ます。そのため、u8など取りうる値が0~255まである場合などを考えると、全て列挙するのは限界があります。
Rustには、それを解消するプレースホルダー_
があるようです。
fn main() {
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
}
他言語でも頻出なので見ただけでわかる方も多いかもしれませんが、この場合は_
で1,3,5,7以外のu8で何も出力されないというわけですね。
if let
で簡潔な制御フロー
matchで特定の値(一つの値)だけ処理したいときに便利な構文
コード例
fn main() {
let some_u8_value = Some(0u8);
// _ => ()を足すのが毎回面倒
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
// if letを使うことで簡潔に書ける
if let Some(3) = some_u8_value {
println!("three");
}
}
少し発展的なコード例
#[derive(Debug)] // すぐに州を点検できるように
enum UsState {
Alabama,
Alaska,
// ... などなど
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn main() {
fn value_in_cents(coin: Coin) -> u32 {
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}
return count;
}
}
ざっくり要約
・EnumはStructの上位互換で、あらゆるデータ型を柔軟に扱える
・Rustにはnullがなく、代わりにOption<T>
というのがある
・matchでEnumで定義した値を分岐処理できる(関数も実行可能)
・matchの代わりにif let
という(時に)簡潔表現もある
よくわからなかったこと、問題点など
・Enumについて、なぜimplで定義したメソッドのシグニチャに&selfがあるときでも「::」記法でいいのか。
・Option<T>
で定義した値を<T>
に変換する方法
気をつけた方が良いこと
・matchは包括的でないといけない
・Option<T>
は定義されているので、再度定義するとエラーになる
まとめ
matchがとても便利でいいですね。
やはり、Option<T>
に慣れられるかが少し不安ですが、慣れれるよう頑張りたいと思います。
Author And Source
この問題について(【5日目】Rust入門者に贈るRust入門者がRustと闘う物語 -Enumとmatchとif let-), 我々は、より多くの情報をここで見つけました https://qiita.com/emckk/items/4dfca150fac966fedfd4著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .