$30 off During Our Annual Pro Sale. View Details »

Rustハンズオン @ Rust CA 1 Day Youth Boot Camp

Yuki Toyoda
December 18, 2021
3.5k

Rustハンズオン @ Rust CA 1 Day Youth Boot Camp

Yuki Toyoda

December 18, 2021
Tweet

Transcript

  1. Rust ハンズオン 2021/12/10 株式会社サイバーエージェント

  2. 目次 ■ 自己紹介 ■ Rustとはどのような言語か? ■ 基本的な文法の解説 o Hello, world

    の実装 o FizzBuzz の実装 ■ 実践編 o cat コマンドの実装 o grep コマンドの実装
  3. 目次 ■ 自己紹介 ■ Rustとはどのような言語か? ■ 基本的な文法の解説 o Hello, world

    の実装 o FizzBuzz の実装 ■ 実践編 o cat コマンドの実装 o grep コマンドの実装
  4. 自己紹介

  5. 自己紹介 ■ 豊田優貴 (ユキさんとよく呼ばれる) ■ 株式会社サイバーエージェント o ソフトウェアエンジニア@AI 事業本部 Dynalyst

    o Rust 領域における Next Expert ■ 共著『実践Rustプログラミング入門』 ■ コミュニティオーガナイザー o Rust.Tokyo o RustFest Global ■ その他、OSS コントリビューションなど
  6. 普段の仕事で何を作っているか ■ 入社後、ずっと広告配信プロダクトの担当をしている。 o 〜2020年: 位置情報広告配信 AirTrack テックリード o 2021年〜:

    リターゲティング広告配信 Dynalyst ■ 速度と安定性が求められる Real-Time Bidding (RTB) の世界。 o ほとんどのレスポンスを安定して100ms以内に返す必要がある。 o リクエスト量は平均して秒間数十万リクエストに達する。 ■ ソフトウェアエンジニアとして、ほぼすべての領域をこなす。 o フロント、バックエンド、インフラまですべてのタスクをこなす。 o ちなみに、チームメンバーもだいたいすべての領域ができる。 o 強いて言うなら Scala エンジニア。
  7. Next Experts について 特定領域への貢献意欲や一定の実績を有し、将来的な Developer Experts を目指すエンジニアのための活動支援制度。サイバーエージェントには、 Developer Experts と呼ばれる、特定領域における社内・社外の活動支援

    制度が存在します。
  8. 『実践Rustプログラミング入門』 Rustは、C/C++の代わりとなる最新の爆速言語とし て注目されています。「とにかく実行速度が速い」「モ ダンな言語機能が一通り入っている」「OSからWebア プリケーションまで幅広く実装できる」「ツール群がと ても充実している」「安全性が強力に担保されている」 など、数多くの魅力があります。本書は、Javaや Pythonなど他の言語に習熟しているエンジニアを対 象に、Rustの独特な仕様と開発ノウハウをわかりやす く解説した入門書です。

  9. Rust.Tokyo, RustFest Global オーガナイザー 日本初のRustカンファレンス「Rust.Tokyo」と、全世界向けの「RustFest Global」のオーガナイザーも勤めています。今年の Rust.Tokyo は 9/18 に無事に終了しました。ありがとうございました。

    RustFest Global のミーティングの様子 Rust.Tokyo 2021 サイト
  10. 目次 ■ 自己紹介 ■ Rustとはどのような言語か? ■ 基本的な文法の解説 o Hello, world

    の実装 o FizzBuzz の実装 ■ 実践編 o cat コマンドの実装 o grep コマンドの実装
  11. Rust とはどのような言語か

  12. 現時点で抱いているイメージを教えて下さい! 🙋

  13. Rust とは It wasn’t always so clear, but the Rust

    programming language is fundamentally about empowerment: no matter what kind of code you are writing now, Rust empowers you to reach farther, to program with confidence in a wider variety of domains than you did before. すぐにはわかりにくいかもしれませんが、Rustプログラミング言語は、エンパワーメント (empowerment)を根本原理としています: どんな種類のコードを現在書いているにせよ、Rustは幅広 い領域で以前よりも遠くへ到達し、 自信を持ってプログラムを組む力を与え(empower)ます。 -- The Rust Programming Language
  14. Rust とは A language empowering everyone to build reliable and

    efficient software Rust は信頼がおけて効率のよいソフトウェアを作れるように開発者をエンパワメントする言語です。 -- Rust 公式
  15. Rust とは A language empowering everyone but especially folks who

    didn’t think systems programming was for them Rust はシステムプログラミングは自分の領域だとは思っていなかった人々をとくにエンパワメントする言 語です。 -- Ashley Williams (Rust Core Team member, Rust Foundation Lead)
  16. Rust とは ■ Rust の中心概念は「empowerment」です。 o em: en-。提供するとか、文脈によって変わる英語の接頭辞。power: 力。 o

    つまり、「力を与えること」。 o Rust という強力な武器で、信頼性と安全性の高いソフトウェアの構築をできる、という 力を開発者に与えるプログラミング言語。 o これまでシステムプログラミングには縁がなかったプログラマであっても、安全で信頼に 値する方法でシステムプログラミングへの道を開いてくれる言語である。 o 低レイヤーのプログラマだけでなく、十分な表現力をもって、あらゆるレイヤーのプログ ラマに力を与える言語でもある。 ■ Rust という新しい武器を手に入れ、力を手にしましょう。 o というのが、本日の主題でもあります。
  17. Rust とはどのような言語か ■ Rustのメリットを知る ■ Rustのデメリットを知る ■ 期待される利用領域を知る

  18. Rust とはどのような言語か ■ Rustのメリットを知る ■ Rustのデメリットを知る ■ 期待される利用領域を知る

  19. その前に

  20. Rustのよさを正しく理解するには知識を要する ■ 他のプログラミング言語の状況をよく知っている必要がある。 ■ これまで低レイヤーな領域で何が起きていたかを知る必要がある。 こうした知識を一旦インプットしてから前に進みましょう

  21. 従来のプログラミング言語に存在していたトレードオフ ■ 安全性と細やかな制御は両立しなかった。 o メモリを細かく制御できるC/C++は、安全性を担保するのに苦労する。 o 安全性の高いGCは、メモリを細かく制御するのに苦労する。 Rust 安全性 細やかな制御

    Python C 詳解Rustプログラミングのp.14の図を引用 多くの言語は この領域で動作する。 Rustは両方を提供する。
  22. これまで低レイヤーな領域で起きていたこと ■ メモリ安全性を担保できない実装がある。 o null ポインタのデリファレンス o dangling ポインタ(use-after-free, double

    free) o 未初期化状態のメモリを使用する o バッファオーバーフローなど。 ■ メモリ起因の脆弱性は実プロダクトでも問題になる。 o Google Chrome の脆弱性の約70%はメモリ起因だった。 o など。
  23. Rust のメリット ■ 性能のよさ o メモリ使用量が小さい。 o とても速い。 ■ 実行時ではなくコンパイルタイムに下記の安全性を担保できる。

    o メモリ管理を安全に行える ▸ 脆弱性の節で解説した話が起こりにくい。 ▸ 普段書く際は、メモリのことをあまり意識しなくてよい仕組みが裏にある。 o 並行処理を安全に行える ▸ データ競合が起こらない。 ▸ スレッドセーフなデータしか共有できない。
  24. Rust のメリット ■ プログラマの生産性を高めるツール群が充実している o 具体的には cargo というビルドツールひとつですべてが済む。 o このおかげで、サードパーティライブラリの利用が容易。

    o クロスプラットフォームビルドも手軽に行える。 o 標準リンタ (clippy) やフォーマッタ (rustfmt) が揃っている。 o LSP (rust-analyzer) がかなりよくできている。 ■ オープンマインドでプロフェッショナルなコミュニティ o 最近少し揉めていたが、揉めごともきちんと議論して前に進めようとする。 o ユーザーからの提案がとても歓迎される。コンパイラの開発は誰でも参加できる。
  25. Rust とはどのような言語か ■ Rustのメリットを知る ■ Rustのデメリットを知る ■ 期待される利用領域を知る

  26. Rust のデメリット ■ 循環的なデータ構造のモデリングが難しい o たとえば、循環構造をもつリンクリストを初心者のうちに作るのが難しい。 o データ構造のモデリングに Rust 特有のワークアラウンドを適用する必要がある。

    ■ コンパイルを通すのが時として難しい o コンパイルを通すまでが他の言語に比べると大変。 o ただ、エラーメッセージは親切。直し方を教えてくれる。 ■ 言語仕様が大きく、まだ拡大中で覚えることが多い o 機能が多く、いまでさえ発見がある。 o コンテキストを理解して適切に使いこなすのが難しいものがある。
  27. Rust とはどのような言語か ■ Rustのメリットを知る ■ Rustのデメリットを知る ■ 期待される利用領域を知る

  28. 期待される利用領域 ■ CLI ツール o クロスプラットフォームビルドの対応が楽にできるため。 o ripgrep, ytop など

    Linux の標準コマンドの置き換えツールが盛ん。 ■ 組み込みファームウェア開発 o 従来は C/C++ が大きなシェアを持っていた領域。 ■ システムプログラミング o OS やブラウザの実装などに利用されることもある。
  29. 期待される利用領域 ■ Web フロントエンド o JS/TSのツールの裏側がRustで実装されている事例が増えている。 o Web Assembly との相性がよい。

    ■ Web サーバーサイド開発 o 実は Rust の利用事例で一番多かったことが2019年のアンケートで判明。 o さまざまなサーバーサイド向けライブラリが登場している。 o 豊田も仕事で使用したことがあります。 ▸ その時の発表: https://speakerdeck.com/helloyuk13/rust-de-web- apurikesiyonhadokomadekai-fa-dekirufalseka
  30. 目次 ■ 自己紹介 ■ Rustとはどのような言語か? ■ 基本的な文法の解説 o Hello, world

    の実装 o FizzBuzz の実装 ■ 実践編 o cat コマンドの実装 o grep コマンドの実装
  31. 基本的な文法の解説 環境構築と Hello, world & Fizzbuzz

  32. 基本的な文法の解説 ■ 環境をセットアップする。 o Rust をインストールしよう! o VSCode と rust-analyzer

    を入れよう ■ 軽く動かしてみる。 o Hello, world してみよう ■ 変数宣言、制御構文、関数宣言 o let, let mut o if を使って FizzBuzz する o 関数を切り出して処理を共通化する o など。
  33. 基本的な文法の解説 ■ 環境をセットアップする。 o Rust をインストールしよう! o VSCode と rust-analyzer

    を入れよう ■ 軽く動かしてみる。 o Hello, world してみよう ■ 変数宣言、制御構文、関数宣言 o let, let mut o if を使って FizzBuzz する o 関数を切り出して処理を共通化する o など。
  34. Rust をインストールしよう! ■ Rust をインストールしましょう!下記ページの curl コマンドを打ちます。 https://www.rust-lang.org/ja/tools/install

  35. エディタについて ■ 今日は VSCode を使用します。 o JetBrains 愛用者は、IntelliJ か CLion

    に Rust プラグインを入れる。 o VSCode ならトラブルシュートに協力できるかも…笑。 ■ VSCode に rust-analyzer というプラグインを入れてください。 o https://marketplace.visualstudio.com/items?itemName=matklad.rust -analyzer o Vim や Emacs などでも動作する。
  36. 今日作り上げるコードについて ■ 下記のリポジトリにあります。コピペなどご活用ください。 o https://github.com/yuk1ty/rust-basic- handson/tree/20211210_youth_camp

  37. 基本的な文法の解説 ■ 環境をセットアップする。 o Rust をインストールしよう! o VSCode と rust-analyzer

    を入れよう ■ 軽く動かしてみる。 o Hello, world してみよう ■ 変数宣言、制御構文、関数宣言 o let, let mut o if を使って FizzBuzz する o 関数を切り出して処理を共通化する o など。
  38. プロジェクトを作ろう ■ 今回作業するためのプロジェクトを作成します。 o cargo new コマンドを叩いてプロジェクトを作成します。 ▸ 一通り実行可能な状態で新規プロジェクトを作ることができます。 ■

    Hello, world と表示させてみましょう。 o cargo run コマンドを叩いて実行できます。
  39. Hello, world のコードを見てみよう ■ 生成されたディレクトリ内の main.rs に書かれているコード。

  40. Hello, world のコードを見てみよう ■ 後ほど解説するが、fn キーワードで関数を宣言できます。 o main という名前の関数は、実行時のエントリポイントです。 ■

    println! で標準出力ができます。 o ! はマクロであることを意味します。 ▸ マクロとは、Rust のプログラムをプログラミングする仕組みのことです。 ▸ コンパイルタイムでマクロ内のコードが展開・生成されます。 ▸ 他の言語ではメタプログラミングとも呼ばれます。 ■ Rust では、文・式・ブロックの末尾にセミコロンが基本的に必要です。 o ただし、セミコロンがない場合は特別視されます。 ▸ これは後ほど解説します。
  41. マクロの謎を解明する (質問を受けることが多いので) ■ マクロがコンパイルタイムで展開するコードを見ることができます。 o cargo expand というツールをインストール。 o デモ。

    ■ リフレクションとは違い、コンパイル時にコードが生成されます。 o 使うと Java 等のリフレクションと似ているように見えますが、少し違います。 ▸ リフレクションと比べると、もう少しできることが広いです。 ▸ Java 等のリフレクションは実行時に処理が走りますが、マクロはコンパイルタイムです。 ▸ Rust はできる限りコンパイルタイムに多くの物事を解決し、処理のオーバーヘッドを減らすこ とを目指しています。 ■ (少し専門的な話ですが) Rust のマクロは衛生的です。
  42. 基本的な文法の解説 ■ 環境をセットアップする。 o Rust をインストールしよう! o VSCode と rust-analyzer

    を入れよう ■ 軽く動かしてみる。 o Hello, world してみよう ■ 変数宣言、制御構文、関数宣言 o let, let mut o if を使って FizzBuzz する o 関数を切り出して処理を共通化する o など。
  43. 変数宣言をしてみよう ■ 先ほどの main.rs を、下記のように書き換えてみます。

  44. 変数宣言をしてみよう ■ let で変数宣言をできます。束縛、と Rust ではいいます。 ■ Rust では変数宣言はデフォルトでイミュータブルです。 o

    イミュータブル (不変) : 再代入や破壊的変更をできないこと。 ■ 変数名の後ろに「: 型名」と書くと、型注釈をつけられます。 o Rust では型推論が強力なため、実際はほとんどつけることはありません。 ■ println! 内の {} は、変数を文字列内に代入するプレースホルダ。 o 余談: {} 以外にもいくつか種類があり、用途ごとに使い分けします。 ▸ https://doc.rust-lang.org/rust-by-example/hello/print.html
  45. 「Rust の変数宣言はイミュータブル」を体感しよう ■ 下記は多くのプログラミング言語ではコンパイルが通るコードです。 ■ しかし、下記のコードはコンパイルが通りません。イミュータブルだからです。

  46. Rust のコンパイルエラーはとても親切 ■ コンパイルすると、どこを直したらよいかを教えてくれます。 o なので、普段は指示通りに直しながらコーディングをします。

  47. let mut をつけて、再代入できるようにする ■ コンパイラの指示通りに let mut に変更し、再代入を許可します。

  48. 余談: なぜイミュータブルか ■ ミュータブルな実装は、変数のスコープが伸びたり、予期せぬ破壊的変更が加 えられたりし、メンテナンス性が高いとは言えないケースがあります。 o 金融系のシステムでは100万行を越えるような大規模開発をすることになりますが、そ こでスコープを絞ってさえいれば…というバグをいくつも見てきました。 o とくに並行・並列処理をする際には、予期せぬ破壊的変更が入らない保証を言語側でし

    てくれると大変ありがたいのです…。 ■ 一方で、すべてのケースでイミュータブルがよいわけではなく、スコープをし ぼってミュータブルな実装を採用するとよい場面もあります。 o たとえば、メモリ節約をしたいケースなどでは、ミュータブルな実装のほうが空間効率が よいケースがあります。
  49. よく見るプリミティブ型 (これ以外にもあります。詳しくはドキュメントをご覧ください。) i8, i16, i32, i64 符号付き整数型 u8, u16, u32,

    u64 符号なし整数型 f32, f64 浮動小数点数型 isize / usize CPUアーキテクチャごとのビットサイズ。 bool ブール値。 &str, String 文字列を扱う際によく利用される。後述。
  50. 文字列型 ■ String と &str の使い分けは、最初の鬼門になります。 o どっちがどっちかわかりにくいので、よく聞かれます。 o そのため、今日はとくに文字列型を絞って解説します。

    ■ なぜ多いかというと、Rust はシステムプログラミング言語だから。 o 他の言語と比較すると、よりさまざまなケースに対応する必要があるから。 ▸ UTF-8 の文字列を処理するための String, &str ▸ OS からの文字列の受け取りをする OsString (など) ▸ C コードからの文字列の受け取りをする CString (など) 他にもこれだけの文字列型がある…
  51. 文字列型 ■ String と &str の使い分けは、最初の鬼門になります。 o どっちがどっちかわかりにくいので、よく聞かれます。 o そのため、今日はとくに文字列型を絞って解説します。

    ■ なぜ多いかというと、Rust はシステムプログラミング言語だから。 o 他の言語と比較すると、よりさまざまなケースに対応する必要があるから。 ▸ UTF-8 の文字列を処理するための String, &str ▸ OS からの文字列の受け取りをする OsString (など) ▸ C コードからの文字列の受け取りをする CString (など)
  52. 文字列型 – 基本 ■ String o UTF-8 のテキストを保持する。 o ヒープメモリにアロケートされる。

    o リサイズできる。要するにミュータブル。 ■ &str o いわゆるスライス。 o 機械語生成時にすでに確保済みのメモリ領域にアロケートされる。 o 読み取り専用なので、リサイズできない。要するにイミュータブル。
  53. 文字列型 – 選び方 ■ UTF-8 文字列か? ■ ミュータブルな文字列か? o Yes:

    String o No: &str UTF-8 文字列? ミュータブルに したい? &str を使う String を使う はい いいえ
  54. 文字列型 – 選び方 ■ UTF-8 文字列か? ■ ミュータブルな文字列か? o Yes:

    String o No: &str UTF-8 文字列? ミュータブルに したい? &str を使う String を使う はい いいえ
  55. 文字列型 – 選び方 ■ UTF-8 文字列か? ■ ミュータブルな文字列か? o Yes:

    String o No: &str UTF-8 文字列? ミュータブルに したい? &str を使う String を使う はい いいえ
  56. 文字列型 – 選び方 ■ UTF-8 文字列か? ■ ミュータブルな文字列か? o Yes:

    String o No: &str UTF-8 文字列? ミュータブルに したい? &str を使う String を使う はい いいえ
  57. String ■ 下記のコードは下記の図のようなメモリ確保をされます。 Stack buffer capatcity length 10 8 u

    t r i l t o a Heap length capacity
  58. &str ■ 下記のコードは下記の図のようなメモリ確保をされます。 Stack buffer length 8 u t r

    i l t o a preallocated read-only memory length ※ preallocated read-only memory: 機械語生成時にすでに確保済みのメモリ領域を指す。
  59. 余談: スタックメモリとヒープメモリ ■ スタックメモリ o ローカル変数や関数からの処理の戻り先、引数と言った一時的なデータを保持。 o サイズを指定してメモリを確保する必要がある。 o スタックのようにメモリを管理するのでシンプル。

    o 速い。 ■ ヒープメモリ o プロセスが動的に確保する領域で、柔軟性が高い。 o C 言語で言う malloc で確保される領域にあたる。 o 明示的に解放されるまで (free) は使い続けられる。 o スタックメモリよりは遅い。
  60. 余談: Rust では ■ Rust の場合は、指示しない限りはスタックメモリで処理される。 o 本日最後の方に、実際にスタックに積まれる様子を図示します。 ■ ただ一部、ヒープを利用するものがある。

    o Vec のように動的に領域を確保する必要があるものや、 o Box のようにそもそもヒープに割当することを明示的にやるものがある。
  61. String ↔ &str ■ ダブルクオーテーションで囲うと &str になります。 ■ .to_string() すると、String

    になります。
  62. 最初のうちは ■ &は参照を意味しますが、Rust は参照を持ち回すと大変です。 o 今日は深くは解説しませんが、ライフタイムと呼ばれる話が絡んできてしまいます。 o ライフタイムは解決がたまに難しく、大変になりがちです。 ■ 初心者のうちは、まずは

    String を取り回すことを覚えましょう。 o 慣れてきたら、「この文字列は宣言後変更をすることはないな」と思う箇所を &str に。
  63. 基本的な文法の解説 ■ 環境をセットアップする。 o Rust をインストールしよう! o VSCode と rust-analyzer

    を入れよう ■ 軽く動かしてみる。 o Hello, world してみよう ■ 変数宣言、制御構文、関数宣言 o let, let mut o if を使って FizzBuzz する o 関数を切り出して処理を共通化する o など。
  64. FizzBuzz を通じて制御構文や関数を学ぶ ■ FizzBuzz: よくプログラマがやる遊びのひとつです。 o 3で割り切れる数のとき→ Fizz と出力 o

    5で割り切れる数のとき→ Buzz と出力 o 3で割り切れるかつ、5で割り切れる数のとき→ FizzBuzz と出力 ■ この節では、if, for, 関数への切り出し方などを学びます。 ■ 関数型プログラミング的なアプローチも身につけます。
  65. if 式を使って FizzBuzz を作ってみる ■ 下記のようなコードを書きましょう。

  66. if 式を使って FizzBuzz を作ってみる ■ if は式なので、結果を変数に束縛できます。 ■ セミコロンを文末につけると式になり、ない場合は文として解釈が行われま す。

    ■ if 式がある関係で、Rust には三項演算子はありません。
  67. for 式を使って0〜99の数字をイテレートする ■ 下記のようなコードを書きましょう。

  68. for 式を使って0〜99の数字をイテレートする ■ 0..100と書くと、0以上100未満の範囲を示すオブジェクト(Range)を生 成できます。 o num に0〜99の値が1つずつ流れ込みます。 o 裏側は

    Range という構造体です。ただ、これは糖衣構文です。 ▸ https://doc.rust-lang.org/std/ops/struct.Range.html ■ for はイテレータを回す糖衣構文になっています。 o Range はイテレータの性質をもちます。 o 応用: Iterator トレイトを実装しています。 ▸ https://doc.rust-lang.org/std/iter/trait.Iterator.html
  69. 関数に切り出す ■ 先ほど書いた処理を fizzbuzz という関数に切り出しましょう。

  70. 関数に切り出す ■ fn と宣言すると関数を宣言できます。 o 関数名は snake_case です。 o 引数は「仮引数名:

    型名」で定義できます。 o 返り値(戻り値)は -> という記号のあとに書きます。 ■ セミコロンを書かない末尾の文が値を返すことを示し、return は不要です。 o セミコロンセンシティブな文法なので、少し好みは分かれるかも。 o 不要ではありますが、return を書くことはできます。ほとんど書かないけど。 o アーリーリターンしたい場合には return は必須になります。
  71. 関数型プログラミング的なアプローチ ■ これまでは for 式を用いた手続き型的なプログラミングをしました。 ■ Rust は関数型プログラミングの手法をいくつか取り入れています。 o map,

    filter, fold などです。Rustでは、これらをアダプタと呼びます。 ■ それらを使用することで、再利用性の高いコードを書くことができます。
  72. 関数型プログラミング的なアプローチ ■ 下記のようにコードを書き直してみましょう。

  73. 関数型プログラミング的なアプローチ ■ 0〜99のイテレータを作成し、その結果を fizzbuzz 関数に通します。 ■ map で fizzbuzz 関数を実行し、i32

    -> String のマッピングをします。 ■ fold で結果生成される文字列を結合していき、まとめます。 o |...| で囲まれた部分はクロージャーと呼びます。 ▸ 他の言語ではラムダ式とも呼ぶかもしれません。
  74. 関数型プログラミング的なアプローチ ■ 0〜99のイテレータを作成し、その結果を fizzbuzz 関数に通します。 ■ map で fizzbuzz 関数を実行し、i32

    -> String のマッピングをします。 ■ fold で結果生成される文字列を結合していき、まとめます。 o |...| で囲まれた部分はクロージャーと呼びます。 ▸ 他の言語ではラムダ式とも呼ぶかもしれません。
  75. 関数型プログラミング的なアプローチ ■ 0〜99のイテレータを作成し、その結果を fizzbuzz 関数に通します。 ■ map で fizzbuzz 関数を実行し、i32

    -> String のマッピングをします。 ■ fold で結果生成される文字列を結合していき、まとめます。 o |...| で囲まれた部分はクロージャーと呼びます。 ▸ 他の言語ではラムダ式とも呼ぶかもしれません。
  76. 関数型プログラミング的なアプローチ ■ 0〜99のイテレータを作成し、その結果を "zzbuzz 関数に通します。 ■ map で "zzbuzz 関数を実行し、i32

    -> String のマッピングをします。 ■ fold で結果生成される文字列を結合していき、まとめます。 o |...| で囲まれた部分はクロージャーと呼びます。 ▸ 他の言語ではラムダ式とも呼ぶかもしれません。
  77. 関数型プログラミング的なアプローチ ■ 手続き型と関数型、どちらのスタイルで書いてもよいです。 ■ ゼロコスト抽象化の恩恵により、速度にほぼ差はありません。 o もちろん実用上はケースバイケースです。理論上は、という話です。 o 関数型的な書き方のほうが、最適化が走りやすく若干速くなると聞いたことはあります。

  78. 実践編 1. cat コマンドの実装

  79. cat コマンド実装の目次 ■ プロジェクトを作成する。 ■ main.rs 自身を表示するプログラムを書く。 o パターンマッチングの解説 o

    Result 型を用いたエラーハンドリングの解説。 ■ ファイルパスを渡し、そのファイルを表示するプログラムを書く。 o Option 型を用いた実装の解説。 o if let 構文の解説。
  80. cat ■ 本来はファイル同士を連結する(concatenate)ための Linux コマンド。 ■ ファイルの中身をちらっと見る際によく使用されます。

  81. 最終目標 ■ cat コマンドのように、ファイル名を引数で渡すと中身を表示できます。

  82. プロジェクトを新規作成する ■ cat と、このあと作る grep で使用できるプロジェクトを新規作成します。

  83. main.rs 自身を表示するプログラムを書く ■ ファイルを読み込んで文字列として表示するコードを書きます。

  84. main.rs 自身を表示するプログラムを書く ■ read_to_string 関数 o パスの内容を読み込んで、String 型にして返します。 ■ use

    o 他の言語で言う import と同じで、モジュールを読み込みます。 ■ match o パターンマッチという文法です。 o Ok や Err は Result 型という enum のバリアントです。
  85. パターンマッチ ■ 他の言語でいう switch 文をもう少し強力にしたものです。 ■ パターンマッチは式なので、値を返すことができます。 ■ Rust では多くの型がパターンマッチに対応しています。

    o 構造体 (このあと説明します) o enum o 文字列型 (String など) o 数値 (i32 など) o ベクタ (Vec<T>) o その他、さまざまな型をパターンマッチで処理できます。
  86. パターンマッチの例: 数値型 ■ 数値型はパターンマッチの対象になります。 o 1, 2, 3 に該当した場合は、対応した文字列を出力します。 o

    `_` は「それ以外のパターン」という意味になります。
  87. パターンマッチの例: 文字列型 ■ String 型もパターンマッチの対象になります。

  88. Rust のエラーハンドリング ■ Result 型を使い、パターンマッチをしてエラーハンドリングします。

  89. Rust のエラーハンドリング ■ Result 型は Ok か Err をバリアントにもつ enum

    です。 o enum は Rust で非常によく出るパターンです。 ▸ 少し専門的な話: Ok や Err は代数的データ型 (ADT) と呼ばれます。 ▸ 数学的注釈: enum は直和集合です。 o enum はパターンマッチングできます。 ▸ Ok(content) で、content という変数に内容を持ちます。 ▸ Err(reason) の場合は、reason という変数にエラー内容が入ります。
  90. ? キーワード ■ ? キーワードを用いると、エラーを伝播させることができます。 o これにより「このレイヤーではまだエラーをハンドリングせず、ただ上のレイヤーに伝播さ せたい」というユースケースに対応できます。

  91. 指定したファイルの内容を出力できるようにする ■ ファイルパスを指定して中身を出力できるように変更します。

  92. 今回記述が増えた内容について ■ std::env::args を使うと、実行時引数の取り出しができます。 o nth 関数で実行時引数を取り出します。 ■ Some, None

    は Option 型のバリアントです。 o 値がない可能性があることを示します。他の言語では null だったり nil だったり。 o Option 型も enum なので、パターンマッチできます。
  93. 今回記述が増えた内容について ■ std::env::args を使うと、実行時引数の取り出しができます。 o nth 関数で実行時引数を取り出します。 ■ Some, None

    は Option 型のバリアントです。 o 値がない可能性があることを示します。他の言語では null だったり nil だったり。 o Option 型も enum なので、パターンマッチできます。
  94. 今回記述が増えた内容について ■ std::env::args を使うと、実行時引数の取り出しができます。 o nth 関数で実行時引数を取り出します。 ■ Some, None

    は Option 型のバリアントです。 o 値がない可能性があることを示します。他の言語では null だったり nil だったり。 o Option 型も enum なので、パターンマッチできます。
  95. 今回記述が増えた内容について ■ std::env::args を使うと、実行時引数の取り出しができます。 o nth 関数で実行時引数を取り出します。 ■ Some, None

    は Option 型のバリアントです。 o 値がない可能性があることを示します。他の言語では null だったり nil だったり。 o Option 型も enum なので、パターンマッチできます。
  96. if let ■ 「値があるケースだけ特定の処理をしたい」場面に多々出会います。 ■ その場合は None 側は不要になりますが、パターンマッチでは冗長です。 ■ if

    let という構文を使用することができます。 o 実は変数束縛のタイミングでパターンマッチングが走っています。
  97. 最後に実行してみましょう ■ `cargo run -- ファイル名` で実行できます。 o `--` は、この記号の後ろは実行時引数になるという意味です。

  98. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/cat_2/src/main.rs

  99. 実践編 2. grep コマンドの実装

  100. grep ■ cat コマンドを拡張すると grep コマンドを作れます。 ■ 構造体や Rust の第一関門である所有権という概念が登場します。

    ■ また、サードパーティークレートの使い方を学びます。
  101. grep コマンド実装の目次 ■ 指定した文字列がある行を検索&出力できるプログラムを書く。 ■ grep に使用する引数を構造体にまとめる。 o 構造体の使い方の解説。 o

    構造体に対して実装を生やす方法の解説。 o 複数ファイル扱えるようにする。 ■ structopt を使った実行時引数のパース。 o サードパーティークレートの使い方の解説。 o 所有権と借用の解説。 ■ grep の並列処理化。 o rayon を使った並列処理の実装。
  102. 最終目標 ■ grep コマンドのように、所定のファイル内にある文字列を検索できます。

  103. 指定した文字列がある行を検索し、出力する ■ 該当する文字列があった場合、その行を標準出力します。

  104. 指定した文字列がある行を検索し、出力する ■ grep 関数を作り、指定した文字列パターンに一致するかチェックします。 o as_str 関数で &str 型に変換します。これは contains

    が &str を求めるためです。 ■ .lines() 関数により、一行一行をイテレータとして回すことができます。 o なので、 for 式で一行ずつループさせられています。
  105. main 関数を修正する ■ さらに main 関数も一部修正します。 o 第一引数で grep で検索したい文字列パターンを、

    o 第二引数でファイルパスを指定します。
  106. main 関数を修正する ■ (pattern, path) はタプルと呼ばれる文法です。 o いわゆる組で、配列とは違い異なる型の組をもたせられます。 ▸ 数に制限はありません。

    ▸ 応用: Rust の Unit 型は、要素0個のタプルです。 ▸ 数学的注釈: 直積集合と密接な関係にあります。 o タプルもパターンマッチ可能です。
  107. ここまでの実装のまとめ ■ cat 用の実装に検索用実装を追加しました。 ■ 実行時引数で検索したい文字列を受取可能にしました。

  108. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_1/src/main.rs

  109. 動かしてみよう ■ `cargo run -- version Cargo.toml` o スペース区切りで1つの引数としてみなされます。 o

    「version」が探す文字列、「Cargo.toml」が対象ファイルです。
  110. grep に使用する引数を構造体にまとめる ■ 実行時引数として使用するパラメータをひとまとまりにしましょう。 o 関連するデータは、さらに上位の概念でグルーピングしたいですよね。 ■ 構造体 (struct) という機能を使うことができます。

    o C 言語等に登場する構造体とほぼ同じです。 o データのまとまりを表現したい際に使用します。
  111. 構造体を定義する ■ grep に使用する引数をまとめる GrepArgs を作成します。

  112. 構造体を定義する ■ struct キーワードを使って構造体を定義できます。 ■ pattern, path はフィールドです。 o フィールドは

    snake_case です。 o 「フィールド名: 型名」の順に定義します。
  113. 構造体に実装を追加する ■ 構造体に対する実装を追加し、コンストラクタを定義します。

  114. 構造体に実装を追加する ■ impl キーワードを使い、構造体に対する実装を定義できます。 o 「impl 構造体名」の順で定義できます。 ■ 今回定義する new

    はスタティックメソッドです。 o 後ほど main 関数から呼び出しします。 o これはいわゆる静的メソッドです。インスタンスには紐付かず、型に紐づきます。 o インスタンスに紐づくインスタンスメソッドについては、後ほど解説します。
  115. 構造体に実装を追加する ■ これはサンプルですが、インスタンスメソッドは次のように定義できます。

  116. 構造体に実装を追加する ■ レシーバの self を関数の第一引数につけると、インスタンスメソッドになり ます。 o Python などと同じ形式だと思います。 ■

    レシーバを経由して、構造体自身のフィールドにアクセスできます。
  117. 作ったスタティックメソッドを呼び出してみる ■ 「構造体名::メソッド名」で呼び出しが可能。 o インスタンスメソッドの場合は「変数名.メソッド名」です。

  118. run 関数を修正する ■ 作った構造体を引数で受け取りできるようにします。 ■ 構造体から必要な値を取り出せるように修正します。

  119. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_2/src/main.rs

  120. CLI ツールっぽくする ■ CLI (Command Line Interface) ツールっぽくしましょう。 o 普段使っている

    Linux コマンドのような入力の仕方をするツールです。 ■ structopt というクレートを使います。 o Rust では「ライブラリ」のことを「クレート (crate; 木箱)」と呼びます。 o cargo は積荷という意味ですが、その積荷のうちの一つに木箱がある…みたいな。
  121. クレートを追加しましょう ■ Cargo.toml の [dependencies] に structopt の依存を追加します。 o 今回はバージョン

    0.3.21 を使用します。追加後、cargo build をしましょう。
  122. structopt の設定を行う ■ #[derive(StructOpt)]、#[structopt(…)]を追加しましょう。

  123. structopt の設定を行う ■ #[derive(…)]や#[structopt(…)]は「アトリビュート」と呼ばれます。 o 他の言語だと「アノテーション」という名前かもしれません。 o derive(StructOpt)で、StructOpt トレイトを継承する、という意味です。 ▸

    トレイトはこのハンズオンでは紹介できませんが、他の言語でいう interface のようなもの です。 ▸ 応用: トレイトは型クラスです。 o structopt(…)は、手続きマクロと呼ばれるマクロです。 ▸ Procedural Macros。かなり応用的な話なので、ハンズオンでは省きます。 ▸ https://doc.rust-lang.org/reference/procedural-macros.html
  124. structopt の設定を行う ■ これらを追加することで、裏で実行時引数をパースする実装が生えます。 o マクロには実装を生成する機能がありましたね。 o 自分で引数解析を実装しなくても、クレートが自動で解析してくれます。

  125. structopt で実行時引数をパースする ■ from_args 関数で実行時引数をパースします。 o std::env::args を使用していた箇所は、軒並み from_args で置き換えられます。

  126. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_3/src/main.rs

  127. help コマンドが生えてくるのを確認する ■ この時点で help コマンドが生え、CLI ツールっぽくなりました。 o cargo run

    -- --help と入力し、 help の確認をしましょう。
  128. 複数ファイルを指定できるようにする ■ フィールドをベクタにすると、複数ファイルを指定できるようになります。

  129. 複数ファイルを指定できるようにする ■ Vec はサイズが可変の配列です。 o 比較的よく利用します。 o これをフィールドに設定すると、structopt が裏で特別な実装を生やします。

  130. ファイルパス1つ1つを検索できるように修正する ■ run 関数にファイルパスを1つ1つイテレートする実装を追加します。 o iter() を使うと、ベクタ内をイテレートできます。

  131. コンパイルしてみるも… ■ おや、コンパイルエラーが。

  132. 所有権とは ■ Rust には GC がなく、リソースの解放は自動で行われます。 o どのタイミングかというと、関数やブロックの終了の箇所など。 o Borrow

    Checker というものが Rust にはあります。 ▸ これが、参照の二重解放や解放済み領域を使用していないかを自動でチェックします。 ▸ Rust の安全性の秘訣はここにあります。 ■ Rust には値の所有者がかならず1つだけ存在します。 o プログラミング中は、「誰が値をもっているか」を意識する必要があります。 ■ 所有者が移動することをムーブといいます。 o 今日は深くは説明しませんが、コピーが起きるものもあります (i32 などの型)。 ▸ コピーの場合、ムーブは起きません。Copy トレイトを実装したものはコピーが行われます。
  133. 所有権の移動を実際に見てみる

  134. 所有権の移動を実際に見てみる ■ 12行目で確保された user は、16行目の関数にムーブされました。 ■ 19行目で呼び出している user は所有権がありません。

  135. 所有権の移動を実際に見てみる ■ 次の図は概念図ですが、理解の手助けになるはずです。 o 厳密性には少し欠けますが、所有権が移動するとはどういうことかを説明しています。 o メモリ上に確保された領域がどうなるかの遷移を見ることができます。

  136. None
  137. None
  138. None
  139. None
  140. None
  141. None
  142. None
  143. None
  144. None
  145. 所有権は貸し出すことができる ■ 所有権は貸し出しできます。借用と呼びます。 o 関数の仮引数と実引数に渡す変数に「&」をつけることで、その変数の値を貸し出します。

  146. 先ほどのコンパイルエラーをよく見直す ■ 「value moved here」の文字があります。

  147. 何が起きているのか ■ 実は for ループは1回ごとに確保したリソースを解放します。 ■ 解放するということは、実質ムーブが発生します。 o なので、1回目に変数をムーブしてしまうと、2回目のループの時点ではリソースが解放 されてしまっています。

  148. どう解決するのか ■ grep 関数を呼び出したタイミングで、state.pattern の所有権がなく なっているとがわかっています。 ■ 先ほど学んだ所有権の貸し出しを利用しましょう。 o grep

    関数の pattern の型を参照型に変えます。 o grep 関数に渡す実引数 state.pattern の頭に & をつけます。
  149. どう解決するのか ■ コードを修正してみると、コンパイルが通ることを確認できます。

  150. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_4/src/main.rs

  151. ファイルパスも出力できるようにしましょう ■ 同様にファイルパスを渡せるようにしましょう。

  152. ちょっとコードをきれいにする ■ &String は &str に裏で暗黙に型変換されるので… o つまり、&String と書かずに、&str と書くことができる。

    ▸ pattern も path も、ミュータブルである必要はない。 ▸ さらにいうと、content もミュータブルである必要はない。 o ちょっと初心者の範囲を超えているので深くは踏み入らない。 ▸ 参考になるかも: https://qiita.com/nirasan/items/e9c621240a7aae914cb8 o grep 関数についているこれらをすべて修正できる。
  153. 最終的にはこう直せる

  154. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_5/src/main.rs

  155. 実行してみましょう ■ cargo run して実行してみましょう。

  156. 高速化に取り組む ■ かなり大量の行を検索させるとスピードが遅くなります。 ■ 最適化はケースバイケースですが、今回は決め打ちで並列化をします。 ■ Rust では並列化を楽に行えるクレートがあり、それを利用します。

  157. rayon への依存を追加する ■ Cargo.toml に rayon への依存を追加しましょう。 o 今回はバージョン 1.5.1

    を利用します。
  158. rayon への依存を追加する ■ rayon の prelude をインポートし、先ほど書いた iter() 関数を par_iter()

    関数に置き換えるだけで並列化できます。
  159. ここまでのコード ■ https://github.com/yuk1ty/rust-basic- handson/blob/20211210_youth_camp/grep_6/src/main.rs

  160. 今回紹介できなかった機能 ■ 今回時間の都合で紹介できなかった機能がいくつかあります。下記です。 o ライフタイム o トレイト o ジェネリクス o

    スマートポインタ (Box, Rc, RefCell など) o 並行処理にまつわる機能 (Send, Sync) o ユニットテスト o モジュール、など ■ これらをより掘り下げたい方は、拙作ですが下記のブログをどうぞ。 o https://blog-dry.com/entry/2021/01/23/141936
  161. 演習時間

  162. どちらかを選んで取り組んでみましょう ■ Grep に拡張コマンドを追加する。 o 「行番号」を表示する n (line-number) オプションを実装してみる。 o

    grep の man page を眺めて、いくつかオプションを実装してみる。 ■ 自分の好きなものを実装してみる。 o ご自身のお好きなものを実装してみましょう!
  163. 参考文献など ■ このハンズオンの演習問題の多くは下記のスライドに依っています。 o https://chikoski.info/rust-handson/ ■ 『詳解Rustプログラミング』 Tim McNamara /

    吉川邦夫 ■ 『Programming Rust Second Edition』 Jim Blandy 他 ■ The Rust Programming Language o https://doc.rust-lang.org/book/ ■ 『詳解Linuxカーネル 第3版』 Daniel P. Bovet 他 ■ Software Design 2021年9月号 o 連載: 「Rust でわかるメモリ管理」