Rust入門してみた

自己紹介

  • @takatori
  • チームラボ株式会社
  • 2年目
  • Rust, JavaScript, AWS

Rust

  • システムプログラミング言語
    • OSとかデバイスドライバとか書くやつ
  • GCがないのにメモリ管理やってくれる
  • 安全・速度・並列
  • ゼロコスト抽象化

よくわからないけどすごそう

  • メモリ安全性
  • 代数データ型
  • パターンマッチ
  • トレイト
  • マクロ

メモリ安全性

 C言語のメモリ管理

  • 安全なコードを書くのが難しい
    • プログラマがメモリの確保・解放をする
    • 脆弱性を埋め込んでしまう
      • バッファーオーバーラン
      • ダングリングポインタ
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  char *a = malloc(1);
  free(a);  // 開放

  char *b = malloc(1);
  *b = 'b';
  *a = 'a'; // ダングリングポインタへの操作
  printf("%c", *b); // "a" ("b"ではない!?)
}

GCがある言語のメモリ管理

  • メモリ管理はガーベージコレクタが行ってくれる
    • 参照されなくなった領域はGCが回収
      • フルGCが走るとシステムが止まる
         

Rustのメモリ管理

Rustは以下の3つの概念を用いることで

GC無しで安全なメモリ管理を提供する

  • 所有権
  • 借用
  • ライフタイム

所有権

  • Rustはリソースに対する束縛を一つに制限する
  • 束縛が一つしかないのでダングリングポインタは発生しない
  • 束縛がスコープから外れる時、リソースを解放する

 

fn foo() {
    let v = vec![1, 2, 3]; // 変数束縛

    let v2 = v; // 所有権がvからv2に変更される

    println!("{}", v);  // ここでvを参照しようとするとコンパイルエラー
}
// v2がスコープから外れるのでメモリも解放される

所有権を返す

// 値渡し、所有権も移る
fn foo(v1: Vec<i32>, v2: Vec<i32>) -> i32){
    // v1とv2についての作業を行う
    42
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];

let answer = foo(v1, v2); 

// 所有権が移動したのでv1、v2をここで使用するとコンパイルエラー
fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
    // v1とv2についての作業を行う

    // 所有権と関数の結果を返す
    (v1, v2, 42)
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];

let (v1, v2, answer) = foo(v1, v2);

借用

  • 参照を取ることで所有権を借りることができる
  • スコープから外れるときに所有権を返す
  • リソースの割当は解除しない
// 所有権を借りる
fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
    // v1とv2についての作業を行う

    // 答えを返す
    42
}

let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];

let answer = foo(&v1, &v2);

// ここではv1とv2が使える!

ライフタイム

  • 借用チェッカー
    • 全ての借用に問題がないことを確認する仕組み
  • 参照の有効なスコープのこと
{
  let r;
  {
      let x = 1;
       r = &x; 
  // ここまでがxの有効なスコープ
  } 
   assert_eq!(*r, 1);  // xのスコープ外で参照しているのでコンパイルエラー
}

代数的データ型

enum Option<T> {
   None,
   Some(T)
}

// ScalaのEitherみたいなやつ
enum Result<T, E> {
    Ok(T),
    Err(E),
}

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

パターンマッチ

// x: i32
match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}


// x : Option<t>
match x {
  None    => false,
  Some(_) => true
}

トレイト

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for i32 {
    fn area(&self) -> f64 {
        println!("this is silly");

        *self as f64
    }
}

5.area();

クロージャ

let plus_one = |x: i32| x + 1;

assert_eq!(2, plus_one(1));

ゼロコスト抽象化

  • すべてコンパイル時に解決される
  • 実行時にオーバーヘッドが存在しない
  • C++並の速度が出る

その他

  • 型推論
  • hygienic macro

まとめ

  • Rustたのしー

Rust入門してみた

By Satoshi Takatori

Rust入門してみた

  • 936