Rust 1.9 が本日(日本時間2016年5月27日)リリースされました。Rust は、安全性、スピード、並列プログラミングにフォーカスした、システムプログラミング向けの言語です。
また同時に、次のバージョン 1.10 がベータ版になりました。1.10 のリリース予定日は、6週間後の2016年7月8日(日本時間)です。
もしこれから Rust を始めるのでしたら、オフィシャルマニュアルの 日本語翻訳版 を読むのがいいでしょう。また、@joemphilips さんが立ち上げた、「Rust by Example」の 日本語翻訳 作業も、現在進行中です。(翻訳の 協力者を募集中 です!)
(これ以降はリリースアナウンスの抄訳です)
1.9 安定版の内容
アンワインド(unwind) の制御
Rust 1.9 における最大の進化は、std::panic
モジュールが安定化されたことでしょう。これには、panic によって始動した巻き戻し処理(アンワインド処理)を停止するメソッドが含まれています。
use std::panic;
let result = panic::catch_unwind(|| {
println!("hello!");
});
assert!(result.is_ok());
let result = panic::catch_unwind(|| {
panic!("oh no!");
});
assert!(result.is_err());
この新しい API は RFC 1236 で定義されています。
一般的に、Rust では、操作を失敗させるような2種類の要素を区別します:
- 想定内 の問題によるもの。ファイルが見つからないなど。
- 想定外 の問題によるもの。インデックスが配列の境界を超えたなど。
想定内の問題は、通常は、あなたがコントロールできないような状況によって引き起こされます。強固なコードは、環境に起因するあらゆる問題に対処できるべきです。Rust では、想定内の問題は Result
型 で対処します。これを使うと、関数が呼び出し元に問題の詳細を伝えることができ、呼び出し元では、それに基づいたきめ細やかな対応を取ることができます。
想定外の問題はバグに分類され、契約やアサーションに違反した時に起こります。想定外であることから、きめ細やかな対応をとることは意味をなさないでしょう。代わりに、Rust では、panic による「fail fast(早めに失敗する)」アプローチを採用しています。これはデフォルトでは、エラーに出会ったスレッドの使っていたスタックを巻き戻します(デストラクタのみを実行し、それ以外のコードは実行しません)
他のスレッドは走り続けますが、panic を起こしたスレッドと(チャネルや共有メモリを通して)やり取りする際に、panic に気づきます。つまり、panic は、プログラムの実行を、何らかの「隔離境界」に達するまでの範囲でアボートします。境界の反対側のコードは実行を継続でき、場合によっては、きめ細やかな方法で panic から「復帰」できるかもしれません。例えばサーバーでは、スレッドの1つで発生したアサーションエラーによって、全体をダウンさせたくはないでしょう。
新しい catch_unwind
API は、(訳注:panic を起こした)スレッド内 に新たな隔離境界を作る手段を提供します。これが必要となる、2つの事例があります:
- Rust を他の言語に組み込む
- スレッド管理の抽象化
最初のケースですが、言語の境界を超えてアンワインドすることは未定義動作となっており、多くの場合 segfault を起こすでしょう。panic を捕捉できるようにすることは、Rust のコードを C API を通して安全に公開できることを意味し、アンワインドを C のコード側のエラーに変換できるようになります。
2番目のケースですが、スレッドプール・ライブラリを想像してください。プール内のスレッドが panic した時、通常は、スレッド自体を kill したいとは思わないでしょう。それよりも、panic を補足し、プールのクライアントにそれを伝えることを望むでしょう。catch_unwind
API は、resume_unwind
と対になっており、これを使うと、プールのクライアントが属する側で、panic を再始動できます。
どちらのケースでも、スレッド内に新たな隔離境界を導入し、次に、panic をどこか他の場所で通用するエラーへと変換するようになるでしょう。
最後にもう一点、なぜ catch_panic
ではなく catch_unwind
なのでしょうか? 実は panic に対して、また別の戦略を追加することを 検討中 なのです。これを使うと、汎用的な フック を実行した後に、プロセス全体をアボートできます。アプリケーションによっては、これが、プログラムのバグに対する最も適切な処置かもしれません。また、アンワインドを避けることが、実行速度やコードサイズの面で有利なこともあります。
訳注:catch_unwind
については、こちらの記事でも詳しく紹介しています。
Deprecation ワーニング
ライブラリ作者向けに、新たなアトリビュートが導入されました。これを使うと、API に deprecation ワーニングによる目印を付け、クレートのユーザーたちに、その API ではなく、代替の API を紹介できます。Deprecation ワーニングは、標準ライブラリでは以前から使われていましたが、この度、RFC 1270 によって、エコシステム全体で使えるものとなりました。
新たなターゲット
新たに、以下のターゲット向けの標準ライブラリを公開しました。
- mips-unknown-linux-musl,
- mipsel-unknown-linux-musl, and
- i586-pc-windows-msvc.
最初の2つのターゲットは、クロスコンパイルの観点から、特に興味深いものです。詳しくは rustup
に関する最近のブログポスト をご覧ください。
コンパイル時間の改善
型の統合(unification) の際に行われる 変数の等価性の比較について、時間計算量が改善 されました。O(n!) から O(n) へ減少しています。その結果、一部のプログラミング・パターンのコンパイルが、大幅に高速化されました。
特殊化(specialization) の利用を開始
このリリースでは、標準ライブラリ内で、特殊化(specialization) の初期的な利用が始まっています。特殊化は、いまのところ nightly だけで使えますが、この機能は、具体的な型情報に基づいて、汎用的なコードを、自動的に、より特化した形にします。
標準ライブラリにおける1つの例は、文字列のスライス(&str
)から、所有権のある String
への変換でしょう。メソッドの1つ to_string
は汎用的な API 由来で、以前は相対的に遅いものでした。その一方で、to_owned
という独自の実装は、より高いパフォーマンスを実現していました。特殊化を用いたことで、この2つのメソッドは、いまや等価なもの となりました。
このような、特殊化のシンプルな実験を経験したことにより、今後のリリースでは、同様の手段でもっと多くのパフォーマンス改善を実現できるでしょう。
ライブラリの安定化
1.9では、約80のライブラリ関数とメソッドが安定化されました。最もメジャーなのは、すでに紹介した std::panic
モジュールですが、他にもたくさんあります。
ネットワーキング
-
TcpStream
、TcpListener
、UdpSocket
に、接続に関する各種設定を行うためのメソッドが多数追加されました。 -
SocketAddr
とその仲間に、set_ip()
とset_port()
便利メソッドが追加されました。
コレクション
-
BTreeSet
とHashSet
にtake()
、replace()
、get()
メソッドが追加されました。これらを使うと、オリジナルの要素の所有権を取り戻せます。 -
OsString
にいくつかのメソッドが追加され、String
とほぼ同格になりました。 - スライスに、
memcpy
の安全版であるcopy_from_slice()
が追加されました。
エンコーディング
-
char
が UTF-16 にデコードできるようになりました。(訳注:char
型は UTF-32 でエンコードされており、&str
やString
は UTF-8 でエンコードされています)
ポインタ
- 生ポインタに
as_ref()
とas_mut()
が追加されました。これらはOption<&T>
を返し、null ポインタはNone
へ変換されます。 -
ptr::{read,write}_volatile()
を使うと、生ポインタに対して、volatile な読み込みと書き込みを行えます。(訳注:volatile により、コンパイラによる最適化を抑止します。また、マルチスレッド動作中に、各スレッドから最新の値が見えることが保証されます5月27日削除:各スレッドから最新の値が見えることは保証 されない ようです。詳しくは、本記事の コメント をご参照ください。Thanks @yohhoy san!)
最後に、libcore
の多くの型には Debug
実装がありませんでした。1.9リリースでは、これが対応されました。
詳しくは、詳細なリリースノート をご覧ください。
Cargo の機能
Cargo には2つの大きな変更が加わりました:
1つ目は、Cargo が 複数同時に実行できるようになった ことです。(訳注:リンク先の説明によると、これまで Cargo は、1台のマシン上では、2つ以上同時に実行することができなかったそうです)
2つ目は、新しいフラグ RUSTFLAGS
が 追加 されたことです。このフラグを使うと、環境変数から、rustc
へ任意のフラグ(複数)を与えられます。例えば、パッケージ管理ソフトなどで便利です。
詳しくは、詳細なリリースノート をご覧ください。