KMC活動ブログ

京大マイコンクラブの活動の様子を紹介します!!

Rustは開発中だけど面白い!【KMCアドベントカレンダー19日目】

あ…これゲームの紹介みたいなタイトルだわ…

この記事は、今年もやります!KMCアドベントカレンダー!! - KMC活動ブログの19日目の記事です。

また、昨日18日目の記事は八ツ橋シューティングについて(前編) 【KMCアドベントカレンダー18日目】 - KMC活動ブログでした。

みなさんこんばんは。KMC二回生のnendokiです。MacBookからMac OS Xを削除するものに鉄槌を下す仕事をしています。

ところで、流行り廃りがあまりにも激しく、あらゆるドキュメントはジンバブエドルのごとくちり紙になることで有名なIT業界ですが、その流れはもっと根本的な、プログラミング言語にまで押し寄せているようですね。

少し前には無数のJS(など)に変換するための言語(いわゆるAltJS)がこの世に産み落とされ、Apple社がSwiftというプログラム言語を発表したことも記憶に新しいかと思います。

今回はタイトルの通りRustというプログラミング言語を紹介します。RustはFirefoxで有名なMozillaが開発主体となっている言語で、2010年ごろ発表だとか2012年にバージョン0.1が公開されたとか(wikipediaより)で割と最近の言語だと思います。印象としてはC++の文法に関数型言語の概念を足してモダンで安全になった言語って感じなので、ビルド環境を整えさえすれば気軽に書き始めることができます。

書き手の時間もないので、Rustの書き方とか機能とかチュートリルとかは該当記事に譲るとして、せめて何が面白いのかなーくらいをつらつら書いていきます。開発中なのでこの仕様はすぐに変わるかもしれません。

参照

Rustの公式ページでは、言語デザインについての哲学についての言及(The Rust Design FAQ)がなされています。

そこにはメモリの安全性は譲れないといった感じの強めの文章が目に飛び込んでくることから、値の扱いについてが言語設計の中心にあることがわかります。実際にプログラマはデータをコピーを減らしつつメモリ上にどう載せるかを考えてプログラムを書くことになります。

特徴的な例として、Rustではポインタは使わない代わりに、nullが許容されない参照を用いることになります。

参照にはlifetimeというラベルのようなものが与えられており、これは値が生成された場所に対して静的に決められます。このlifetimeは関数の返り値とか構造体への格納を行うときに明示する必要があります。逆に言えば、lifetimeのおかげで参照を抱えた構造体といったC++的には微妙極まりないものでも安全に扱うことができるわけですね。

もう一つ、参照には面白い概念があります。それがborrowingと呼ばれるしくみで、大雑把には変数が参照によって参照されている場合、参照元の変数を書き換えることを禁止する仕組みです。参照のスコープを外れることで、参照元の変数は再び書き換え可能になります。この機能は、同じ変数を同時に書き換えたり、使用中に突然値が変わるようなものを防ぐことになるため、マルチプロセスなプログラムを作るときには強力な仕組みとなるでしょう。

box

「lifetimeがあれば参照だって返せるし安全高速だよ✩」と言われても値を作って返す系の関数にはまるで意味がないだろ馬鹿野郎という話になりますね。実際、ポインタを排して参照だけとするとヒープ領域を操作することができません。boxはヒープ領域へのポインタを表す型です。デアドレスも参照と同じ演算子を使います。

このbox、式に対してboxとつけるだけでヒープ上に確保されたことになるお手軽演算子でもあります。余談ですが、古いドキュメントとかでは~演算子が割り当てられたりしています。

boxはヒープ領域へのポインタという書き方は少しだけ正確ではなく、正しくはヒープ領域へのuniqueポインタです。boxを他の変数へコピーすると、元の変数が失効し、参照できなくなります。普通にスコープアウトした場合にメモリを開放すれば良いので、安全に扱うことができそうですね。

ところで、関数へはどのように渡せば良いでしょうか。関数の引数はコピーとして与えられるので、普通に与えたら壊れます。この場合は、boxそのものか一度デアドレスしたものへの参照を与えてやることになります。参照とboxの仕組みが若干ぶつかっている気がしますが、そこは今後に期待ですね。

boxの機能は非常によく使うもののため、幾つかの便利構文があります。その一つにlet boxのように書くことで気軽にunboxできます。以下の例はあまり意味のあるものではありませんが、box内部の値を、boxを破壊することなく取り出しています。box内部への参照が欲しい場合は、let box refと書くことができます。

    let x = box 1u;
    let box y = x;

このbox refという記法はパターンマッチ内でも使うことができるため、boxを含んだenumといったものも簡単に扱えるわけですね。

便利なenum

Cの機能にあったenumとかC++11で新しくなった*1enumとかからさらに進化したenumです。いわゆる関数型プログラミングお馴染みの代数的データ型が採用されています。要するに強い型付けがなされるtagged unionですね。

C++とかでoptional型を作ろうとすると非常に面倒くさいらしいですが、Rustなら以下の通りです。

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

Some(T)というのは、T型の値を持つSomeという値になるわけですね。取り出す時にはパターンマッチを使います。

fn is_some<T>(x: &Option<T> ) -> bool {
    match *x {
        Some(_) => true,
        None => false
    }
}

Someの値を使いたいときは_をxとかに置き換えればいいです。コピーコストが怖い場合はref xとか、boxの中身が欲しい場合はbox refとか書くと幸せですね。

ちなみに、Option型は、unsatableながら便利な関数が多いので一度見てみると良いかもしれません。

trait

haskellの型クラスのようなものです(完)。とはいかないので簡単に述べると、未知の型に対して関数を宣言したり実装したりすることができます。オブジェクト指向的に使いたい場合は、構造体を宣言してすぐにその構造体に対してtraitを設定することになります。Javaでいうインターフェースのような使い方をしたい場合は、traitとして適当なインターフェース名にして、他の構造体に対応する実装をimplで個別に与えてやる形になります。

この時、第一引数がself&self&mut selfの場合は、インスタンスメソッドになり、それ以外の場合はクラスメソッドになります。ちなみキャストの関係はself->&self<-&mut selfなので、特にメンバ変数を変更する必要がない場合は&selfで、どうしても変更する必要がある場合は&mut selfだけを実装するのがオススメだそうです。

C++で言う所のtype traitsなのでしょうか。まあ扱いやすくなっていると思います。

まとめ

三つだけかよとお思いの方もいらっしゃるかと思いますが、単純に私のHPが尽きました。チュートリアルにはもっと魅力的な機能がわかりやすく解説されているのでオススメです。

参照だけで十分だとか静的型検査なんて必要ないとのたまう軟弱な言語と比べれば、いかに安全さと速さを求めているかがなんとなくわかるとおもいます。

さて、次回はhatsusatoさんの「C++11 の rvalueの話」だそうです。C++を軽くドついたあとのになってしまい、少し申し訳ないですね。

それでは。