---
title: Rust勉強会new
tags: Rust, WebAssembly, TDD
description: Rust勉強会
lang: ja
image: https://www.rust-lang.org/static/images/rust-logo-blk.svg
slideOptions:
transition: slide
---
# Rust勉強会(new)
ワイ史上最強の言語! Rustをちゃんと理解してあげる

## 今日のお品書き
**はじめに**
- 注意事項
- 参考リンク
**telumoが思うRustの素晴らしさ**
- オープンソース
- フツーに書ける
- パフォーマンスが高い!
- テストしやすい!!!(これ、重要)
- クロスコンパイル
- モダン〜OptionやResult〜
- フロントも書ける!(WebAssembly)
- マルチパラダイム
- マクロが書ける!
- アトリビュートが便利!
**所有権/借用/ライフタイム**
難しいと言われるRustの概念について少しだけ。
**実装編(Installから〇〇アプリまで)**
**5. おわりに**
まとめます。
---
## はじめに
### 注意事項
- 現時点でのRustのバージョンは1.43.1です。
- 主催者はRust初心者です。
- 資料はあくまでも補助資料のつもりで。
- 言うまでもありませんが、言語は手段です。「この言語さえ勉強すれば大丈夫!」という魔法の様な言語は***存在しません***ので、用法・用量を守って正しくお使いください。
- とはいえRustは素晴らしい言語ですので、「**Rustが好きすぎる病**」が発症する可能性があります。ご注意ください。
- 質問はいっぱいしてください!
- 普段はこんなにしっかり資料を作りません。(今回はちょっと自己研鑽のためにも資料を作ってみました。)
### 参考リンク
| リンク | 内容 |
| -------- | -------- |
| [Rustプログラミング言語](https://www.rust-lang.org/ja) | 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の説明。参照用。 |
| [Rustの日本語ドキュメント集](https://doc.rust-jp.rs/) | 日本語ドキュメント。若干古いものあるので注意。 |
| [Awesome Rust](https://github.com/rust-unofficial/awesome-rust#game-development) | Rustの便利ツールがリスト化されている。 |
---
## Rustの素晴らしさ => オープンソース
RustはMozillaが作った言語。
Mozillaらしく[オープンソース(github)](https://github.com/rust-lang/rust)。
RFCの議論さえもYouTubeに公開している!↓
{%youtube 41pnkSS6MzY %}
GoとかSwiftとか最近人気な言語はあるやん?
でも、結局、GoogleとかAppleの言語なんよ。
全てのソースコードも確認できて、リクエストも出せる。最高。
こういうのも「[最も愛されている](https://codezine.jp/article/detail/12350)」理由かもしれない。
## Rustの素晴らしさ => フツーに書ける
Rustは難しくない。
C/C++, Java, JavaScriptとかを書ける人は普通にRustも書けるよ。
https://github.com/telumo/giflet/blob/master/src/main.rs
## Rustの素晴らしさ => パフォーマンスが高い!
**速い**
Rustは最も速いプログラミング言語の1つ。
CやC++などと同様の速さらしい。
理由の一つにGC(ガベージコレクタ)がないことがある。
:::spoiler メモリーリークを防ぐ戦略
1. 自力
- C, C++など
- メリット
- ちゃんと実装すれば、高速。
- デメリット
- ちゃんと実装しなければ、死ぬ。
2. ガベージコレクション(GC, ガベコレ)
- ほぼ全ての言語が装備(Java, PHP, Python, Ruby, Go, Haskell...)
- 実装方法
- 参照カウント
- マーク・アンド・スイープ
- ...
- メリット
- メモリ管理を意識する必要がない。
- デメリット
- 自力戦略に比べると速度は劣る。
- 場合によってはStop the world?(現代のGCでは改善されているかも。)
3. 所有権
- Rust
- メリット
- CやC++と同じくらい高速
- デメリット
- 学習コスト?(ボローチェッカーで管理してくれるものの)
:::
</br>
**メモリ安全**
あとで話す所有権のおかげでメモリ安全!
(`unsafe`とか使わなければね。)
**ゼロコスト抽象化(静的ディスパッチ)**
```rust=
use std::fmt::Debug;
fn main() {
fn static_dispatch<T: Debug>(x: T) {
println! {"{:?}", x};
}
static_dispatch(2020);
static_dispatch("thursday");
#[derive(Debug)]
struct Point {
x: i32, // xは32ビット符号あり数値として指定
y: String, // yは文字列として指定
}
let points = Point { x: 2020, y: "thursday".to_string() };
static_dispatch(points);
}
```
## Rustの素晴らしさ => テストしやすい!!!
`#[test]`って書くだけ
https://github.com/telumo/giflet/blob/master/src/cli.rs#L49
## Rustの素晴らしさ => クロスコンパイル
linuxやmacOS上でWindows用のバイナル作る場合は、基本的にはこれだけ。
```shell=
cross build --target x86_64-pc-windows-gnu
```
参考
https://github.com/telumo/giflet/releases
:::spoiler りんごで例えると?
日本人の味覚に合うりんごがアメリカで作れるらしいよ。
:::
## Rustの素晴らしさ => モダン〜OptionやResult〜
まず、`null`がない。全て型で理解する。
```python=
def f(x=None):
if x is None: # こう言うの書きたくないよね。
return 0
else:
return x
```
`Result`と`Option`を理解する
```rust=
enum Result<T, E> {
Ok<T>,
Err<E>,
}
enum Option<T> {
Some<T>,
None
}
```
```rust=
// Optionの便利メソッド
fn main(){
// Some(v): 値がある場合
// None: 値が無い場合
let some_value: Option<usize> = Some(1);
let none_value: Option<usize> = None;
// Optionの中身はパターンマッチで取り出す
match some_value {
Some(v) => println!("some value = {}", v),
None => println!("none value"),
};
// Someの場合だけ処理したいならif letの方が便利
if let Some(v) = some_value {
println!("some value = {}", v);
}
// 比較もできる
// ただし、Option<T>のT型がEqやOrdトレイトを実装している必要がある
assert!(some_value != none_value);
// Option<T>には便利メソッドがいろいろある
// Some(v) or Noneを判定
assert!(some_value.is_some());
assert!(none_value.is_none());
// unwrap: (&self) -> T
// Someの場合は中身を返し、Noneの場合はpanicして終了
assert_eq!(some_value.unwrap(), 1);
// assert_eq!(none_value.unwrap(), 1); // panicする
// expect: (&self, &str) -> T
// unwrapと同じだが、panicするときにメッセージを指定できる
assert_eq!(some_value.expect("panic!"), 1);
// assert_eq!(none_value.expect("panic!"), 1); // panicする
// unwrap_or: (&self, T) -> T
// unwrapと同じだが、Noneの場合にpanicする代わりに別の値を返す
assert_eq!(some_value.unwrap_or(0), 1);
assert_eq!(none_value.unwrap_or(0), 0);
// unwrap_or_else: (&self, FnOnce() -> T) -> T
// unwrap_orと同じだが、Noneの場合の値をClosureで指定する
// 遅延評価したいときに使う
assert_eq!(some_value.unwrap_or_else(|| 0), 1);
assert_eq!(none_value.unwrap_or_else(|| 0), 0);
// map: (&self, FnOnce(T) -> U) -> Option<U>
// Someの場合は関数を適用し、Noneの場合はNoneまま返す
assert_eq!(some_value.map(|v| format!("value is {}", v)), Some("value is 1".to_string()));
assert_eq!(none_value.map(|v| format!("value is {}", v)), None);
// map_or: (&self, U, FnOnce(T) -> U) -> U
// unwrap_orのmap版
assert_eq!(some_value.map_or("value is default".to_string(), |v| format!("value is {}", v)), "value is 1".to_string());
assert_eq!(none_value.map_or("value is default".to_string(), |v| format!("value is {}", v)), "value is default".to_string());
// map_or_else: (&self, FnOnce() -> U, FnOnce(T) -> U) -> U
// unwrap_or_elseのmap版
assert_eq!(some_value.map_or_else(|| "value is default".to_string(), |v| format!("value is {}", v)), "value is 1".to_string());
assert_eq!(none_value.map_or_else(|| "value is default".to_string(), |v| format!("value is {}", v)), "value is default".to_string());
// 他にもいろいろあります!
// https://doc.rust-lang.org/std/option/enum.Option.html
}
```
```rust=
fn main(){
// Ok(v): 処理に成功した場合。返す値が無い場合はUnit型()を返すとよい
// Err(e): 処理に失敗した場合。
let ok_value: Result<usize, &'static str> = Ok(1);
let er_value: Result<usize, &'static str> = Err("error message");
// Resultの中身はパターンマッチで取り出す
match ok_value {
Ok(v) => println!("ok value = {}", v),
Err(e) => println!("err value = {}", e),
};
// どちらか片方の場合だけ処理したいならif letが便利
if let Ok(v) = ok_value {
println!("ok value = {}", v);
}
if let Err(e) = er_value {
println!("err value = {}", e);
}
// 比較もできる
// ただし、Reulst<T,E>のTとE両方がEqやOrdトレイトを実装している必要がある
assert!(ok_value != er_value);
// Result<T,E>には便利メソッドがいろいろある
// Ok(v) or Err(e)を判定
assert!(ok_value.is_ok());
assert!(er_value.is_err());
// ok: (&self)
// Option型に変換する。Okの場合はSomeに、Errの場合はNoneになる
assert_eq!(ok_value.ok(), Some(1));
assert_eq!(er_value.ok(), None);
// err: (&self)
// okメソッドの逆バージョン。Okの場合はNoneに、Errの場合はSomeになる
assert_eq!(ok_value.err(), None);
assert_eq!(er_value.err(), Some("error message"));
// unwrap<T,E>: (&self) -> T
// Okの場合は中身を返し、Errの場合はpanicする
assert_eq!(ok_value.unwrap(), 1);
// assert_eq!(er_value.unwrap(), 1); // panic
// expect<T,E>: (&self, &str) -> T
// unwrapと同じだが、panic時のエラーメッセージを指定できる
assert_eq!(ok_value.expect("panic"), 1);
// assert_eq!(er_value.expect("panic"), 1); // panic
// unwrap_or<T,E>: (&self, T) -> T
// unwrapと同じだが、panicする代わりに別の値を返す
assert_eq!(ok_value.unwrap_or(0), 1);
assert_eq!(er_value.unwrap_or(0), 0);
// unwrap_or_else<T,E>: (&self, FnOnce(E) -> T) -> T
// unwrap_orと同じだが、Errの場合の値をClosureで指定する
// 遅延評価したいときに使う
// Optionと違ってErrの値を引数に取るので注意
assert_eq!(ok_value.unwrap_or_else(|_e| 0), 1);
assert_eq!(er_value.unwrap_or_else(|_e| 0), 0);
// unwrap_err<T,E>: (&self) -> E
// expect_err<T,E>: (&self, &str) -> E
// unwrap, expectのokとerrを逆にしたバージョン。okだとpanicする
// assert_eq!(ok_value.unwrap_err(), "error message"); // panic
assert_eq!(er_value.unwrap_err(), "error message");
// assert_eq!(ok_value.expect_err("panic"), "error message"); // panic
assert_eq!(er_value.expect_err("panic"), "error message");
// map<T,E,U>: (&self, FnOnce(T) -> U) -> Result<U,E>
// map_err<T,E,U>: (&self, FnOnce(E) -> U) -> Result<T,U>
// Resultの中身を関数を適用してmapする
// Okの場合に適用するmapとErrの場合に適用するmap_errがある
assert_eq!(ok_value.map(|v| format!("{}", v)), Ok("1".to_string()));
assert_eq!(er_value.map(|v| format!("{}", v)), Err("error message"));
assert_eq!(ok_value.map_err(|s| s.len()), Ok(1));
assert_eq!(er_value.map_err(|s| s.len()), Err(13));
}
```
:::spoiler りんごで例えると?
:::
## Rustの素晴らしさ => フロントも書ける!(WebAssembly)
> プログラミング言語やライブラリの名前ではなく、ブラウザでプログラムを高速実行するための、
「ブラウザ上で動くバイナリコードの新しいフォーマット(仕様)」
である。
Google, Microsoft, Mozzila, Appleによって仕様が策定され開発が進められている。
https://qiita.com/ShuntaShirai/items/3ac92412720789576f22
Rust, WebAssembly, and the future of Serverless by Steve Klabnik
{%youtube CMB6AlE1QuI%}
これを見るとWebAssemblyが今後のDockerの代わりになるかもしれない。。。!
**[yew](https://github.com/yewstack/yew)**
これは[Elm](https://guide.elm-lang.jp/)
```elm=
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = 0, update = update, view = view }
type Msg = Increment | Decrement
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
```
これがRust
```rust=
use wasm_bindgen::prelude::*;
use yew::prelude::*;
struct Model {
link: ComponentLink<Self>,
value: i64,
}
enum Msg {
AddOne,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
value: 0,
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::AddOne => self.value += 1
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
// Should only return "true" if new properties are different to
// previously received properties.
// This component has no properties so we will always return "false".
false
}
fn view(&self) -> Html {
html! {
<div>
<button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
<p>{ self.value }</p>
</div>
}
}
}
#[wasm_bindgen(start)]
pub fn run_app() {
App::<Model>::new().mount_to_body();
}
```
## Rustの素晴らしさ => マルチパラダイム
**オブジェクト思考**
```rust=
pub struct AveragedCollection {
list: Vec<i32>,
average: f64,
}
impl AveragedCollection {
pub fn add(&mut self, value: i32) {
self.list.push(value);
self.update_average();
}
pub fn remove(&mut self) -> Option<i32> {
let result = self.list.pop();
match result {
Some(value) => {
self.update_average();
Some(value)
},
None => None,
}
}
pub fn average(&self) -> f64 {
self.average
}
fn update_average(&mut self) {
let total: i32 = self.list.iter().sum();
self.average = total as f64 / self.list.len() as f64;
}
}
```
※インターフェースに変わる様な`trait`もあるよ。
**関数型**
関数
```rust=
// Rust
// f : |int,int| -> int
fn f (x:int, y:int) -> int { x + y };
// fact : |int| -> int
fn fact (n:int) -> int {
if n == 1 { 1 }
else { n * fact(n-1) }
}
```
```haskell=
{- Haskell -}
f :: (Int, Int) -> Int
f (x, y) = x + y
fact :: Int -> Int
fact n =
if n = 1 then 1
else n * fact(n-1)
```
パターンマッチ
```rust=
// Rust
match e {
0 => 1,
t @ 2 => t + 1,
n if n < 10 => 3,
_ => 4
}
```
```haskell=
{- Haskell -}
case e of
0 -> 1
t @ 2 -> t + 1
n | n < 10 -> 3
_ -> 4
```
代数的データ型
```rust=
// Rust
enum Option<T> {
None,
Some(T)
}
// x : Option<t>
match x {
None => false,
Some(_) => true
}
```
```haskell=
{- Haskell -}
data Maybe a =
Nothing
| Just a
{- x : Maybe t -}
case x of
Nothing -> False
Just _ -> True
```
トレイトと型クラス
```rust=
// Rust
trait Testable {
fn test(&self) -> bool
}
impl Testable for int {
fn test(&self) -> int {
if *self == 0 { false }
else { true }
}
}
fn hello(x:int) -> bool {
x.test()
}
```
```haskell=
{- Haskell -}
class Testable a where
test :: a -> Bool
instance Testable Int where
test n =
if n == 0 then False
else True
hello :: Int -> Bool
hello x =
test x
```
## Rustの素晴らしさ => マクロが書ける!
https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/macros.html
```rust=
macro_rules! write_html {
($w:expr, ) => (());
($w:expr, $e:tt) => (write!($w, "{}", $e));
($w:expr, $tag:ident [ $($inner:tt)* ] $($rest:tt)*) => {{
write!($w, "<{}>", stringify!($tag));
write_html!($w, $($inner)*);
write!($w, "</{}>", stringify!($tag));
write_html!($w, $($rest)*);
}};
}
fn main() {
use std::fmt::Write;
let mut out = String::new();
write_html!(&mut out,
html[
head[title["Macros guide"]]
body[h1["Macros are the best!"]]
]);
assert_eq!(out,
"<html><head><title>Macros guide</title></head>\
<body><h1>Macros are the best!</h1></body></html>");
}
```
## Rustの素晴らしさ => アトリビュートが便利!
**#[derive]が便利!**
https://qiita.com/apollo_program/items/2495dda519ae160971ed
**serde**
https://qiita.com/garkimasera/items/0442ee896403c6b78fb2
## 所有権/借用/ライフタイム
所有権のルールはこれだけ
- Rustの各値は、所有者と呼ばれる変数と対応している。
- いかなる時も所有者は一つである。
- 所有者がスコープから外れたら、値は破棄される。
突然ですが、これコンパイル通ると思いますか?
```rust=
fn main() {
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
}
```
これは?
```rust=
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
```
これはどうでしょう?
```rust=
// rust
fn main(){
let a = String::from("aaa");
let b = just_return(a);
println!("{}", a);
println!("{}", b);
}
fn just_return(x: String) -> String{
return x
}
```
これなら?
```rust=
fn main(){
let a = String::from("aaa");
let b = just_return(&a);
println!("{}", a);
println!("{}", b);
}
fn just_return(x: &String) -> String{
return x.to_string()
}
```
借用
じゃーこれは?
```rust=
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
```
これなら?
```rust=
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
```
```rust=
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
```
```rust=
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1はここでスコープを抜けるので、問題なく新しい参照を作ることができる
let r2 = &mut s;
```
```rust=
let mut s = String::from("hello");
let r1 = &s; // 問題なし
let r2 = &s; // 問題なし
let r3 = &mut s; // 大問題!
```
結局ボローチェッカーを信じれば大丈夫!
## 実装編(Installから〇〇アプリまで)
### 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
```
### 2. TDD
### 3. [watch](https://github.com/passcod/cargo-watch)を使う
```shell=
cargo install cargo-watch
```
### 4. [clippy](https://github.com/rust-lang/rust-clippy)
```shell=
rustup component add clippy
cargo clippy
```
### 5. 何作る?
## おわりに
- Rustはモダンな言語機能と低水準プログラミングが両立できる。
- とにかくテストがしやすい!!
- WebAssemblyが作りやすい。