RustのRFC一覧 (~1240)

概要: RustのRFCの一覧を作ろうとしたが、あまりに多いのでとりあえず1240までで公開することにした。なお、単に全てのRFCを列挙したいならばここを見ればよい。 このRFCはRustのコミュニティーが管理しているものであり、 “RFC” の元祖であるIETF RFCとは関…

RustBelt試行中: get_x の一般化

RustBeltの examples/get_x.v をいじってみた。 From iris.proofmode Require Import tactics. From lrust.typing Require Import typing. Set Default Proof Using "Type". Section get_x. Context `{typeG Σ}. Definition get_x : val := funrec: <> ["p"]…

RustのRFC一覧 (~0900)

概要: RustのRFCの一覧を作ろうとしたが、あまりに多いのでとりあえず0900までで公開することにした。なお、単に全てのRFCを列挙したいならばここを見ればよい。 このRFCはRustのコミュニティーが管理しているものであり、 “RFC” の元祖であるIETF RFCとは関…

RustBeltのビルド (Windows)

概要: Coqと高階分離論理を用いたRustの検証プロジェクトであるRustBeltの論文とCoqの証明ファイルが公開されたので、とりあえずビルドしてみた。 RustBeltはopamを使うと簡単にビルドできるようだが、ここではWindowsでビルドしてみた。 環境 64bit Windows…

RustのRFC一覧 (~0500)

概要: RustのRFCの一覧を作ろうとしたが、あまりに多いのでとりあえず0500までで公開することにした。なお、単に全てのRFCを列挙したいならばここを見ればよい。 このRFCはRustのコミュニティーが管理しているものであり、 “RFC” の元祖であるIETF RFCとは関…

RustのRFC一覧 (~0200)

この記事は古いのでこちらをご覧ください 概要: RustのRFCの一覧を作ろうとしたが、あまりに多いのでとりあえず0200までで公開することにした。なお、単に全てのRFCを列挙したいならばここを見ればよい。 このRFCはRustのコミュニティーが管理しているもので…

Rustのトレイト選択

概要: トレイト選択とは、トレイト制約の解決方法を検索するコンパイラの処理であり、型推論にもコード生成にも使われる重要な部品である。本記事ではトレイト選択の動作の詳細について解説する。 トレイト選択とは トレイト選択 (selection) とは、トレイト…

Rustにおける左辺値選好と可変性調停

概要: Rustのミュータビリティー推論に使われる左辺値選好と可変性調停について説明する。 左辺値選好 Rustの型推論では、期待型のほかに、左辺値選好 (lvalue preference) という状態もトップダウンに渡される。 LvaluePreferenceは以下のどちらかの値をと…

Rustのバイト列リテラルの型

Rustのリテラルの型検査は check_lit にある。これによると、文字列リテラルの型は &'static str である一方、バイト列リテラルの型は &'static [u8] ではなく、 &'static [u8; N] である。 そのため、以下のようなコードがコンパイルできる。 fn main() { l…

Rust unsize期待型とキャスト期待型

Rustの期待型は以下のようなデータ構造になっている。 #[derive(Copy, Clone, Debug)] pub enum Expectation<'tcx> { NoExpectation, ExpectHasType(Ty<'tcx>), ExpectCastableToType(Ty<'tcx>), ExpectRvalueLikeUnsized(Ty<'tcx>), } このように4つのコン…

Rust 関数呼び出しの期待型の伝搬

以前の記事で説明したように、Rustでは型強制や整数リテラルなどの特殊な型推論をシームレスに行うために期待型という概念を導入している。 この期待型はトップダウンに伝搬されるが、先ほどの記事で挙げた例 fn main() { let x : Box<[_]> = Box::new([1, 2…

Rustにおける演算子の型推論の特殊ルール

概要: 原則として x + y は ::std::ops::Add(x, y) の構文糖衣であるが、型推論で特別扱いされる。 演算子の脱糖 演算子の脱糖は型推論の後、HIRからMIRへの変換のタイミングで行われる。原則として x + y は ::std::ops::Add(x, y) の構文糖衣である。これ…

Rust パターンマッチの変数束縛とコンストラクタ/定数の区別

パターンマッチを持つ言語では、変数束縛とコンストラクタ/定数が構文上曖昧である場合がある。Rustでは以下の規則に従っている。 以下のように、構文的にパスであるとわかる場合は、常にコンストラクタ/定数とみなす。 :: を含んでいる場合。 (::A, self::A…

Rustで use std; が必要なときとエラーになるときがあるのは何故か

Rustでは use std; や use rand; のようなインポートが必要な場合と、逆に書くとエラーになる場合がある。 簡単に言うと トップレベルモジュールでは、書くとエラーになる(既にある名前と衝突する)。それ以外の場所では、必要な場合がある。 これは名前をロ…

Rustでunsafeが必要な操作

Rustでは unsafe の表明を行わない限り未定義動作が発生しない。コンパイラや言語仕様のミスにより未定義動作が発生しうる場合には優先的に修正される。(なお、整数オーバーフローのように特定条件下でエラーになるものであっても、正しくunwind/abortできる…

Rustコンパイラのコンパイルの流れ

Rustコンパイラは同梱のrustbuildというツールでビルドされる。これはRustとPython2で書かれている。 README.md にも説明が書かれているが、ここで改めて説明をしてみる。 ./x.py は src/bootstrap/bootstrap.py にリンクされている。これは次のような動作を…

Rustコンパイラの自前ビルド

コンパイラの動作を調べるにあたって、いちいちmasterをビルドするのは不便なので、安定版の自前ビルドを作成することにした。 $ wget https://static.rust-lang.org/dist/rustc-1.18.0-src.tar.gz $ tar xvf rustc-1.18.0-src.tar.gz $ cd rustc-1.18.0-src…

RustでOptionやResultの配列ができてしまったときの一般的なテク4つ

Vec<Result<_>> ではなく Result<Vec<_>> を得る collect() 関数を使うと、 Vec<Result<_>> を得ることもできるし、 Result<Vec<_>> を得ることもできる。変換先の型を明示することで区別する。 fn main() { // 全てSomeならSome(配列)を返し、どれかがNoneなら全体もNoneになる assert_eq!([So</vec<_></result<_></vec<_></result<_>…

Rust パターンマッチの網羅性

Rustのパターンマッチは網羅性が検査され、網羅的でない場合はコンパイルエラーになる。網羅性は以下のように検証される。 型の分類 パターンマッチの網羅性をするときには、全ての型がADTのように扱われる。つまり、有限個の引数をとるコンストラクタが有限…

Rustのmatchにおけるカンマの省略

Rustの match の腕はカンマで区切られるが、これが省略できる場合が2つある。 末尾のカンマ => { .. } の直後のカンマ fn main() { match Some(1) { Some(x) => { } // => { .. } の直後にはカンマは省略可能 None => () // 末尾のカンマは省略可能 } } この…

Rustの構造体/列挙型フィールドの並べ替え

現在の安定版では無効化されているが、Rustのbeta/nightlyには構造体/列挙型フィールドの自動並べ替えが実装されている。この動作を説明する。 例 以下のようなプログラムを書くと、現在の安定版では6が表示され、beta/nightlyでは4が表示される。 use std::…

Rustの配置構文とbox構文

概要: Rustの不安定機能である配置構文とbox構文の仕組みを説明する。 配置構文の動機 Rustの値渡しはデフォルトでムーブであり、コピーコンストラクターのような重い処理が勝手に実行されることはないから、多くの場面では値渡しのコストはそれほど高くない…

Rustの型推論の概略

概要: Rustの型推論の大枠を説明する。 なお、筆者もRustの型推論の動作を詳細に把握しているわけではない。 短くまとめると Rustの型推論はHindley-Milner型推論ベースである。したがって後方の式や文の内容から手前の型が推論されることもある。しかし以下…

Rustのtry-catch構文

Rustのnightlyに新しく入ったtry-catch関連構文を紹介する。 do catch によるcatch構文 #![feature(catch_expr)] use std::fs::File; use std::io::{self, BufReader, Read}; fn main() { do catch { let f = File::open("foo.txt")?; let mut f = BufReader…

Rustのderiveはあまり頭がよくない

概要: Rustの derive はあまり頭がよくない。 derive がドジを踏む例 derive の問題は顕在化しやすく、RustコンパイラのGitHub上でも何度も重複するissueが投げられていた。今は主に #26925 を中心に議論がまとまっているので、そちらを参照するとよいだろう…

Rustのスレッドローカル変数について

Rustには2種類のスレッドローカル変数がある。 #[thread_local] 属性 #[thread_local] 属性は、指定した static アイテムをスレッドローカルにするようにLLVMに伝える。これによりC言語のスレッドローカル変数と同様に、ELFリンカ側の処理によりスレッドロー…

Rustにおけるキーワードの役割一覧

概要: Rustにおけるキーワードの役割をできる限り列挙する。キーワードではないものについても一部取り上げる。 強キーワード部門 キーワード 役割 as 修飾パス <T as Tr>::foo useとextern crateにおける別名 use foo as bar; 型の変換 x as usize box box式 box 1 b</t>…

Rustにおける記号の役割一覧

概要: Rustにおける記号の役割をできる限り列挙する。 ASCII制御文字部門 文字 役割 HT (水平タブ) 空白 LF (ラインフィード) 空白文字列中の改行シバン #! や行コメント // の終わり VT (垂直タブ) 空白 CR (キャリッジリターン) 空白CRLFでLFと等価に振る…

FnBoxについて

Rustの FnBox について、動機・仕組み・問題点を説明する。 FnBox の動機 以前の記事では、「「クロージャを boxせずに 返したい」という欲求は人類の四大欲求のひとつと言われている。 」と書いたが、出所の異なるクロージャを同じ型で扱う必要がある場合は…

Rustで引数型と戻り値型がSizedでなくてもよい条件

過去の記事でSizedについて説明したが、関数の引数と戻り値についてはやや直感に反する条件が適用されているので説明する。 戻り値型: 関数やメソッドが本体をもつ場合に検査される。本体を持たない場合は Sized でなくてもよい。 引数型: それ自体は検査さ…

RustのUnsafeCellとFreeze

概要: UnsafeCell の存在目的について説明する。 UnsafeCell とは UnsafeCell とは、その名前の通り、内部可変性(interior mutability)を実現するためのunsafeはプリミティブである。 UnsafeCell の代表的な用途は内部可変性を実現するための安全なラッパー…

Rustの身代わりパターン

概要: &mut 参照に対して所有権が必要な操作をするときは特定のパターンが用いられる。これを身代わりパターンとでも呼ぶことにする。 例: 単方向リンクリスト またしても単方向リンクリストを考える。 struct List<T> { root: Option<Box<Node<T>>>, } struct Node<T> { value</t></box<node<t></t>…

Rustコンパイラのデバッグ出力を見る

Rustコンパイラの細かい挙動を追うには、コンパイラ内に設置してある debug!(..) の出力を追う手がある。 デバッグ出力を有効化してコンパイラをビルドする まず手元のRustコンパイラのソースから、デバッグ出力を有効化したコンパイラを作成する必要がある…

Rustの基本型のメソッドはどこで定義されているか

概要: Rustの基本型そのものはコンパイラで特別に定義されている。では型に関連づけられたメソッドはどこにあるのか。 固有メソッドのありか 固有メソッドは core の各所で定義されている。例えば i32 の固有実装は core::numに定義されている。 #[lang = "i…

RustマクロでFizz Buzz

RustのマクロでFizz Buzzを書いてみた。 gist.github.com このFizz Buzzは、ループと倍数判定をマクロで処理している。10進数として表示する部分はマクロではなくRust本体に任せていりう。 動作の説明 Rustの macro_rules! でメタプログラミングを行う場合、…

Typed Arenaとdropck

概要: Typed Arenaはdropckの目的を説明するよい例になっている。 Typed ArenaとDrop 以前 Typed Arenaの紹介記事 を書いたが、このソースコードに以下のように Drop の実装を足してみる。 extern crate typed_arena; use std::cell::RefCell; use typed_are…

RustのNULLポインタ最適化

概要: RustのNULLポインタ最適化について説明する。 NULLポインタ最適化とは 以下のようにポインタ型のOptionが、元のポインタと同じサイズになる最適化である。NULLをNoneに割り当てている。 fn main() { println!("{}", std::mem::size_of::<Box<u32>>()); // 8 pri</box<u32>…

末尾再帰をループにできないRustプログラムの例

概要: 生存期間の関係で、ループでは書けないが末尾再帰では書けるアルゴリズムの例を挙げる。 単方向リンクリスト 次のような単純なリンクリストを考える。 struct List<T> { root: Option<Box<Node<T>>>, } struct Node<T> { value: T, next: Option<Box<Node<T>>>, } backの実装 単方向</box<node<t></t></box<node<t></t>…

Rustのshebang comment

shebangはRustではコメントとみなされる。 #!/bin/cat fn main() { println!("Hello, world!"); } shebangとみなされる条件は、 ファイル(字句解析の処理単位)の先頭である。 #! で始まっている。 #![ で始まっていない。 である。LF \n またはファイルの末…

君のRustは20倍遅い

Rustはデフォルトでは本来の力を発揮しない。試しに手頃なベンチマークを3個くらい試したらだいたい20~100倍程度遅かった。 「Rustで ○○ を高速にする方法」が知りたい人は、まず、Rustコンパイラが本来の力を発揮しているか確認したほうがよい。 Cargoの場…

Rust構文解析器のトークン分割戦略

他の多くの言語と同様、Rustの字句解析器は貪欲にトークンを分割する。しかし構文解析の途中で必要に迫られて、さらに細かくトークンを分割する場合がある。 先にまとめ 以下の場合は、構文解析のタイミングで字句がさらに細かく分割される。 式の位置に、前…

Rustでグラフを表現するにはTyped Arenaが便利

概要: Rustでグラフのように相互参照を含むデータ構造を表現するには、Typed Arenaという方法が適している。これについて説明する 整数による表現 グラフの表現方法で、最も簡単なのは、ノードを整数で表し、グラフのデータを別に持つ方法である。 fn main()…

Rustの基本型の名前解決

Rustの基本型の名前はキーワードではない。したがって基本型の名前は名前解決と同時に処理されることになる。ところがややこしい点として、基本型と同名のモジュールに解決される場合もある。この挙動について調べた。 基本型の名前は PrimitiveTypeTable::n…

Rustのモジュールの復習

以前名前解決についてまとめたが、やはり調べ損ねている部分があるので、もう一度まとめてみた。 DefとModuleとNameBindingKind DefId はRust中に出現する定義(enum, enum のバリアント、 fn, let, macro_rules! foo など)を指している。これはcrateのID + c…

Rustの文でセミコロンを省略してよい条件

Rustの文でセミコロンを省略してよい条件を説明する。 意味論的な原則 Rustのセミコロンは意味と構文からそれぞれ説明できる。意味論的には、以下の原則を覚えておけば十分である。 セミコロンで終端された文は強制的に () 型となる。 ブロックの途中の文は …

Rust トレイトオブジェクトの生存期間境界の既定値

概要: トレイトオブジェクト型には生存期間を指定できるが、省略した場合既定値が適用される。この既定値を決定する規則について説明する。 トレイトオブジェクトの生存期間境界 トレイトオブジェクトは正確には以下の構文を持つ。 // obligation 1: only Se…

トレイトオブジェクトのメソッド解決

Rustのこのissueで知ったが、トレイトオブジェクトのメソッド解決はやや特殊らしい。 特殊といっても特に難しいことはない。トレイトオブジェクトは、元となったトレイトを実装するが、それだけではなく、元のトレイトのメソッドを、固有メソッドとしても解…

Rustで使わない変数名をつけるのとアンダースコアの微妙な違い

変数はブロックの末尾まで生存するが、アンダースコアは変数ではないためその文の中でのみ生存する。どうせ使わないからほぼ同じだが、Dropの順番が異なる。 struct A(&'static str); impl Drop for A { fn drop(&mut self) { println!("dropped: {}", self.…

Rustのstd::mem::forgetがunsafeでない理由

Rustのstd::mem::forgetはunsafeではない。このことはAPIドキュメントに説明されている。Rustのオブジェクトは二重dropしてはいけないが、dropをせずに放置する分には(少なくともメモリ保護の観点からは)安全であり、 Drop を実装する側は drop が呼ばれなく…

Rustのasm!マクロが生成するもの

Rustのasm! はインラインアセンブリを挿入するための組み込みマクロである。さて、このマクロは何を生成しているのか。 実は、RustのASTには、対応する具象構文を持たない特殊な抽象構文 InlineAsm が定義されている。 pub enum ExprKind { Box(P<Expr>), ... /// </expr>…