--- title: Rust勉強会 tags: Rust, WebAssembly, TDD description: Rust勉強会 lang: ja image: https://www.rust-lang.org/static/images/rust-logo-blk.svg slideOptions: transition: slide --- # Rust勉強会 ワイ史上最強の言語! Rustをちゃんと理解してあげる ![Rust Logo](https://www.rust-lang.org/static/images/rust-logo-blk.svg) ↑ Rustのロゴ(開発者が自転車を趣味にしていたことからこのアイコンらしい。) --- ## 注意事項 - 現時点でのRustのバージョンは1.43.1です。 - 主催者はRust初心者です。資料もかなり雑です。(抑えるところは抑えているつもり。。) - 言うまでもありませんが、言語は手段です。「この言語さえ勉強すれば大丈夫!」という魔法の様な言語は***存在しません***ので、用法・用量を守って正しくお使いください。 - とはいえRustは素晴らしい言語ですので、「**Rustが好きすぎる病**」が発症する可能性があります。ご注意ください。 - 質問はいっぱいしてください! - 普段はこんなにしっかり資料を作りません。(今回はちょっと自己研鑽のためにも資料を作ってみました。) --- ## 勉強会の目的 - Rustの立ち位置や使い所をなんとなく理解する。 - Rustの言語仕様(特に所有権)をある程度理解する。 - Rustを通じてモダンなプログラムの書き方(Option, Result, パターンマッチ)を理解する。(「別にRust使う予定無いし!」という方のためにもPythonやC言語などのサンプルコードも交えて説明します。) --- ## 今日のお品書き **1. Rustとは** ここでRustの全体感を話します。 **2. Rustの文法** Rustの文法をリソース管理の部分と関数型の部分のみ説明します。 **3. おすすめ開発手法** Rust開発環境や開発手法を説明します。 **4. 実際にアプリを作る** TDDで簡単なアプリケーションを作ってみます。 **5. おまけ:WebAssembly** WebAssemblyについて簡単に説明。 **5. おわりに** まとめます。 --- ## 1. Rustとは ### まずは[Wikipedia](https://ja.wikipedia.org/wiki/Rust_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E))を読んでみる ざっくりとした情報を取得するには、Wikipediaはうってつけ。 Rustのキーワードを洗い出しました。 #### システムプログラミング言語 > Rust(ラスト)はMozillaが支援するオープンソースの**システムプログラミング言語**である。 > Rust言語は速度、並行性、安全性を言語仕様として保証する**C言語、C++に代わる**システムプログラミングに適したプログラミング言語を目指している。 [color=#907bf7] :::spoiler システムプログラミング言語とは? システムプログラミング言語というのは、物理的なハードウェアへのより直接的なアクセス手段を提供する言語。 - オペレーティングシステム - ユーティリティソフトウェア - デバイスドライバ - コンパイラ - リンカ 基本的にはこれらを作ることが目的として設計されている。 そう言う意味では、アプリケーション向けの言語とは異なる。 (アプリケーション向きでないとは言ってない。) システムプログラミング言語例 - C - C++ - Ada - D - Rust - Swift ::: #### オープンソース > プロジェクトはオープンソースの**コミュニティベース開発**で進行しており、言語仕様(検討段階含む)、ソースコード、ドキュメントはオープンソースライセンスで公開されている。 [color=#907bf7] Mozillaっぽいよね。 #### マルチパラダイム > Rustはマルチパラダイムプログラミング言語であり、**手続き型プログラミング**、**オブジェクト指向プログラミング**、**関数型プログラミング**などの実装手法をサポートしている。 [color=#907bf7] 基本的にはC言語に似ている。 :::spoiler 関数型プログラミング?? 手続き型やオブジェクト指向は有名だが、関数型プログラミングやその言語は聞き馴染みないかもしれない。 関数型言語は、関数を引数ないし返値として渡せる第一級関数として扱える。 詳しくは別の勉強会でやりたいが、関数型プログラミングの特長としては、こんな感じ。 - 宣言的(定義的)にプログラムを書けるので(慣れたら)読みやすい - 代数的データ型を利用して抽象的なコードが書ける - *比較的*安全な(副作用のない)コードを書ける ::: #### 速度はC言語並み > コンパイル基盤にMIRとLLVMを用いており、**実行時速度性能はC言語と同等程度**である。 [color=#907bf7] まぁ、とにかく速い。 :::spoiler LLVMって何さ? TODO** ::: #### メモリ安全 > 強力な型システムとリソース管理の仕組みにより、**メモリ安全性が保証**されている。 [color=#907bf7] 強力な型システムというのは、「静的型付けかつ強い型付け」のこと。 リソース管理の仕組みというのは、「ボローチェッカー」のこと。 :::spoiler **静的型付け?** プログラムを実行しながら型検査を行う => 動的型付け ```python= a = 10 a = "hoge" # => OK! ``` プログラムを実行前に型検査を行う => 静的型付け ```rust= let mut a = 10; // => 型推論でこれはOK! a = "hoge"; // => NG!! ``` **強い片付け?** 実行時に型変換を許さない。 **ボローチェッカー** Rustには、「所有権」という概念があり、一つのリソースは一つの所有者(変数)にのみ関連づけられる。 ボローチェッカーは所有権の競合および不正利用を検証する。 ::: #### 愛されている言語 > Rustは2016〜2019年の間Stack Overflow Developer Surveyで「最も愛されているプログラミング言語」で一位を獲得し続けている。 [color=#907bf7] 4年連続1位!! --- ### [Rustの公式サイト](https://www.rust-lang.org/ja)をみてみよう ~効率的で信頼できるソフトウェアを誰もがつくれる言語~ #### なぜRustか? ##### パフォーマンス > Rustは**非常に高速でメモリ効率が高く**ランタイムやガベージコレクタがないため、**パフォーマンス重視**のサービスを実装できますし、組込み機器上で実行したり他の言語との調和も簡単にできます。 [color=#907bf7] :::spoiler ガベージコレクタとは? Todo: ::: :::spoiler 他の言語との調和? FFIやWeb Assemblyについて Todo: ::: ##### 信頼性 > Rustの豊かな型システムと**所有権モデル**によりメモリ安全性とスレッド安全性が保証されます。さらに様々な種類のバグをコンパイル時に排除することが可能です。 [color=#907bf7] 所有権についてはあとで説明。 ##### 生産性 > Rustには優れたドキュメント、有用なエラーメッセージを備えた使いやすいコンパイラ、および統合されたパッケージマネージャとビルドツール、多数のエディタに対応するスマートな自動補完と型検査機能、自動フォーマッタといった一流のツール群が数多く揃っています。 [color=#907bf7] #### Rustでつくろう ##### コマンドライン > Rustの強力なエコシステムならCLIツールを素早く作れます。Rustはアプリのメンテナンスを信頼できるものにし、その配布も簡単です。 [color=#907bf7] ##### WebAssembly > Rustを使ってJavaScriptをモジュール単位で高速化しましょう。npmに公開しwebpackでバンドルすればすぐに使えます。 [color=#907bf7] :::spoiler WebAssemblyとは?? Todo: ::: ##### ネットワーク > 予測可能なパフォーマンス。極小のリソースフットプリント。堅固な信頼性。Rustはネットワークサービスにぴったりです。 [color=#907bf7] 簡単に言うと、サーバーにもどうぞってこと。 ##### 組込み > 低リソースのデバイスがターゲットですか? 高レベルの利便性を損なわずに低レベルの制御をしたいですか? Rustにお任せください。 [color=#907bf7] #### Rustの活用事例 それぞれ確認してください。Firefox、DropBox、Cloudflareの事例があります。 #### 参加しよう ##### Rustを読む ドキュメントはめちゃくちゃ充実している。 ##### Rustを観る YouTubeも面白い。RFCの議論なんかしている。 ##### コードに貢献する - [rust/CONTRIBUTING\.md at master · rust\-lang/rust](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) 今年中には、1プルリクを! #### 謝辞 ##### 個人 ##### 企業スポンサー そうそうたる企業 (余談だけど、[このページ](https://www.rust-lang.org/ja/sponsors)リロードするたびに企業の順番が変わる) - AWS - Google Cloud - Microsoft Azure - Mozilla - 1Password TODO: wikiと公式だけからじゃなくて、記事も載せたい ## 2. Rustの文法 **参考リンク** | リンク | 内容 | | -------- | -------- | | [The Book](https://doc.rust-lang.org/book/) | 文法が一通り書かれている。最初から全部読もうとすると大変かも。| | [Rust By Example](https://doc.rust-lang.org/book/) | コードで説明するタイプ。説明は少なめ。練習問題もあり。| | [標準ライブラリ](https://doc.rust-lang.org/std/index.html) | Rust標準ライブラリガイド。最初から読む物じゃなくて、参照用。 | | [CARGOブック](https://doc.rust-lang.org/cargo/index.html) | RustのパッケージマネージャーCargoの説明。参照用。 | | [RUSTDOCブック](https://doc.rust-lang.org/rustdoc/index.html) | ドキュメントの作成方法や書き方の説明。参照用。 | | [RUSTCブック](https://doc.rust-lang.org/rustc/index.html) | RustのコンパイラRustcの説明。参照用。 | TODO: 日本語ドキュメントを書く 他にも色々あるので、[Rustを学ぶ](https://www.rust-lang.org/ja/learn)を確認してください。 この章では、[The Book](https://doc.rust-lang.org/book/)と[Rust By Example](https://doc.rust-lang.org/book/)を参考にしながら、説明します。 ### Hello, World! まずは、はろわ。簡単。 Rustのエントリーポイントは`main()`関数。 `println!()`というマクロで標準出力できる。 ```rust= fn main() { println!("Hello, World!"); } ``` :::spoiler その他の出力系マクロ - format! (文字列をフォーマット) - print! (↑を出力) - println! (↑の改行あり版) - eprint! (print!のエラー出力版) - eprintln! (println!のエラー出力版) ::: :::spoiler フォーマットの使い方 ```rust= fn main() { // 基本はこんな感じ。 println!("{} days", 31); // => 31 days // 数字を利用して、こんな風にも書ける println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); // => Alice, this is Bob. Bob, this is Alice // 引数を利用することも可能。 println!("{subject} {verb} {object}", object="the lazy dog", subject="the quick brown fox", verb="jumps over"); // => the quick brown fox jumps over the lazy dog // :を利用すると特別な値に変換できる println!("{} of {:b} people know binary, the other half doesn't", 1, 2); // => 1 of 10 people know binary, the other half doesn't // 空文字で埋めることも。 println!("{number:>width$}", number=1, width=6); // => 1 // 0埋めももちろん可能。 println!("{number:>0width$}", number=1, width=6); // => 000001 } ``` ::: ### 変数と可変性 Rustの変数は標準で**不変(immutable)**! ```rust fn main() { let x = 5; // letで変数宣言する println!("The value of x is: {}", x); x = 6; // コンパイルエラー!! println!("The value of x is: {}", x); } ``` もちろん可変(mutable)な変数も扱える。 その場合は、`mut`というキーワードを利用する。 ```rust= fn main() { let mut x = 5; println!("The value of x is: {}", x); x = 6; println!("The value of x is: {}", x); } ``` :::spoiler immutableの何がうれしいのか - 意図しない不具合を抑制できる - プログラムの可読性、保守性が向上する TODO: ::: シャドーイングが可能。地味に便利。 ```rust= fn main() { let x = 5; let x = x + 1; // <= シャドーイング! let x = x * 2; // <= シャドーイング! println!("The value of x is: {}", x); } ``` これは、変数を新たに定義しているので、再代入とは異なる。 新たな変数名を定義するまでもない時に便利。 ### データ型 **スカラー型** - 整数 - 浮動小数点数 - 論理値 - 文字 ***整数型*** | 大きさ | 符号付き | 符号なし | | ------- | ------- | ------ | | 8-bit | i8 | u8 | | 16-bit | i16 | u16 | | 32-bit | i32 | u32 | | 64-bit | i64 | u64 | | 128-bit | i128 | u128 | | arch | isize | usize | `arch`というのは、アーキテクチャ依存という意味。 4バイト(32-bit) or 8バイト(64-bit) ```rust= fn main() { let x = 5; // <= デフォルトだとi32 let y: u8 = 1; // こんな感じに型を指定。 } ``` ***浮動小数点数*** | 大きさ | 符号付き | | ------- | ------- | | 32-bit | f32 | | 64-bit | f64 | ***論理値*** `true` or `false`簡単。 実は1バイトのメモリを食ってる。(理論上は1bitで済むはず。) ```rust= fn main() { let x:bool = true; let y = false; let z = 100 < 10; } ``` ***文字列*** ```rust= fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻'; //ハート目の猫 } ``` **複合型** - タプル型 - 配列型 ***タプル型*** タプルは**複数の型(異なっても良い)**をはり合わせることができる型。 後述struct(構造体)と基本的には変わらない。 ```rust= fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1); } ``` 値の取り出し方 ```rust= fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; // こんな風にパターンマッチで取り出せる println!("The value of y is: {}", y); } ``` アクセス方法 ```rust= fn main() { let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; // こうやってアクセス let six_point_four = x.1; // こうやってアクセス let one = x.2; // こうやってアクセス } ``` ***配列型*** **同じ型**の値を複数まとめられる型。 ```rust= fn main() { let a = [1, 2, 3, 4, 5]; let first = a[0]; let second = a[1]; let where = a[10]; // <= 実行時にエラーになるよ。 } ``` :::spoiler Vec型との比較 Rustには、Vec型という 常に固定長の要素があることを確認したい時に有効。(Vec型との比較) TODO: ::: ### 関数 `fn`キーワードを利用して関数を宣言。簡単。 ```rust= fn main() { println!("Hello, world!"); another_function(); } fn another_function() { println!("Another function."); } ``` 引数をとるときは? 簡単。 ```rust= fn main() { another_function(5); } fn another_function(x: i32) { println!("The value of x is: {}", x); // xの値は{}です } ``` 戻り値があるとは? ```rust= fn five() -> i32 { 5 // 最後の値が評価される // return 5; でもOK!! } fn main() { let x = five(); println!("The value of x is: {}", x); } ``` 引数と戻り値の複合 簡単。 ```rust= fn main() { let x = plus_one(5); println!("The value of x is: {}", x); } fn plus_one(x: i32) -> i32 { x + 1 // x + 1; と書くとコンパイルエラー } ``` :::spoiler 式と文 文は、なんらかの動作をして値を返さない命令。 式は結果値に評価される。 ```rust= fn main() { let y = 6; // これは式。 } ``` ```rust= fn main() { let x = (let y = 6); // だから、これはNG!!(コンパイルエラー) } ``` ちなみに全ての言語が代入で値を返さないわけじゃ無い。 たとえば、Rubyでは代入で値が返る。(C言語も) ```ruby= y = 10 # y = 10 が 10を返す x = y = 10 ``` Rustで同じことやりたかったら、こう! ```rust= fn main() { let x = 5; let y = { let x = 3; x + 1 // => 値を返している }; println!("The value of y is: {}", y); } ``` ::: ### コメント 飛ばします。あとでドキュメントコメントについてやるかも(TODO) ### フロー制御 ***if式*** 書き方は一般的なif文と一緒。 ```rust= fn main() { let number = 6; if number % 4 == 0 { println!("number is divisible by 4"); } else if number % 3 == 0 { println!("number is divisible by 3"); } else if number % 2 == 0 { println!("number is divisible by 2"); } else { println!("number is not divisible by 4, 3, or 2"); } } ``` ただ、式なので、値を返します。 ```rust= fn main() { let condition = true; let number = if condition { 5 } else { 6 }; println!("The value of number is: {}", number); } ``` 同じ型じゃ無いとダメ。 ```rust= fn main() { let condition = true; let number = if condition { 5 } else { "six" // => 数値型と異なる!! }; println!("The value of number is: {}", number); } ``` ***loop*** これはただの無限ループ ```rust= fn main() { loop { println!("again!"); } } ``` ***while*** whileもあるよ。 ```rust= fn main() { let mut number = 3; while number != 0 { println!("{}!", number); number = number - 1; } println!("LIFTOFF!!!"); } ``` ***for*** forもあるよ。 ```rust= fn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { println!("the value is: {}", element); } } ``` Pythonのこんな書き方便利ですよね。 ```python= def main(): for number in range(10): print(number) for (index, val) in enumerate(["hoge", "fuga", "piyo"]): print(f"{index} : {val}") ``` Rustだとこう書きます。 ```rust= fn main() { for number in 1..10 { println!("{}", number); } for (index, val) in ["hoge", "fuga", "piyo"].iter().enumerate() { println!("{} : {}", index, val); } } ``` ### 所有権 :::spoiler 本題にはいる前に ***スタックとヒープ*** 実行時にプログラムが使用できるメモリ領域にはスタックとヒープの2種類がある スタック領域 - 高速 - データは既知の固定サイズ - 秩序 - last in, first out (push, pop) ヒープ領域 - 低速(スタックに比べて) - 可変サイズ - 無秩序 - allocate して pointer を管理する ***メモリーリークを防ぐ戦略*** 1. 自力 - C, C++など - メリット - ちゃんと実装すれば、高速。 - デメリット - ちゃんと実装しなければ、死ぬ。 2. ガベージコレクション(GC, ガベコレ) - ほぼ全ての言語が装備(Java, PHP, Python, Ruby, Go, Haskell...) - 実装方法 - 参照カウント - マーク・アンド・スイープ - ... - メリット - メモリ管理を意識する必要がない。 - デメリット - 自力戦略に比べると速度は劣る。 - 場合によってはStop the world?(現代のGCでは改善されているかも。) 3. 所有権 - Rust - メリット - CやC++と同じくらい高速 - デメリット - 学習コスト?(ボローチェッカーで管理してくれるものの) ::: <br/> > 所有権はRustの最もユニークな機能であり、これのおかげでガベージコレクタなしで安全性担保を行うことができるのです。 故に、Rustにおいて、所有権がどう動作するのかを理解するのは重要です。 ***所有権のルール*** 1. Rustの各値は、所有者と呼ばれる変数と対応している。 2. いかなる時も所有者は一つである。 3. 所有者がスコープから外れたら、値は破棄される。 #### 変数スコープ スコープの概念は他の言語と同じ。 ```rust= { // sは、ここでは有効ではない。まだ宣言されていない let s = "hello"; // sは、ここから有効になる // sで作業をする } // このスコープは終わり。もうsは有効ではない ``` まとめると、 - sがスコープに入ると、有効になる - スコープを抜けるまで、有効なまま ## 3. おすすめ開発手法 俺氏が1週間で考えた最強の開発手法(多分一般的です。) こういうの、大事。 ### 0. インストール まずは、`rustup`コマンドを取得する。 これで`cargo`コマンドも一緒に入る。 ```shell= curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` 最新のRustをインストールしておく。 ```shell= rustup update ``` ### 1. VSCode + RLSを使う やっぱりVSCode最強説。軽いし、いろんなプラグインがある。 TODO: VSCodeプラグイン作る勉強会やりたい。 [RLS](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust)を入れると無敵。 一つだけ、Rustパスを設定するのを忘れずに。 ``` /Users/hikaru/.cargo/bin/rustup ``` - TDD - Cargo watch - cargo clippy - cargo make ## 4. 実際にアプリを作る コマンドラインツールでいいかな。 ## 5. おまけ:WebAssembly WebAssemblyについて少し。 TODO:Yewで何か作ってみる? ### wasmとは ## 6. おわりに まとめ。 - Rustを勉強すると、組み込みやってみようかな。とか、WebAssemblyやってみようかなとか思う。 - めちゃくちゃドキュメントやサイトに力を入れている - ## メモ ### data types - option - result - iterator ### control flow - match - iteration ### ```cpp= void f(Foo* ptr) { if(!ptr) { return; } ptr->g(); } ``` いけてないところ - nullチェックしてる - ptrが変更される可能性がある - ptrが無効な値の可能性がある ```rust= fn f(ptr: Option<&Foo>) { match ptr { Some(ptr) => ptr.g(); None => {} } } ``` ### エラーハンドリング まずは`panic!`を理解 ```rust fn exit(x: i32) { if x == 0 { panic!("we got a 0") } else { println!("things are fine!") } } fn main() { exit(1) exit(0) } ``` `Result`と`Option`を理解する ```rust enum Result<T, E> { Ok<T>, Err<E>, } enum Option<T> { Some<T>, None } ``` ```rust fn exit(x: Option<i32>) { match x { Some(0) => panic!{"we got 0"}, Some(x) => println!("we got a {} things are fine!", x), None => println!("we got nothing!") } } fn main() { exit(Some(1)) exit(Some(10)) exit(None) exit(Some(0)) } ``` ファイルの入出力 ```rust use std::fs::File; fn main() { // returns Result<File> let res = File::open("text.txt"); let f = match f { Ok(file) => file, Err(e) => { panic!("{}", e); } }; } ``` こんな風にパターンマッチも可能 ```rust use std::fs::File; fn main() { // returns Result<File> let res = File::open("text.txt"); let f = match f { Ok(file) => file, Err(ref error) if error.kind() == ErrorKind::NotFound => { match File::create("text.txt") { Ok(fc) => fc, Err(e) => panic!("culd not create file: {:?}", e), } }, Err(error) => panic!("culd not open file: {:?}", error) }; } ``` ## 関連リンク 言語に関するもの - [公式サイト](https://www.rust-lang.org/ja/learn) - [日本語ドキュメント(まとめ)](https://doc.rust-jp.rs/) - [最新版日本語ドキュメント](https://doc.rust-jp.rs/book/second-edition/) - [Rust Playground](https://play.rust-lang.org/) - [github](https://github.com/rust-lang/rust) 記事 - [プログラミング言語Rustのススメ](https://qiita.com/elipmoc101/items/3c8b6d8332a9019e578c) - [Rust なのか Go なのか](https://python.ms/rust-or-go/) - [JetBrainsの集計](https://www.jetbrains.com/ja-jp/lp/devecosystem-2019/rust/) - [Rustを業務で1年使ってみてわかったこと](https://qiita.com/AtsukiTak/items/0ecba19f3518c23d2bf3) ニュース - [最も言語愛されている言語2019](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages) - [プログラミング言語「Rust」の普及に立ちはだかる壁](https://japan.zdnet.com/article/35153014/) - [DropboxがコアサービスをRustで書き換えた背景とは](https://thinkit.co.jp/article/17513) - [WebAssemblyとRustが作るサーバーレスの未来](https://thinkit.co.jp/article/17486) - [実装言語を「Go」から「Rust」に変更、ゲーマー向けチャットアプリ「Discord」の課題とは](https://www.atmarkit.co.jp/ait/articles/2002/10/news038.html) - [プログラミング言語「Rust」がプログラマーに愛される理由とは?](https://news.mynavi.jp/article/rust-1/) その他 - [awesome-rust](https://github.com/rust-unofficial/awesome-rust)