知っておきたいRustの基本

開発

Rustを勉強してみて、押さえておきたい点をいくつかまとめました。

周辺ツール

rustup

Rustのバージョン管理ツール。
rustupをインストールすると、通常以下もインストールされる。

  • rustc:Rustコンパイラ
  • cargo:Rustのパッケージマネージャ
  • rust-std:Rustのスタンダードライブラリ
  • rust-docs:ドキュメント

パッケージ、ライブラリ、モジュール

先にパッケージ・クレート・モジュールの関係性を伝えておくと、こんなイメージ。

クレート(crate)

バイナリやライブラリのことをRustではクレートと呼ぶ。

バイナリ:人間が読めないファイル。CPUやプロセッサをビットで直接制御したりする。
ライブラリ:何かしらのタスクを最適化するために、事前に作られたコードの集合。

パッケージ

一連の機能を提供するクレートのこと(クレートは1つでも複数でもOK)。

モジュール

一般的には、特定の機能を持ったひとまとまりの構成要素。
Rustでも同様で、クレート内のコードなどに有効であり、modで定義できる。モジュールの中にモジュールがあってもOK。

mod myjob {
    mod cooking {
        fn cut() {}

        fn boil() {}
    }

    mod cleaning {
        fn wash_dishes() {}

        fn wipe_windows() {}
    }
}

Cargo.toml

パッケージなどの設定ファイル。
npmにおけるpackage.jsonみたいなもの。

命名規則

基本的にスネークケース。

基本文法

定数

constで宣言。型宣言も必須。関数呼び出し結果などには使用できない。

const MAX_COUNT: u32 = 100_000;

変数

letで宣言する。mutを追加すると、mutable(可変)であることを意味する。

let mut x = 2;

シャドーイング

letで変数を上書きできる。mutを付与した宣言とは違い、型も変えて新しくセットするイメージ。
加工のために一時的に作った変数でも、そのままの名前で再加工できたりする。

let spaces = "   ";
let spaces = spaces.len();

アトリビュート(attribute)

#[]#![]を使用して表現される。クレート、モジュールなどに対するメタデータ。
下記コードでは、popup関数でwasm_bindgenを使用することを意味する。

#[wasm_bindgen]
pub fn popup(message: &str) {
    alert(&format!("Hi, {}!", message));
}

定数・変数のbind {}

{}に変数が補完される。下記コードでは、Hi {name}, {message}!となる。

#[wasm_bindgen]
pub fn popup(name: &str, message: $str) {
    alert(&format!("Hi, {}, {}!", name, message));
}

メモリ関係

スタック・ヒープ

スタックには固定サイズのデータが格納される。
一方、ヒープにはデータサイズが不明であったり、可変なものが格納される。

ヒープを使用するとき、OSが十分な大きさの空領域を見つけ、使用中にし、ポインタ(その領域のアドレス)を返す。この一連のことを「ヒープに領域を確保する、allocateする」と呼ぶこともある。

【補足】ヒープに格納されるデータでも、一部はスタックに格納されることもある。
例えば、String型はポインタ、長さ、許容量の3つで構成されるが、これらの情報はスタックに保持され、データの中身はポインタが示すヒープ領域に保持される。

所有権

メモリ上にあるデータを参照する権利のこと。所有権をもつ変数を所有者と表現する。
ガベージコレクションに似た働きをする側面もある。

  • 各値(メモリ上のデータ)は、所有者と呼ばれる変数と対応している
  • 所有者は常に一つ(複数の変数が同じメモリを参照できない。後述のムーブが例。)
  • 所有者がスコープから外れたら、値を破棄する(メモリ解放)

ムーブ

所有権が移動すること。
ヒープ領域の同じメモリを複数の変数が参照する場合、古い方の参照を無効にする(所有権が移動している)。あるメモリを参照する変数は常に1つになる。

let text1 = String::from("hello");
let text2 = text1; // 両者は同じメモリを参照するため、ムーブが起き、これ以降text1は使用できない。

クローン

ディープコピーもcloneというメソッドを使用することで可能。

let text1 = String::from("Clone!");
let text2 = text1.clone();

println!("text1 = {}, text2 = {}", text1, text2);

ディープコピーとは
ポインタをコピーするのではなく、実際のデータをコピーすること。
別々のデータとして扱われるため、相互に影響を与えることはない。

よく見る演算子

&

参照と借用を行うときに使われる。

  • 参照:値の所有権はそのままで、所有者以外の変数がデータを参照すること。
  • 借用:関数の引数に参照を取ること。
fn main() {
    let text1 = String::from("Hi");

    let length = calculate_length(&text1);

    println!("length: {}", length);
}

fn calculate_length(text: &String) -> usize {
    text.len()
}

参照・借用した値を変更するには、mutをつける必要がある。

fn main() {
    let mut text1 = String::from("Hi");

    addName(&mut text1);
}

fn addName(text: &mut String) {
    text.push_str(", Tom!");
}

!

マクロ(ある機能の集合)であることを表す。マクロはメタプログラミングの1つ。

println!("Hello, world!");

メタプログラミング
コンピュータプログラムそのものをデータのように扱えるプログラミング技術。(引用:Wikipedia: メタプログラミング
あるコードを記述するために、それを呼び出すより少ないコードを書くイメージ。
関数に似ているが、Rustの場合実行タイミングなど異なる点がいくつかある。

WebAssembly関連

主要パッケージ

wasm-pack

RustをWebAssembly(以下、WASM)にコンパイルするためのパッケージ。

wasm-bindgen

JavaScriptのものをRustにインポートし、RustのものをJavaScriptにエクスポートできるパッケージ。
JSとRustの型を取り持ったり、JSの例外をRustで処理したりできる。

RustコードをWASMにビルドする時

以下のことが行われる。

  1. RustコードをWASMにコンパイル
  2. そのWASMファイルをブラウザが理解できるモジュールにラップするJavaScriptファイルを生成。(wasm-bindgen)
  3. pkgディレクトリを作成し、JSファイルとWASMファイルをそこに移動させる
  4. Cargo.tomlを読み込み、それに合わせたpackage.jsonをpkgディレクトリ内に作成する
  5. コピーしたREADMEをpkgディレクトリに移動

WASMとブラウザ

ブラウザでWASMモジュールを実行する時、下記を行う必要がある。

  • .wasmファイルの読み込み
  • インスタンス化
  • 起動

そして、それをJavaScript APIで実行できる。(fetch 等)
※WASMはブラウザだけでなく、Node.jsなどサーバーでも実行可能。

もっと詳しく知りたい方はRustドキュメント(英語日本語)をチェックしてみてください!

タイトルとURLをコピーしました