たまにコードを書くと書き方をよく忘れてるのでメモ。Rust v1.11~1.21-nightly
記述のソースとなるリンクも大量に貼る。標準ライブラリの覚え書きも。
必須ドキュメント
- Standard Library API Reference - 標準ライブラリ・リファレンス
-
The Rust Reference - 言語リファレンス
- The Rust Reference (Beta) - beta.
-
The Rust Programming Language - 入門書。日本語翻訳プロジェクト
-
1st Edition
- Effective Rust
- Syntax Index - シンタックス索引。記号からも機能を検索できて便利。
- 2nd Edition
- Old version
-
1st Edition
-
The Rustonomicon - How to use
unsafe
.- Rust 裏本 - Rustonomicon 日本語訳(翻訳中)
- Grammar - Rust の言語仕様。ただし不完全
- Rust by Example - 用例集
- Rust Compiler Error Index - コンパイラ・エラー一覧
- Frequently Asked Questions / よくある質問 - FAQ
その他リンク
-
rust-lang/rust - GitHub のレポジトリ
- RELEASES.md - リリースノート
- Rust Playground - 小さなコードをテストできる
-
Cargo: packages for Rust - 各種パッケージが登録されている
-
The Manifest Format -
Cargo.toml
の詳細フォーマット
-
The Manifest Format -
- The Unstable Book - "feature flag" 一覧
- The Rust Programming Language Blog - 公式開発ブログ
-
Style Guidelines - 古いスタイルガイド
- Naming conventions - 命名規約
- rust-lang-nursery/fmt-rfcs - Rust code formatting RFCs (策定中)
参考リンク
- Elegant Library APIs in Rust - Pascal’s Scribbles - APIの書き方についての、ためになる指針
- 簡潔なQ - ドキュメント化されてない Rust 言語/コンパイラについての深い挙動についての調査が色々と。
ツール
Cargo: packages for Rust
色々行う。
- プロジェクトを作成/build/run/test/...
- パッケージを管理
$ cargo new hello_world --bin
$ tree hello_world/
hello_world/
├── Cargo.toml
└── src
└── main.rs
1 directory, 2 files
- 実行形式のプロジェクトを作成
-
--bin
を抜くと、ライブラリ・プロジェクトを作成 -
Cargo.toml
ファイルにプロジェクトの詳細が記述される
-
$ cd hello_world
$ cargo run
- デフォルトでは実行ファイルのバイナリサイズが大きくなるが、ダイナミックリンク化で小さく出来る
rustup
rustup はコンパイラ等のバージョンマネージャー。
サイトトップのコマンド curl https://sh.rustup.rs -sSf | sh
でインストール。
-
$ rustup update
- ツールチェインをアップデート -
$ rustup show
- インストールされているツールチェイン一覧を表示 -
$ rustup install stable
- stable チャンネルをインストール $ rustup install beta
$ rustup install nightly
-
$ rustup install nightly-YYYY-MM-DD
- 指定日の nightly をインストール -
$ rustup default stable
- stable チャンネルをデフォルトに $ rustup default beta
$ rustup default nightly
-
$ rustup default nightly-YYYY-MM-DD
- 指定日の nightly をデフォルトに
racer
racer は IDE での編集支援ソフト。
-
$ cargo install racer
- インストール
Atom.io での使い方は
- Atom で racer パッケージをインストール
-
rust-src
コンポーネントを$ rustup component add rust-src
でインストール - Atom で racer パッケージの Setting
-
Path to the Racer executable
には、ターミナルで$ echo $HOME/.cargo/bin/racer
した結果を、 -
Path to the Rust source code directory
には、ターミナルで$ echo $(rustc --print sysroot)/lib/rustlib/src/rust/src
した結果を入力
斯くして Atom 上の .rs ソースファイルで入力補完が効くようになる。
rustfmt
cargo install rustfmt-nightly
キーワード Keywords
用途による大まかな分類。ただし複数の用途を与えられているキーワードもある。
- キャスト -
as
- 関数・変数・定数定義 -
fn
,let
,const
,static
- 制御構造
- 分岐 -
if
,else
,match
- ループ -
for
,in
,loop
,while
- 脱出・続行 -
break
,continue
,return
- 分岐 -
- モジュール -
crate
,extern
,use
,mod
- パス -
self
,super
- パス -
- カスタム型 -
struct
,enum
,union
- トレイト・型 -
trait
,type
,where
- 実装 -
impl
,for
- 型エイリアス -
Self
- 引数 -
self
- 型エイリアス -
- 可視性限定子 -
pub
,in
- 危険マーカー -
unsafe
- 定数 -
true
,false
- クロージャ -
move
- パターン -
mut
,ref
-
feature(box_patterns)
,feature(box_syntax)
-box
-
feature(catch_expr)
-do
,catch
-
feature(specialization)
-default
なお、default
, 'static
, union
, catch
は特定の文脈においてのみ特別な意味を持つ、弱いキーワード (weak keywords) となっている。
参考リンク:Rustにおけるキーワードの役割一覧 - 簡潔なQ
予約キーワード
-
become
- 末尾再帰最適化用。See Issue #271, Rustは末尾呼び出し最適化を行いますか? -
macro
- RFC 1584 macro 2.0 -
abstract
,final
,override
- RFC 0342-keywords -
unsized
- RFC 0490-dst-syntax -
alignof
,offsetof
,sizeof
,typeof
- Issue #1144 -
virtual
- RFC 0341-remove-virtual-structs.md -
proc
- RFC 0114-closures -
priv
- RFC 0026-remove-priv -
pure
,yield
コメント Comments
- 以下4種のコメントがある。
-
//
- お馴染みCスタイルの行コメント -
/*
~*/
- ブロックコメント-
/* This is /* commented */ out. */
- ネスト可能
-
-
///
,/**
~*/
- ドキュメント用のコメント。- 属性
#[doc="..."]
と等価。すなわち、続くアイテムを修飾する。
- 属性
-
//!
,/*!
~*/
- ドキュメント用のコメント。- 属性
#![doc="..."]
と等価。すなわち、囲むアイテムを修飾する。普通はモジュールを説明。
- 属性
-
- コメントアウトされた部分は、意味的に空白と等価に扱われる。
- 以下のマクロ呼び出し結果は、
a+b+c+a+b+c+
- 以下のマクロ呼び出し結果は、
macro_rules! test (($($x:tt)*) => {$( print!("{}+", stringify!($x)); )*}; );
test!{a b c}
test!{a/* */b//
c}
文 Statements
- Rust は式言語なので、文は少ない。
- 文は値を返すような対象ではない。
- よって、例えば次の
let
文/fn
アイテムを囲むブロックの値は、文の存在が無視され単にユニット()
となる。
- よって、例えば次の
let a: () = { let a = 100; };
let b: () = { fn test_fun() -> i32 { 1 } };
let
文 let
statements
- キーワード
let
を使って変数(識別子)を値に束縛する。-
let pat ( : type )? ( = init )?;
の形式- 型指定 type が無くても、初期値 init や後々代入される値に基づいて、自動的に型推論される。
- 型推論不可能な場合、エラーが出る。
-
let
文で初期値を与えなくても、後で値を与えれば問題ない。- 未初期化変数を使用しようとした場合、エラーが出る。[#E0381]
-
unsafe
なstd::mem::uninitialized
関数を用いて、初期化に関するコストを避けることが出来るが危険。
- 型指定 type が無くても、初期値 init や後々代入される値に基づいて、自動的に型推論される。
- 既存の変数の再定義が可能。
- C言語のような変数束縛の並列 (
int a=1,b=2;
のようなの) はできない。-
let a = 1, b = 2;
は不可。 -
let (a, b) = (1, 2);
のようにタプルを使った同時束縛は可能。
-
-
let a = 2;
let b: f32 = 10.0;
let c;
let d: u8;
let a = a as f32 + 0.1;
let (a, b) = (a * b, (a + b).to_string());
c = 'c';
d = 10;
println!("{} {} {} {}", a, b, c, d); // 21 12.1 c 10
let e: char;
// println!("{}", e); <- error[E0381]: use of possibly uninitialized variable: `e`
// ^ use of possibly uninitialized `e`
-
let mut hoge = ...;
のように書くとlet mut
で一塊のように見えるが、実際はmut
はパターンの一部なので、mut hoge
で一塊となっている。- よって次のようにも書ける。
let (mut a, ref b) = (10i32, 20i32);
a *= 2;
assert_eq!(a, *b);
- ワイルドカードパターン
_
を値に束縛すると、その値は即捨てられる(リソース解放)。- 以下のサンプルでは、まず
_
に与えられた値が順にDrop
し、スコープを抜けた後、残った変数が作られた順序とは逆にデストラクタが呼ばれる。- ライフタイムが
_a
$\supsetneq$_b
$\supsetneq$_c
$\supsetneq$_d
であるため。
- ライフタイムが
- トレイトの実装においてメソッドの引数が不要なとき、ダミーの引数として
_
を使ったりする。fn foo(_: T) -> bool { false }
- 以下のサンプルでは、まず
- また識別子名をアンダーライン
_
から始めると、unused_variables
の警告が出ない。
use std::fmt::Display;
use std::ops::Drop;
struct Foo<T: Display>(T);
impl<T: Display> Drop for Foo<T> {
fn drop(&mut self) {
println!("Drop {}", self.0);
}
}
{
let _ = Foo("-3");
let _a = Foo(-2);
let _b = Foo(-1);
let _ = Foo('0');
let _c = Foo('1');
let _d = Foo('2');
let _ = Foo(3.0);
println!("-- End of Scope --");
}
Stdout is
Drop -3
Drop 0
Drop 3
-- End of Scope --
Drop 2
Drop 1
Drop -1
Drop -2
- 型が不明な変数について、
let a: () =
を使えば簡単に型を確認することができる。- コンパイラーがエラー文として正しい型を教えてくれる。
- 次の例では、型が
std::result::Result<i32, std::num::ParseIntError>
であると解る。
use std::str::FromStr;
let a: () = i32::from_str("100");
// ^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
// = note: expected type `()`
// found type `std::result::Result<i32, std::num::ParseIntError>`
- コンパイラーは賢いので、特に指示する必要の無いジェネリック型のパラメータの一部をワイルドカード
_
にしても、よろしく取りはからってくれる。
let a = [1, 2, 3];
let doubled: Vec<_> = a.iter().map(|&x| x * 2).collect();
// == let doubled = a.iter().map(|&x| x * 2).collect::<Vec<_>>();
assert_eq!(vec![2, 4, 6], doubled);
アイテム Items
- アイテムとは、ソースコードにおいてアルゴリズム以外の部分、型や外部要素の利用等の宣言を行う文。
- 大ざっぱに
キーワード 識別子 { 本体部分 }
の形式が多い。 -fn
,trait
,impl
,mod
, ...- この場合、末尾にセミコロンは不要。
- 波括弧
{}
の代わりに、丸括弧()
が可能な場合もある -struct
,macro_rules!
, ...-
キーワード 識別子 ( 本体部分 );
の形式。 - この場合、セミコロン
;
が必要。
-
- 波括弧
{}
ブロックが無い、場合もある -const
,use
,extern crate
, ...-
キーワード 識別子+ 本体部分 ;
の形式 - この場合も、セミコロン
;
が必要。
-
const
アイテム
- キーワード
const
を用いて定数を定義する。 -
const
定数名は、全部大文字であることが望ましい。- これを破ると警告が出る。
#[warn(non_upper_case_globals)]
- これを破ると警告が出る。
-
let
式と違い、定数の型の明示が必要。 -
const
定数は場合により適宜プログラムにインライン展開されるので、定まったアドレスを持たないことに注意。 -
const mut
変数を定義することは不可能。
static
アイテム
- キーワード
static
を用いてグローバル変数を定義する。 -
static
変数名は、全部大文字であることが望ましい。- これを破ると警告が出る。
#[warn(non_upper_case_globals)]
- これを破ると警告が出る。
-
let
式と違い、またconst
定数と同様に、定数の型の明示が必要。 -
static
変数は定まったアドレスを持つことに注意。- 故に参照は
'static
ライフタイムを持つ。
- 故に参照は
-
static mut
変数を定義することも可能- 他のスレッドから書き換えられたりする危険性があるので、使用には
unsafe
が必要
- 他のスレッドから書き換えられたりする危険性があるので、使用には
static STATIC_F: f32 = 0.0;
static STATIC_F_REF: &'static f32 = &STATIC_F;
static STATIC_TEXT: &'static str = "a static str";
static mut STATIC_MUT_F: f32 = 0.0;
static STATIC_MUT_F_REF: &'static f32 = unsafe { &STATIC_MUT_F };
unsafe {
println!("{}", STATIC_MUT_F);
println!("{}", *STATIC_MUT_F_REF);
STATIC_MUT_F += 1.0;
println!("{}", STATIC_MUT_F);
println!("{}", *STATIC_MUT_F_REF);
}
- ジェネリックな関数内の
static
変数は、型パラメータを変えてもその実態が同一のものであるので注意。
fn foo<T>() -> *mut i32{
static mut FOO: i32 = 0;
unsafe { println!("{}", FOO); FOO += 1; &mut FOO }
}
assert_eq!(foo::<i8>(), foo::<u8>());
assert_eq!(foo::<i8>(), foo::<f32>());
assert_eq!(foo::<i8>(), foo::<char>());
assert_eq!(foo::<i8>(), foo::<&str>());
fn
アイテム(関数定義文)
- 関数定義はキーワード
fn
を用いて行う - 関数本体の最後の式が戻り値となる。
- キーワード
return
を用いて、本体の記述の途中で戻り値を与えることも出来る。 -
return
が使われず、本体部分の末にセミコロン;
があると、戻り値はユニット()
となる。
- キーワード
- 関数の本体の中で独自の関数を定義することも出来る(関数内関数)。
View アイテム
参照:クレートとモジュール
- View(眺め、視界)すなわち、そのソースコードにおいて見えているもの・知っているものを左右する(i.e. 外部ソースで宣言されている関数等を既知とするための)アイテム。以下の2つ。
-
extern crate
アイテム - 外部クレートのインポートを宣言する。 -
use
アイテム - モジュール/モジュール以下の要素(関数・型・トレイト・定数など)のインポートを宣言する。- この「インポートを宣言」とは、ソースコードの宣言以降の部分において、宣言対象が既知となる(未知では無い=名前解決できる)ことを意味する。
-
- 両アイテム共に、キーワード
as
を用いて宣言対象に別名を与えることが出来る。
extern crate
アイテム
use
アイテム
-
mod
アイテムには以下の 2 つの機能がある。- ローカルスコープに名前をインポートする。
-
use path::name;
の形式。
-
- ローカルスコープで新しい名前にバインドする。
-
use path::old_name as new_name;
の形式。
-
- ローカルスコープに名前をインポートする。
-
use module_a::module_b::AType::{Self, VariantA, VariantB};
のように複数の名前を同時にインポートできる。- ここの
Self
はuse module_a::module_b::AType;
と等価。
- ここの
- 参考リンク:Rustで use std; が必要なときとエラーになるときがあるのは何故か - 簡潔なQ
mod
アイテム
-
mod
アイテムには以下の 2 つの種類がある。-
特別なファイル (
mod.rs
等) において、モジュールの使用を宣言する。-
mod mod_name;
の形式。
-
-
mod ブロック
- ファイル分割無しに、新規モジュールを作成できる。-
mod new_mod_name { items }
の形式。
-
- 以上2つ共に、
pub
修飾子が頭に付くこともある。
-
特別なファイル (
その他アイテム
-
extern
アイテム -
global_asm!
アイテム -
type
アイテム - カスタム型宣言アイテム
-
enum
アイテム -
struct
アイテム -
union
アイテム
-
-
trait
アイテム -
impl
アイテム - マクロ発動アイテム
- マクロ定義アイテム
式 Expressions
繰り返し(ループ)式 Loops
-
while
,loop
,for
等々のループ式があるが、いずれも全てユニット型1()
のみを返す。
-
loop
ループ - Ref/Grm. 無限ループ-
loop { body }
の形式 -
body ブロックにおいて
break
でループを脱出しないと、無限ループとなる。
-
-
while
ループ - Ref/Grm. 条件ループ-
while expr { body }
の形式 - 条件式部分で波括弧
{
,}
で括り式を並列させることで、少なくとも1回は body ブロックを実行する、いわゆるdo-while
ループを再現できる。-
while { body; expr } {}
の形式- ダミーの本体ブロックとして、尻に
{}
が付くことに注意。
- ダミーの本体ブロックとして、尻に
-
-
-
for
ループ - Ref/Grm. イテレータ(反復子)ループ-
for val in expr { body }
の形式 - トレイト
std::iter::IntoIterator
を実装してイテレータへと変換可能な式 expr の要素を走査する
-
// while
let mut i = 0;
while i < 10 { i += 1; }
assert_eq!(i, 10);
// like do-while
while {
i += 1;
i < 10
} {}
assert_eq!(i, 11);
// for
for c in vec!('H','e','l','l','o','!') {
println!("{}", c);
}
// loop
let a = loop { break; };
let b = loop { break 10; };
assert_eq!(a, ());
assert_eq!(b, 10);
ループ・ラベル Loop labels
- シングルクォート
'
とコロン:
を用いて、各種ループ式にラベルを付けられる。-
'label_name: keyword
の形式- ここで label_name はラベルの名前となる識別子、keyword は
loop
,while
,for
のいずれかのキーワード。
- ここで label_name はラベルの名前となる識別子、keyword は
-
- キーワード
break
/キーワードcontinue
を用いて、ループの脱出/続行を行う。-
break;
/continue;
- この式を囲む最内ループから脱出/続行する。 -
break expr;
-break
式に値を与えることで、この式を囲む最内ループの戻り値を、ユニット()
から expr へと変更できる。 -
break
式、continue
式にラベル名を与えることで、ラベル名で指名したループからの脱出/続行が行える。break 'foo;
-
break 'foo value;
-break
で値を返すときは、ライフタイム-値の順で書く。 continue 'bar;
-
let mut i = 0;
let value = 'loop1: loop {
'loop2: for j in 0..10 {
let mut ks = 0..j+1;
'loop3: while let Some(k) = ks.next() {
if j % 2 == 0 { continue 'loop2; }
if k % 2 == 0 { continue 'loop3; }
println!("{} {} {}", i, j, k);
i += 1;
if i > 10 { break 'loop1 i + j + k; }
}
}
};
println!("value: {}", value);
マッチ式 match
expressions
-
match
キーワードを用いて、パターンマッチを行う。 -
match expr { pattern-list }
の形式 - 各マッチ節が返す値の型は、全て一致しなくてはならない。
match x { 10 => "Ten", _ => 0, }; // Error: note: match arm with an incompatible type
パターン Patterns
- パターンは
match
式だけでなくlet
文でも使われる重要な要素である。 - 次の
match
式で具体例を参照せよ。
for x in 0..14 {
let text = match x {
0 => "zero",
1 | 2 => "one, two",
3 ... 5 => "さん, よん, ご",
x @ 6 ... 7 => match x { 6 => "六", 7 => "七", _ => unreachable!() },
x @ 8 ... 9 | x @ 10 ... 11 if x%2 == 0 => "捌, 拾",
8 ... 11 => "nine, eleven",
_ => ">=12",
};
println!("{}: {}", x, text);
}
- 浮動小数点数をパターンで使うと、警告が出るので注意。
-
|
で並列 -
...
で範囲。- レンジ式の
..
と違い、first ... end
においては最終要素 end も含まれる (inclusive) なので注意 - (少なくとも現在では)特定のプリミティブ(数値型や文字型)に対してしか使えないようなので注意。
- レンジ式の
-
@
で変数に束縛-
|
と同時に使うときは、同じ変数への束縛を再明示
-
-
if
でガード-
pat1 | pat2 if gud
は、pat1 | (pat2 if gud)
ではなく(pat1 | pat2) if gud
的な意味なので注意- i.e.
|
はif
より優先度が高い
- i.e.
-
- マッチングは頭から行われる
-
_
はワイルドカード
if let
式 if let
expressions
- 式とパターンマッチを試して、変数束縛を行う。
-
if let pat = expr { body } else_tail ?
の形式- パターンは等号
=
の左辺(右辺には置けない)。
- パターンは等号
- 常に成立する(マッチに失敗しない)パターンについては、
if let
式を組むとエラーが出る。[#E0162]- この場合
let
文を使えということだ。
- この場合
let val = ("tagA", "a body", "tagB");
if let ("tagA", body, _) = val {
println!("tagA's body: {}", body);
} else {
println!("unmatch");
}
if let (_, body, "tagB") = val {
println!("tagB's body: {}", body);
}
while let
式 while let
expressions
- ループ式の一種。
-
while
ループにおいて、再帰の度に行われる条件式で変数束縛を行う。- 毎回
if let
を行い、if let
に失敗したら再帰終了的な。
- 毎回
let mut x = vec![1, 2, 3];
while let Some(y) = x.pop() {
println!("y = {}", y);
}
// y = 3, y = 2, y = 1
レンジ式 Range expressions
-
..
演算子を用いて、範囲を表す構造体を簡単に記述するための記法。-
演算子の優先順位の表を見て分かるとおり、
..
演算子の優先度は低い。- そのため
1+2*3 .. 10i32.pow(2)-10
のように式を組み合わせたレンジ式において、(1+2*3) .. (10i32.pow(2)-10)
のような括弧は不要。
- そのため
-
演算子の優先順位の表を見て分かるとおり、
- レンジ式は
0 .. 0
や0 .. -10
のようなstart ≥ end
のような式も受け付けるが、中身は空。assert_eq!(0, (0 .. -10).len());
-
std::collections::range
モジュールにおいて、より発展的なRangeArgument
トレイトが与えられているが、まだ実験的 (collections_range
#30877)
4種のレンジ式
-
start..end
- 構造体std::ops::Range
を表す。- 半開区間 [start, end)
- 最終要素 end は含まれない (exclusive) に注意
-
start..
- 構造体std::ops::RangeFrom
を表す。- 区間 [start, +∞)
-
..end
- 構造体std::ops::RangeTo
を表す。- 区間 (-∞, end)
-
..
- 構造体std::ops::RangeFull
を表す。- 全区間 (-∞, +∞)
#![feature(range_contains)]
#![allow(unused_variables)]
use std::ops::*;
let a : Range<i32> = 1..10;
let b : RangeFrom<i32> = 1..;
let c : RangeTo<i32> = ..10;
let d : RangeFull = ..;
assert_eq!(a, Range{ start: 1, end: 10 });
assert_eq!(b, RangeFrom{ start: 1 });
assert_eq!(c, RangeTo{ end: 10 });
assert_eq!(d, RangeFull);
assert!(!a.contains(0) && a.contains(1) && !a.contains(9) && !a.contains(10));
assert!(!b.contains(0) && b.contains(1) && b.contains(9) && b.contains(10));
assert!( c.contains(0) && c.contains(1) && !c.contains(9) && !c.contains(10));
// assert!( d.contains(0) && d.contains(1) && d.contains(9) && d.contains(10));
// RangeFull は contains() メソッドを持たなかった。
演算子式 Operator expressions
殆どの演算子はオーバーロード可能。
各種演算子は以下のモジュールで扱われる。
-
-
単項算術・論理演算子 -
-
,!
- 二項算術・論理演算子
-
複合代入演算子
- 算術代入演算子 -
+=
,-=
,*=
,/=
,%=
- ビット代入演算子 -
|=
,&=
,^=
,<<=
,>>=
- 算術代入演算子 -
- 特殊演算子 -
*
,[]
- 関数呼び出し演算子 -
()
- その他 -
?
,<-
-
単項算術・論理演算子 -
-
-
比較演算子 -
==
,!=
,<
,>
,<=
,>=
-
比較演算子 -
-
不明
-
遅延ブール演算子 -
&&
,||
-
遅延ブール演算子 -
-
?
はトライ演算子。? 演算子の節で詳しく述べる。 -
<-
は実験的な placement-in 演算子。#![feature(placement_in_syntax)]
で使える。 -
インクリメント/デクリメント演算子
++
,--
は無い- 何故かというと、(前置|後置)(イン|デ)クリメントの評価順序の複雑性がしばしばバグの温床となるから。See FAQ: Why doesn't Rust have increment and decrement operators?
演算子の優先順位 Operator precedence
- 次の表で、演算子を優先順位降順に並べる。
- 単項演算子は二項演算子より優先されることが分かる。
- より正確には、後置単項演算子 > 前置単項演算子 > 二項演算子 の順。
- 「非結合」演算子では、その結合を丸括弧
(
,)
で明示しないと文法エラーとなる。-
a == b == c
- Error -
(a == b) == c
- OK
-
- 単項演算子は二項演算子より優先されることが分かる。
- フィールド演算子
.
は?
より強い。よって&self.hoge?
は&((self.hoge)?)
となる。
演算子 | 結合性 |
---|---|
? |
|
単項 - * ! & &mut
|
|
as :
|
左結合 |
* / %
|
左結合 |
+ -
|
左結合 |
<< >>
|
左結合 |
& |
左結合 |
^ |
左結合 |
| |
左結合 |
== != < > <= >=
|
非結合 |
&& |
左結合 |
|| |
左結合 |
.. ...
|
非結合 |
<- |
右結合 |
= += -= *= /= %= &= |= ^= <<= >>=
|
右結合 |
?
演算子 The ?
operator
v1.13より導入された、エラー処理用の演算子。try!
マクロの代わり。
?
演算子オーバーロード用にstd::ops::Try
トレイトは用意されているが、あくまで実験段階。
std::result::Result<T, E>
型の変数に対して後置で作用し、変数の値が
-
Ok(x)
なら、unwrap
して x を継続に渡す -
Err(e)
なら、(関数またはクロージャーの戻り値の型に合わせて)Err(From::from(e))
をreturn
する。
fn try_to_parse() -> Result<i32, ParseIntError> {
let x: i32 = "123".parse()?; // x = 123
let y: i32 = "24a".parse()?; // returns an Err() immediately
Ok(x + y) // Doesn't run.
}
let res = try_to_parse();
println!("{:?}", res);
v1.22より、?
演算子がOption<T>
型に対しても作用するようになった。
すなわち Option
が Try
トレイトを実装した。
-
Some(x)
なら、x を継続に渡す。 -
None
なら、None
をreturn
する。
fn option_try_test(opt: Option<i32>) -> Option<i32> {
let x = opt?;
Some(x * 2)
}
assert_eq!(option_try_test(Some(10)), Some(20));
assert_eq!(option_try_test(None), None);
try_trait
#42327により、Option<T>
型に対する?
演算子でResult<T,NoneError>
型の返り値にも対応する。
-
Some(x)
なら、x を継続に渡す。 -
None
なら、ユニットライク構造体std::option::NoneError
をErr
で包んでreturn
する。
#![feature(try_trait)]
use std::option::NoneError;
fn option_try_test2(opt: Option<i32>) -> Result<i32, NoneError> {
let x = opt?;
Ok(x * 2)
}
assert_eq!(option_try_test2(Some(10)), Ok(20));
assert_eq!(option_try_test2(None), Err(NoneError));
box
式
[Experimental] (box_syntax
#27779)
- キーワード
box
を用いて、与えられた式をヒープへと直接書き込むbox
式を記述する。-
box expr
の形式 - 巨大な値を効率よく扱うために用いる。
-
- 例えば次の文は容易く
error: stack overflow
を引き起こす。(参考: Rustで巨大なヒープメモリを取る)
let buf = Box::new([0; 1024*1024*4]);
- しかし次のように box 式を用いると、式
[0; 1024*1024*4]
の(評価)結果はスタックを経由せず、ヒープへと直接書き込まれるため、スタック・オーバーフローを回避できる。- そしてこの box 式は、型
Box<[i32; 4194304]>
の値を返す。
- そしてこの box 式は、型
#![feature(box_syntax)]
let buf = box [0; 1024*1024*4];
プリミティブ型 Primitive Types
Ref/1st/std
以下のプリミティブ型が用意されている。
-
u8
,i8
,u16
,i16
,u32
,i32
,u64
,i64
,usize
,isize
- 語るまでも無い整数値型。 -
f32
,f64
- 浮動小数点数。様々な初等算術メソッドが用意されている。 -
bool
- 真偽値 -
char
- 文字型 -
array
- スタック上の配列 -
str
- 文字列 -
slice
- スライス -
tuple
- タプル、直積型 -
pointer
- ポインタ
let a_int: i32 = 10;
let a_num: f32 = 1.0;
let a_boolean: bool = true; // or false
let a_char: char = '❤';
let an_array: [i32; 3] = [1u32, 2, 3];
let a_str: &a_str = "text";
let a_slice: &[i32] = &an_array[0..2];
let a_tuple: (&'static str, i32, char) = ("hello", 5, 'c');
let a_ptr: *const i32 = &10i32;
タプル Tuple types
- タプルの要素には、
let
文やif let
式、ドット記法でアクセスできる。- ドット記法の添字は 0 から始まる。
- 1要素タプルには曖昧さ排除のため、型名・リテラル共にカンマ
,
が必要。let one_element_tuple: (i32,) = (10,);
let a_tuple: (bool, i32, f32) = (true, 10, 0.0);
let (head, body, _) = a_tuple;
assert_eq!(head, true);
assert_eq!(body, 10);
if let (true, _, last) = a_tuple {
assert_eq!(last, 0.0);
}
assert_eq!(a_tuple.0, true);
assert_eq!(a_tuple.1, 10);
assert_eq!(a_tuple.2, 0.0);
ユニット型 the unit type
- 0要素タプル型
()
は特別に「ユニット型」と呼ばれる。 - ユニット型の値はユニット
()
ただ一つ。 - C言語の
void
的な、または空集合。 - セミコロン
;
で終わる式文や文はユニットを返す。 - 型のサイズは 0.
assert_eq!(size_of::<()>(), 0);
アレイとスライス Array, and Slice types
アレイ / Array / std.primitive.array
スタック上の固定長配列。[T; N]
が配列の型。
アレイ式 Array expressions
[1, 2, 3, 4];
["a", "b", "c", "d"];
[0; 128];
スライス型 / Slice types / std.primitive.slice
- 隣接 (contiguous) するデータ型への動的サイズの参照(ビュー)。
- 動的なので型に長さ情報を持たない。
-
&[T]
がスライスの型。
スライシング構文 Slicing syntax
// A stack-allocated array
let array: [i32; 3] = [1, 2, 3];
// A heap-allocated array
let vector: Vec<i32> = vec![1, 2, 3];
// A slice into an array
let slice: &[i32] = &vector[..];
ベクタ Vectors
-
std::vec::Vec
はヒープ上に確保された配列を扱うジェネリック構造体。- プリミティブ型ではない。
- 生成にはもっぱらマクロ
vec!
を使う。 - 参考ソース libcollections/macros.rs
let v1: Vec<i32> = vec![1, 2, 3, 4]; // 要素を列挙
let v2 = vec![0; 5]; // vec![初期値; 個数]
assert_eq!(v2, vec![0,0,0,0,0]);
let mut v: Vec<i32> = Vec::new();
v.push(1); v.push(2); v.push(3); v.push(4);
assert_eq!(v, v1);
文字列 Strings
-
&str
- 文字列のスライス型。-
&'static str
- リテラルにより生成されるプリミティブ型。
-
-
String
- UTF-8 エンコードされた文字列。文字列操作するならもっぱらこれを用いる。
文字列リテラル String literals
let a: &'static str = "foobar";
let b = "foo\
bar";
assert_eq!(a,b);
Raw 文字列リテラル Raw string literals
-
r
に続く n (>=0) 個の#
に続く"
で始まり、"
に続く n 個の#
で終わる範囲を文字列とする Raw 文字列リテラル。- Raw 文字列リテラルの中にはエスケープが存在しないので、
r#"
〜"#
の中に文字列"\"#"
を入れることは出来ない。- 代わりに
r##"
…"##
を使おう。
- 代わりに
- Raw 文字列リテラルの中にはエスケープが存在しないので、
let s1 = "text";
let s2 = r"text";
let s3 = r#"text"#;
let s4 = r##"text"##;
assert_eq!(s1, s2);
assert_eq!(s1, s3);
assert_eq!(s1, s4);
バイト文字列リテラル Byte and byte string literals
-
"
で括った文字列/Raw 文字列リテラルにb
の接頭辞で、バイト文字列になる。- 型は
u8
のアレイ:[u8: n]
の'static
参照。 - ASCII コードしか含められない。
- 型は
let s1: &'static [u8; 4] = b"text";
let s2: &'static [u8; 4] = br"text";
let s4: &'static [u8; 4] = br##"text"##;
assert_eq!(s1, s2);
assert_eq!(s1, s4);
カスタム型
- メモリサイズにより以下に分類される。
-
v1.18より、
#[repr]
属性を持たないタプル・列挙型・構造体は、サイズを最小化するため自動的に(内部変数が)再配置される可能性がある。See Pull #40377. -
enum Option<T> { None, Some(T), }
のような列挙型については、None
値をnullポインタで判別し、タグを付け足したりしない最適化が行われる。 - Nullable enum optimizations
構造体 Structs
-
..
でフィールドの一部コピーが簡略化できる。- 構造体パターンにおいて、構造体要素名の省略にも使える。
-
v1.17より、構造体の内部変数名と同一の変数については、コンストラクタにおいて添字を省略できる。See RFC 1682.
- 構造体パターンにおいても同様。
#[derive(PartialEq, Debug)]
struct Point4d {
x: i32, y: i32, z: i32, w: i32,
}
let origin = Point4d { x: 0, y: 0, z: 0, w: 0};
let point1 = Point4d { z: 1, x: 2, .. origin };
let (x, y, z, w) = (point1.x, point1.y, point1.z, point1.w);
let point2 = Point4d { x: x, y: y, z: z, w: w }; // old
let point3 = Point4d { x, y, z, w }; // RFC 1682
assert_eq!(point1, Point4d { x: 2, y: 0, z: 1, w: 0});
assert_eq!(point1, point2);
assert_eq!(point1, point3);
let point4 = Point4d { x: 20, y: 10, z: 11, w: 0};
// 構造体の分解
let Point4d { x: x2, z, ref y, .. } = point4; // ここで `z: z` とする必要は無い。
assert_eq!(x2, 20);
assert_eq!(*y, 10);
assert_eq!(z, 11);
タプル構造体 Tuple structs
- タプルのように、丸括弧
(
,)
で型を囲んで定義する構造体。- 要素に名前を持たない。
- 基本的に1要素タプル構造体しか使われない。
- 1要素タプル構造体は、他の型のラッパーを作るのに便利。See 例
- タプルと同様に、0 から始まる自然数によるドット記法で内部値へアクセスできる。
struct Color(i32, i32, i32);
let c = Color(10, 20, 30);
assert_eq!(c.0, 10);
struct Inches(i32);
let length = Inches(10);
let Inches(integer_length) = length; // let を用いた内部値の取りだし。
println!("length is {} inches", integer_length);
#[derive(PartialEq, Debug)]
struct Point(u32, u32);
let x = Point { 0: 3, 1: 5 };
let y = Point { 1: 5, 0: 3 };
assert_eq!(x, y);
Unit-like 構造体 Unit-like structs
-
ゼロ要素タプル(ユニット型)
()
のように、内部に値を持たない構造体。
struct Electron;
let x = Electron;
列挙型 Enums
- 内部に値を持てるので色々と便利に使える。「タグ付き共用体」
enum Message {
Quit, // Unit-like 構造体変数
ChangeColor(i32, i32, i32), // タプル構造体変数
Move { x: i32, y: i32 }, // 構造体
Write(String), // タプル構造体変数
}
C-like 列挙型 C-like enums
- 内部値を持たない C-like 列挙型は、C言語のように数値型へと変換することが出来る。(See 1st/Jpn)
- その値を明示的に与えることも出来る。
- 数値型から列挙型への変換は一手間必要 (See サンプル)
#![allow(dead_code)]
Number {
Zero, One, Two,
}
#[derive(Debug, Copy, Clone)]
enum Color {
Red = 0xFF0000,
Green = 0x00FF00,
Blue = 0x0000FF,
}
fn main() {
use Number::*;
println!("zero is {}", Zero as i32);
println!("one is {}", One as i32);
use Color::*;
println!("{:?} is 0x{:06X}", Red, Red as i32);
println!("{:?} is 0x{:06X}", Blue, Blue as i32);
let color: Color = unsafe { std::mem::transmute(0x00ff00) };
println!("0x{:06X} is {:?}", color as i32, color);
}
空列挙型
- 空な列挙型は、普通の列挙型とは異なる挙動を示す。
#![feature(never_type)]
#[allow(dead_code)]
enum Empty { }
fn test0(x: Empty) {
match x {
_ => unreachable!(), // warning: unreachable pattern
}
}
fn test1(x: Empty) {
match x {
// no pattern is valid.
}
}
fn test2(x: Option<Empty>) {
match x {
None => println!("Hello"),
Some(_) => unimplemented!(), // warning: unreachable pattern (only if #![feature(never_type)])
}
}
共用体 union
v1.19より、C言語にあるような共用体(タグ無し共用体)がサポートされた。See RFC 1444.
- キーワード
union
を用いて定義 - 変数へのアクセスには
unsafe
が必要- 共用体はどの内部値で定義されたかを覚えていないので、異なる型へのアクセスが危険だから。
- パターンマッチも可能。しかし繰り返すが、共用体は内部型を記憶しないので、
let MyUnion { i } = a;
のようなパターンマッチは常に成功することに留意されたし。
union MyUnion {
f: f32,
i: i32,
x: [u8;4]
}
let a = MyUnion { f: 1.0 };
println!("a.f: {:.3}", unsafe { a.f });
println!("a.i: {}", unsafe { a.i });
println!("a.i: 0x{:08X}", unsafe { a.i });
println!("a.i: 0b{:032b}", unsafe { a.i });
println!("a.x: {:?}", unsafe { a.x });
unsafe { println!("a.x: 0b{:08b}_{:08b}_{:08b}_{:08b}", a.x[3], a.x[2], a.x[1], a.x[0]); };
unsafe {
let MyUnion { f: fval } = a;
let MyUnion { i } = a;
println!("{:0.3} == {}", fval, i);
}
unsafe {
match a {
MyUnion { i: 0...10 } => println!("{}", a.i),
MyUnion { f: fval } if fval == 1.0 => println!("{}", fval),
MyUnion { f } => println!("{}", f),
}
}
言語機能
型キャスト Casting Between Types
型強制 Type coercions
型強制とは(唯一)暗黙的に行われる以下の変換
- mutableな参照(ポインタ)のimmutable化
-
&mut T
->&T
-
*mut T
->*const T
-
- 参照からポインタへの変換
-
&T
->*const T
-
&mut T
->*mut T
- ポインタから参照への変換には、
&*
等が必要(See 参照と生ポインタ)
-
- 参照外し演算子
*
(Deref
) (SeeDeref
による型強制)- 引数やメソッド呼び出しに際して、必要に応じて
*
は幾らでも自動的に挿入される(See Derefとメソッド呼び出し)- 例えば構造体
std::vec::Vec<T>
は、これ自身はiter()
メソッドを実装していないが、Deref<Target=[T]>
を実装しているため、暗黙のDeref
による[T]
への型変換経由で、iter()
メソッドを呼び出すことが出来る。
- 例えば構造体
- この暗黙の Deref を用いて、「クラス継承」を再現できる。
- 引数やメソッド呼び出しに際して、必要に応じて
fn test1(ptr: *const i32) { println!("{}", unsafe { *ptr }); }
fn test2(rf: &i32) { println!("{}", *rf); }
let rf: &i32 = &100;
let ptr: *const i32 = &100; // 暗黙変換
test1(rf);
//test2(ptr); // Error
test2(unsafe { &*ptr });
let v: Vec<i32> = vec!(1, 2, 3, 4);
let mut iter = v.iter(); // called [i32]::iter(& *v)
assert_eq!(iter.next(), &1);
as
(安全キャスト)
- キーワード
as
を用いて安全なキャストを行う。 - 安全なキャストには以下の 3 種がある。
- 明示的型強制 - 型強制で行われる型の変換を、明示的に行う。
- 数値キャスト
- 数値型同士の変換
- C-likeな列挙型を整数型へ
- 真偽値
bool
や文字型char
を整数型へ -
u8
を文字型char
へ
- ポインタキャスト - 生ポインタと整数型の間の相互変換
- Cとは違い、Rust では数値型が暗黙に適当に型変換されることは無いので、例えば
1 + 1.0
のような式はエラーとなる。-
1 as f32 + 1.0
のように明示的に型を揃えるためのas
キャストが必要。 -
演算子の優先順位の表を見て分かるとおり、
as
は二項演算子の中では一番優先順位が高い。- 故に、普通の数値演算において
(1 as f32) + 1.0
のような括弧()
は不要である。
- 故に、普通の数値演算において
-
transmute
- 関数
std::mem::transmute
を用いて、unsafe な型変換が行える。- ビット単位の再解釈を行う。
- そのため、型変換について型サイズの一致は確認する。
- 違反するとエラーが出る。[#E0512]
- そのため、型変換について型サイズの一致は確認する。
-
標準ライブラリのドキュメントに多くの例が挙げられているが、
- ライフタイムの伸張といった、とんでもないことも行える。
- ビット単位の再解釈を行う。
use std::mem::transmute;
unsafe {
println!("{:?}", transmute::<_, [u8; 4]>(1.0f32));
println!("{:?}", transmute::<_, [u8; 8]>(1.0f64));
println!("{:?}", transmute::<_, [u8; 8]>(100i64));
let a: String = "Hello".to_string() + " World!";
println!("{:?}", transmute::<_, &'static str>(a.as_str()));
}
変換関数の例
-
String
->&str
-
String
->Box<str>
- トレイト
From
<String>
- トレイト
-
&str
->String
所有権 Ownership
Rust は所有権システムにより、1つのリソースは1つの変数にしかその所有権が持たれていないことを保証する。
- 「変数」が「リソース(値、変数の中身)」について「所有権」を持っている。
#![allow(unused_variables)]
#[derive(Debug)]
struct Foo(i32);
let a = Foo(100); // `Foo(100)` というリソースの所有権を変数 `a` が持っている。
// ここで1要素タプル構造体 `Foo(i32)` を用いたのは、普通の数値型だと `Copy` を実装しているので、
// 所有権がムーブしないため。
let mut b = a; // 変数 `a` の持つ `100` への所有権が変数 `b` へとムーブされた。
// println!("{:?}", a); // <- 変数 `a` の持つリソースは既にムーブされているので、
// コンパイルエラーが生じる。
let c = b;
b = c; // 変数 `b` はmut変数なので、再度リソースを代入できる。
let d = b.0; // 変数 `b` の要素 `b.0` の型は `i32` で `Copy` を実装しているため、
// 新しい変数 `d, e` へとリソース `100` がコピーされる。
let e = b.0; // 変数 `b` の所有権は失われない。
fn consume_foo(_foo: Foo) {}
consume_foo(b);
// println!("{:?}", b); // 既に関数 `consume_foo`(、正確にはその引数 `_foo`)へと
// 変数 `b` の所有権がムーブされているので、コンパイルエラーが生じる。
- 所有権を持つ変数が失われたとき、リソースは解放される。
- 「変数が失われたとき」とは、処理がリソースを所有している変数の宣言されたスコープを抜ける、変数に所有されないリソースがワイルドカード
_
に代入される、などのとき。 - こうしてリソースが1回だけしか解放されないことが保証される。
- タプルや構造体の要素は個別にムーブ、解放される。
- 「変数が失われたとき」とは、処理がリソースを所有している変数の宣言されたスコープを抜ける、変数に所有されないリソースがワイルドカード
#![allow(unused_variables,non_snake_case)]
#[derive(Debug)]
struct Foo(char);
impl Drop for Foo {
fn drop(&mut self) { println!("Drop {:?}", self); }
}
println!("in #1");
{
let a = Foo('a');
let _ = Foo('_'); // 即値がワイルドカードにマッチされるとき、
// このリソース`Foo('_')`を持つ変数がないので即ドロップ
let b = Foo('b');
let _ = b; // `Foo('b')`は変数`b`が持ったままなので、この行に意味は無い。
println!("in #1.1");
{
let A = Foo('A');
let B = Foo('B');
let C = Foo('C');
// このブロックは、`{ let A = ...; { let B = ...; { let C = ...; }}}`
// と等価なので、`C, B, A` の順にドロップする。
}
println!("in #1.1");
let c = Foo('c');
}
println!("out #1");
println!("in #2");
{
let v012 = (Foo('0'), Foo('1'), Foo('2'));
println!("in #2.1");
{
let (v0, _, v2) = v012;
// `v0`,`v2` に所有された `Foo('0'), Foo('2')` はここでドロップ
}
println!("out #2.1");
// println!("{:?}", v012.0); // `v012.0, v012.2` は既にムーブしているのでアクセスできない
// println!("{:?}", v012.2);
println!("{:?}", v012.1);
// 残りの `v012.1` の所有する `Foo('1')` はここでドロップ
}
println!("out #2");
println!("in #3");
{
struct Bar { a: Foo, b: Foo, cd: (Foo, Foo) };
// `Bar`が`Drop`を実装していないから、以下のようなことが可能
let bar = Bar { a: Foo('a'), b: Foo('b'), cd: (Foo('c'), Foo('d')) };
println!("in #3.1");
{
let Bar { a, b: _, cd: (c, _) } = bar;
// `a`,`c` に所有された `Foo('a'), Foo('c')` はここでドロップ
}
println!("out #3.1");
// println!("{:?}", bar.a); // `bar.a, bar.cd.0` は既にムーブしているのでアクセスできない
// println!("{:?}", bar.cd.0);
println!("{:?}", bar.b);
println!("{:?}", bar.cd.1);
// 残った `bar.b, bar.cd.1` の所有する `Foo('b'), Foo('d')` はここでドロップ
}
println!("out #3");
#[derive(Debug)] struct Foo(char);
#[derive(Debug)] struct Bar(Foo);
impl Drop for Foo { fn drop(&mut self) { println!("Drop {:?}", self); } }
impl Drop for Bar { fn drop(&mut self) { println!("Drop {:?}", self); } }
let foo = Foo('a');
let a = foo.0; // これはコピーだからOK
let bar = Bar(foo);
// let foo = bar.0; // これはムーブになるからNG
// let Bar(foo) = bar; // 構造体パターンによる分解も、要素のムーブになるからNG
-
Box
は言うまでもなくDrop
を実装した型であり、そのためBox<T>
の成分取り出しはT
の場合のようには行かない。- 単純に行うと、1つ成分を取り出すとその時点で
Box
値が consume され、2つ目の成分が取り出せない。(box test 1) - 値を一旦スタック上に移して、それから取り出す手も有る。(box test 2)
- ブロックで囲むことで、box test 2 と同様に。(box test 3)
- 自分自身を返すメソッドを実装する手もある。(box test 4)
- ブロックで囲む box test 3 が一番単純な手だとは思うが、スマートじゃないような……
- 単純に行うと、1つ成分を取り出すとその時点で
// prelude
#![allow(unused_variables)]
struct Foo(i32);
struct Bar(Foo,Foo);
// stack test
let bar = Bar(Foo(0), Foo(1));
let Bar(foo0, foo1) = bar; // OK
// box test 1
let bar = Box::new(Bar(Foo(0), Foo(1)));
// let Bar(foo0, foo1) = *bar; // NG. `bar.0` が `foo0` へとムーブされた時点で `bar` は
// consume され、`bar.1` へとアクセス出来なくなる。
let Bar(foo0, _) = *bar; // 成分を1つかし取り出さない、これは OK
// box test 2
let bar = Box::new(Bar(Foo(0), Foo(1)));
let Bar(foo0, foo1) = { let tmp = *bar; tmp }; // これは OK
// box test 3
let bar = Box::new(Bar(Foo(0), Foo(1)));
let Bar(foo0, foo1) = { *bar }; // box test 2 より、これも OK
// box test 4
// 自分自身を返すメソッドを使っても OK
trait Ident: Sized {
fn id(self) -> Self { self }
}
impl Ident for Bar {}
let bar = Box::new(Bar(Foo(0), Foo(1)));
let Bar(foo0, foo1) = bar.id(); // OK
参照と借用 References and Borrowing
「借用」borrowing 機能により、リソースをコピーすることもその所有権をムーブすることもなく、リソースを用いることが出来る。
- 型
T
の変数x
について、アンパサンド&
を用いて&x
と記述することで、型T
のリソースを借用する参照型(参照)&T
を得る。- また、更にキーワード
mut
を用いることで、mutable な変数x
について、&mut x
により、mutable な参照型&mut T
を得る。 - あるいは
ref
/mut ref
パターンを用いて参照型を得ることが出来る。
- また、更にキーワード
- 1つの変数から同時に複数の immutable な参照を作ることが出来る。
- 1つの mutable な変数からは、mutable な参照は同時に 1 つしか作ることが出来ない。
- これを犯すと、コンパイルエラーが生じる。[#E0499]
- immutable な参照と mutable な参照は、同時に作ることが出来ない。
- これを犯すと、コンパイルエラーが生じる。[#E0502]
- 借用されている変数は、参照が 1 つでも生きている間(=生存期間)、変更することが出来ない。
- これを犯すと、コンパイルエラーが生じる。[#E0506]
- このルールにより、immutable な参照の値が不変であることが保証される。
- これにより、借用されているカスタム型について、その成分 element を変更することも出来ない。
- 以上の性質により mutable な参照は所有権のムーブと似ている。
- 異なるのは mutable な参照がスコープアウトして使えなくなった後、元の変数は通常通りに使用可能であると言うことである。
- つまり、
fn do_something(x: Foo) -> Foo
のようにしてリソースの明示的な返却を行う(See 所有権を越えて)必要が無く、fn do_something(x: &mut Foo)
で済むということである。
- 変数ではなく、リテラルからも参照を作ることが出来る。
-
v1.21.0より、リテラルの参照は、
'static
なライフタイムを持つ。ライフタイムについては後述。 - ただし何故かリテラルの
&mut
による mutable な参照は、ローカルスコープのライフタイムを持つ。(これはバグなんじゃなかろうか?)
-
v1.21.0より、リテラルの参照は、
#![allow(unused_variables)]
#[derive(Debug, PartialEq)]
struct Foo(i32);
let foo = Foo(123);
{ // 同時に無数の immutable な参照を作成できる.
let r1: &Foo = &foo;
let r2 = &foo;
let ref r3 = foo; // ref パターンによる参照作成
let r4: &Foo = r3;
println!("{:?} -> {:?}, {:?}, {:?}, {:?}", foo, r1, r2, r3, r4);
}
let mut bar = Foo(123);
{ // mutable な参照は同時に 1 つしか作成できない。
{
let r1 = &mut bar;
// let r2 = &mut bar; // <- Error
r1.0 += 1;
}
{
let ref mut r3 = bar; // ref mut パターンによる mutable な参照作成
r3.0 += 1;
}
assert_eq!(bar, Foo(125));
}
{ // immutable な参照と mutable な参照は、同時に作ることが出来ない。
{
let r1 = &mut bar;
// let r2 = &bar; // <- Error
}
{
let r1 = &bar;
// let r2 = &mut bar; // <- Error
}
}
{ // 借用されている変数は、変更することが出来ない。
let r1 = &bar;
// bar.0 += 1; // <- Error
}
{ // リテラルの参照は、`'static` のライフタイムを持つ.
fn check_static(a: &'static Foo) { println!("{:?}", a); }
let r1 = &Foo(1);
check_static(r1);
let ref r2 = Foo(2);
check_static(r2);
let r3 = &mut Foo(3); // リテラルを `&mut` したものは `'static` なライフタイムを持たない
// check_static(r3); // <- Error
let ref mut r4 = Foo(4);
check_static(r4);
}
- 参照は参照外し演算子
*
により、一時的に参照から実体へと変化する。- しかし参照の参照外しにより、リソースの所有権をムーブすることは出来ない。
- これを破ると、コンパイルエラーが生じる。[#E0507]
- 一方で Copy を実装していると、リソースのコピーは可能。
- しかし参照の参照外しにより、リソースの所有権をムーブすることは出来ない。
- immutable な参照は言うまでもなく、mutable な参照についても、リソースが元の変数へと返却される必要がある。
- そのため、参照はリソースを consume する、もといリソースの破壊的な処理は行えない。
- これを破ると、コンパイルエラーが生じる。[#E0507]
- そのため、参照はリソースを consume する、もといリソースの破壊的な処理は行えない。
#![allow(unused_variables, dead_code)]
//#[derive(Debug, PartialEq)]
struct Foo(i32);
impl Foo {
fn not_consume(&self) {}
fn consume(self) {}
}
let foo = Foo(100);
let val = 23i32;
{ // 参照からリソースをムーブすることは出来ない.
let f = &foo;
let g = &val;
// let f2 = *f; // <- Error. ムーブ不可なのでエラー
let g2 = *g; // i32 はコピー可なので O.K.
}
{ // 参照からリソースをムーブすることは出来ない.
let f = &foo;
f.not_consume(); // メソッド `consume(self)` は `self` を消費しないので可.
// f.consume(); // メソッド `consume(self)` は `self` を消費するので不可.
}
- 参照型の実体はポインタである。
- そのためキーワード
as
を用いたキャストにより、ゼロコストで生ポインタへと変換できる。-
r
/mut_r
を型T
の immutable / mutable な参照&T
/&mut T
として、-
r as *const T
,r_mut as *const T
でconst
ポインタ -
r_mut as *mut T
でmut
ポインタ
-
-
- そのためキーワード
カスタム型の借用
- カスタム型について、その借用の管理は成分 element 単位で行われる。
#![allow(unused_variables)]
struct Foo(i32);
struct Bar { a: Foo, bc: (Foo, Foo), };
let bar = Bar { a: Foo(1), bc: (Foo(2), Foo(3)) };
{ // カスタム型の成分をそれぞれ借用することが出来る.
let b = &bar;
let ba = &bar.a;
let bb = &b.bc.0;
}
let mut bar2 = Bar { a: Foo(1), bc: (Foo(2), Foo(3)) };
{
{ // 借用の管理は成分 element 単位で行われる
let b_a = &bar2.a;
let b_a2 = &bar2.a;
let b_b = &mut bar2.bc.0;
// let b_b2 = &mut bar2.bc.0; // bar2.bc.0 は既に mutable 借用されているので、更に借用することは不可.
let b_c = &bar2.bc.1;
// let b_c2 = &mut bar2.bc.1; // bar2.bc.1 は既に借用されているので、mutable な参照を作ることは不可.
}
}
ライフタイム
- 参照型の実体はポインタであるが、所有権システムと同様のシステムにより、ダングリング・ポインタ (dangling pointer) もといダングリング参照 (dangling references) の発生をコンパイラが防いでいる。
- すなわち、参照の生存期間 (lifetime; ライフタイム) を、参照先の変数がリソースを所有している期間より常に短くコンパイラが制限している。
- このことで、参照先の変数がリソースの所有権を失っているというようなこと、リソースが開放済みであるようなことが決して発生しない。
参照値の内部値の取りだし
let optional: Option<Box<i32>> = None;
check_optional(&optional);
let optional: Option<Box<i32>> = Some(Box::new(9000));
check_optional(&optional);
fn check_optional(optional: &Option<Box<i32>>) {
match *optional {
Some(ref p) => println!("have value {}", p),
None => println!("have no value"),
}
}
参照(借用)は内部値にも浸透するので、そのパターンマッチに際し ref
が必要。
mem::replace
を使って、借用値をclone()
すること無く書き換える
TL;DR: &mut
変数から、値をムーブして得たいことがある。そのためには、目的の値を別の値で置換することで得る。
-
&mut
で得ている変数やその変数の成分を他へムーブすることは出来ない。- そこで
std::mem::replace
を使って、他の値と交換することで適当な更新を行う。 - See Rustの身代わりパターン - 簡潔なQ,
mem::replace
to keep owned values in changed enums
- そこで
-
mem::replace
とmem::swap
は、ライフタイムや借用の制約がある Rust プログラミングにおいて多用する、共に重要な関数である。 - 以下は
A.x == 0
のときにB
へと変換するコード。- ナイーブな処理では、借用値の中身をムーブ出来ないとエラーが出る。
-
clone()
する処理では上手く行くが、クローン分無駄なコストがかさむ。 - そこで
mem::replace
して目標を空文字列と入れ替えて取り出すと、上手く行く。- 空文字列
String::new()
だけではヒープ確保は行われないので、余計なコストを増やさずに済む。
- 空文字列
- なお、
*e
への代入がif let
式の外側に出ているのは、if let
式の内部ではe
が借用されているため、更新できないから。 - ただしこの例は、
fn a_to_b(e: MyEnum)
と引数をconsumeする関数の方が妥当かもしれない。
use std::mem;
enum MyEnum {
A { name: String, x: u8 },
B { name: String }
}
/*
fn a_to_b_naive(e: &mut MyEnum) {
*e = if let MyEnum::A { ref mut name, x: 0 } = *e {
MyEnum::B { name: *name }
// ^^^^^ cannot move out of borrowed content
} else { return }
}
*/
fn a_to_b_clone(e: &mut MyEnum) {
*e = if let MyEnum::A { ref mut name, x: 0 } = *e {
MyEnum::B { name: name.clone() }
} else { return }
}
fn a_to_b(e: &mut MyEnum) {
*e = if let MyEnum::A { ref mut name, x: 0 } = *e {
MyEnum::B { name: mem::replace(name, String::new()) }
} else { return }
}
型注釈なしの変数への代入
型注釈の無い変数への代入は、元の型が参照型であっても、新しい変数が参照先のリソースの所有権を奪い取る(ムーブさせる)ので注意。
See Issue #35919: Adding type annotation equivalent to existing type changes code semantics
- 次のコードは問題なくコンパイルが通る。
-
let x: &mut _ = y;
の行はlet x = y as &mut _;
と変えても等価。
-
fn main()
{
let y = &mut 2i32;
{
let x: &mut _ = y;
*x = 3;
}
println!("{}", y);
}
- しかし型注釈を取り除いた次のコードではコンパイルエラーが生じる。
-
println!();
の行でuse of moved value:
.y
- これは
let x = y;
の行で、x
へy
の持つ所有権がムーブされるためである。
-
fn main()
{
let y = &mut 2i32;
{
let x = y;
*x = 3;
}
println!("{}", y);
}
- 2つのコードで
x
の型は同一であるのに型注釈の有無で挙動が変わるのは不合理なので、何時か修正されるかも知れない。
実装 impl
impl
キーワードを用いた実装には、以下の2種類がある。
固有実装 inherent implementation
- 関連付ける関数(メソッド)は、以下の2種類に分けられる。
- クラスメソッド(関連関数)
- インスタンスメソッド
- 固有実装が書けるのは、型の定義されているクレート内部のみ。[#E0116]
- よって、例えば
Vec<T>
はstd
クレートで定義されているため、ユーザーが勝手にメソッドを追加することは出来ない。 - 故に、外部クレートの型について固有実装を書きたいときは、1要素タプル構造体などのラッパーを自分で書く必要がある。
- よって、例えば
メソッド構文 Method Syntax
- キーワード
self
を用いて、インスタンスメソッド(のようなもの)を定義できる- 第1引数が
&self
,&mut self
,self
のいずれかである - インスタンスメソッドは、インスタンスに対しドット
.
を用いたメソッド呼び出しが行える。- 例えばインスタンスメソッド
fn method(&self, val: i32)
は、その型Foo
のインスタンスfoo
に対し、foo.method(10)
で呼び出しが行える。
- 例えばインスタンスメソッド
- ドットを用いない呼び出しも行える。
- 先の例においては、
foo.method(10)
==Foo::method(&foo, 10)
- 先の例においては、
- 定義において
-
fn method(&self) { ... }
==fn method(self: &Self) { ... }
-
fn method(&mut self) { ... }
==fn method(self: &mut Self) { ... }
-
fn method(self) { ... }
==fn method(self: Self) { ... }
- 前者は後者の糖衣構文
-
-
fn method(self)
メソッドにおいては、self
は消費 (consume) されてしまうことに留意。- というか
self
を消費(破損)するようなメソッドを、これで記述する。 - 一方
fn method(&self)
,..(&mut sulf)
メソッドにおいては、メソッド呼び出し後にself
は借用から戻ってくる。
- というか
- 第1引数が
- それ以外は、クラスメソッド(のようなもの)である
-
Self::new()
は、よく使われるクラスメソッドの代表例
-
#[derive(Debug)]
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn new(x: f64, y: f64, radius: f64) -> Self {
Circle { x: x, y: y, radius: radius, }
}
}
impl Circle {
fn reference(&self) {
println!("taking self by reference!");
}
fn mutable_reference(&mut self) {
println!("taking self by mutable reference!");
}
fn takes_ownership(self) {
println!("taking ownership of self!");
}
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
let c = Circle::new(0.0, 0.0, 1.0);
c.reference();
println!("{:?}'s area = {}", c, c.area());
println!("{:?}'s area = {}", c, Circle::area(&c));
c.takes_ownership(); // ここで c が消費される
// c.reference(); // よってもう c は使えない
トレイト実装 trait implementation
- トレイトの実装においては、関数(メソッド)を実装するだけでなく、以下の関連付けも行う。
- 型(関連型)
- 定数(関連定数)
関連型
- 型やトレイトの定義において、他の型を関連付けることで、メソッドの引数や戻り値の型の扱いが簡単になる。
関連定数 Associated constants
- v1.20より、型に定数を関連付けられるようになった。
#[derive(Debug)]
struct Foo;
impl Foo {
const ID: i32 = 9;
const FOO: Self = Foo;
}
trait Bar {
const ID2: i32;
}
impl Bar for Foo {
const ID2: i32 = Foo::ID * 10;
}
println!("{}", Foo::ID);
println!("{:?}", Foo::FOO);
println!("{}", Foo::ID2);
クロージャ Closures
- クロージャはバーティカルバー
|
で引数を囲むことで記述する - 引数、戻り値の型名は省略も明示も可能
let plus_one = |x| x + 1;
let plus_two = |x: i32| { x + 2 };
let plus_three = |x: i32| -> i32 { x + 3 };
assert_eq!(3, plus_one(2));
assert_eq!(3, plus_two(1));
assert_eq!(3, plus_three(0));
- クロージャは
std::ops::{Fn, FnOnce, FnMut}
トレイトを実装した何かであり、- クロージャ構文は
Fn, FnOnce, FnMut
トレイト実装の糖衣構文である。 - 単に
Fn(T) -> U
の実装と言うだけでは様々なサイズの実装があり得るため、サイズ不定であり、下のplus_4
の文はエラーとなる。- よって
Box
で包む必要がある。See クロージャを返す - TRPL-ja - See クロージャを boxせずに 返したい: Rustのconservative_impl_traitとimplementation leak - 簡潔なQ.
- よって
- クロージャ構文は
// let plus_4: Fn(i32) -> i32 = |x| x + 4; Error
// ^^^^^^ `std::ops::Fn(i32) -> i32` does not have a constant size known at compile-time
let plus_4_box: Box<Fn(i32) -> i32> = Box::new(|x| x + 4); // OK.
assert_eq!(3, plus_4_box(-1));
move
クロージャ move
closures
-
move
キーワードをクロージャ構文の前に置くことで、クロージャに環境の取得を強制する。 - クロージャを引数に取るには、型にトレイト
std::ops::{Fn, FnMut, FnOnce}
を用いる。- クロージャを引数に取る関数に、関数ポインタを与えることも出来る。
let mut counter = {
let mut tmp = 0i32;
move |step: i32| { tmp += step; tmp } // move が無いとライフタイムエラー
};
fn call_with_2<F>(f: &mut F) -> i32
where F: FnMut(i32) -> i32 {
f(2)
}
assert_eq!(counter(1), 1);
assert_eq!(call_with_2(&mut counter), 1+2);
assert_eq!(counter(3), 1+2+3);
fn triple_func(v: i32) -> i32 { v * 3 }
assert_eq!(call_with_2(&mut triple_func), 6);
マクロ Macros
-
foo!()
のように、括弧の前・識別子の後に!
が付いてるのがマクロ呼び出し。- マクロ呼び出しには、丸括弧
()
の他に波括弧{}
・角括弧[]
を用いることが出来る。-
foo! {1, 2}
,foo!(1, 2);
- (foo!
がユニット型を返すとき)これら2つはほぼ等価。 - 波括弧
{}
の呼び出しでは、マクロの値がユニット()
であるとき、区切りのセミコロン;
が不要。- アイテム的に働くマクロのとき、この波括弧
{}
呼び出しを使うと分かり易いだろう。
- アイテム的に働くマクロのとき、この波括弧
-
test_macro![];
- 何故か角括弧[]
でも呼び出せる。
-
- マクロ呼び出しには、丸括弧
macro_rules! test { () => ( 10 ); }
{
test!();
test!{}; // <- need ';'
test![];
}
macro_rules! test { () => ( ; ); }
{
test!();
test!{} // <- don't need ';'
test![];
}
- Rust には可変長引数関数が無いので、それらの機能はマクロにより提供される。例:
println!
,vec!
- マクロの引数において、各括弧(丸括弧
()
・角カッコ[]
・波括弧{}
)の対応は完全に整合していなければならない。- この対応により、コンパイラはマクロの展開を後回しすることが出来る。
カスタムマクロ
- 識別子
macro_rules!
を用いて、カスタムマクロを定義できる。 - 引数に対してパターンマッチを行い、新しい構文木を作る。
- マクロ引数に対する独自のパターンは、マッチャー matcher と呼ぶ。
- マッチャーにおける変数は、頭にドル記号
$
が、尻にはフラグメント指定子 (fragment specifier) が付く。- フラグメント指定子は、構文木における属性を意味する。
指定子 | 意味 | 例 | 説明 |
---|---|---|---|
ident |
識別子 |
x , foo
|
識別子1つ |
path |
パス | std::io |
モジュールや名前空間を表すパス1つ |
expr |
式 |
1 + 2 , foo(x)
|
値や関数の組み合わせて作られた式1つ |
ty |
型 |
i32 , &str , Foo
|
型1つ |
pat |
パターン |
Some(x) , (x, y) , _ , ref x
|
match 式やlet 文で使われるパターン1つ |
stmt |
文 |
let x = 10.0 , use std::*;
|
let 文やアイテムとかの文1つ |
block |
ブロック |
{} , { println!("Hello"); }
|
波括弧{} で括られたブロック1つ |
item |
アイテム | fn foo() {} |
fn アイテム(関数定義文)とかのアイテム1つ |
meta |
メタアイテム |
derive(Copy) , cfg(foo = "Foo")
|
属性付与文#[〜] 中にあるアイテム1つ |
tt |
トークン木 |
10 , { println!("!?"); }
|
トークン木1つ |
- マッチャーにおいては、正規表現と同様に、
$(...)*
/$(...)+
で0回以上/1回以上のくり返しを表す。- ここで
*
/+
の前に,
や;
などの文字を置くと、適当にデリミタとして働く。- このデリミタの文字は、Rust 文法の整合性から、修飾する識別子により制限を受ける。
-
$pa:ident
に対しては、,
,;
,|
,||
,=>
,=
,-
,~
,!
,@
,#
,%
,^
,<
,>
,<<
,>>
,?
etc. -
$ex:expr
に対しては,
や;
など。 -
$st:stmt
に対しては,
や;
など。 -
$pa:pat
に対しては,
や|
など。 -
$it:item
に対しては,
や;
など。 -
$ty:ty
に対しては,
,;
,:
,|
, etc. -
$bl:block
に対しては、ident
と同様に沢山 -
$tt:tt
に対しては、ident
と同様に沢山 -
$me:meta
に対しては、ident
と同様に沢山
-
- このデリミタの文字は、Rust 文法の整合性から、修飾する識別子により制限を受ける。
- ここで
macro_rules! twice {
( $( $x:stmt ),* ) => {
$(
$x;
)*
$(
$x;
)*
};
}
twice!(print!("1 "));
twice!(print!("2 "), print!("3 "));
// result: 1 1 2 3 2 3
- 変数に与えるフラグメント指定子により、変数にマッチさせた引数は同一でも、生成可能な式に違いが出る。
- 例えば次のマクロは、
ident
やtt
以外の指定子ではうまく働かない。
- 例えば次のマクロは、
macro_rules! get_max (($x:ident) => (std::$x::MAX));
println!("{}", get_max!(u8));
-
cargo rustc -- -Z unstable-options --pretty=expanded
でマクロの展開結果が確認できる。- Nightly チャネルのみ可。
- 以下、フラグメント指定子によるマッチングの例。
- 参考:Rustのマクロを覚える
expr
識別子
macro_rules! match_expr {
($e:expr) => ( println!("{} is a expr.", stringify!($e)) );
($($e:expr)+) => ( println!("{} are exprs.", stringify!($($e)+)) );
}
match_expr!("a");
match_expr!(1 + 2);
match_expr!(foo(10));
match_expr!(if a { 0 } else { 1 });
ty
識別子
macro_rules! match_ty {
($t:ty) => ( println!("{} is a ty.", stringify!($t)) );
($($t:ty)+) => ( println!("{} are tys.", stringify!($($t)+)) );
}
match_ty!(Foo); // unknown type
match_ty!([i32]); // slice
match_ty!([i32; 4]); // array
match_ty!(*const Foo); // pointer
match_ty!(&'a Foo); // reference
match_ty!(fn(i32) -> &str); // fn
match_ty!(Fn(i32) -> &str);
match_ty!(!); // nevar
match_ty!((i32,)); // tuple
match_ty!((i32,float,&'static str));
match_ty!(my::Foo::Bar); // path
match_ty!(<Vec<T> as SomeTrait>::SomeType);
match_ty!(Clone + Copy); // trait object
match_ty!(Clone + Copy + 'a + 'b);
match_ty!(impl ATrait); // impl trait
match_ty!((i32)); // parened
match_ty!(self);
match_ty!(Self);
match_ty!(Foo<i32,Bar>);
match_ty!(());
match_ty!((i32,));
match_ty!(Bar(i32));
pat
識別子
macro_rules! match_pat {
($p:pat) => ( println!("{} is a pat.", stringify!($p)) );
($($p:pat)+) => ( println!("{} are pats.", stringify!($($p)+)) );
}
match_pat!(_);
match_pat!(Some(x));
match_pat!(Foo { val: ref x} );
match_pat!(mut x);
match_pat!(3...5);
match_pat!(x @ 3...5);
match_pat!(a);
// match_pat!(x if x > 10); // not pat
// match_pat!(1 | 3); // not pat
stmt
識別子
-
stmt
は文にマッチする。 -
stmt
は値(式)にもマッチする。- しかし式を
$s:stmt
にマッチさせても、そのままでは式あるべきところに$s
は置けない。-
foo($s)
はマクロの引数を適当に与えてもエラーとなる。 -
foo({$s})
の様に、ブロックで囲むと良い。
-
- しかし式を
macro_rules! match_stmt {
($e:stmt) => ( println!("{} is a stmt.", stringify!($e)) );
($($e:stmt)+) => ( println!("{} are stmts.", stringify!($($e)+)) );
}
// 以下、引数のセミコロンの有無に注目
match_stmt!("a");
match_stmt!(1 + 2);
match_stmt!(foo(10));
match_stmt!(let a = 10);
match_stmt!(use std::*;);
match_stmt!(const A: i32 = 10;);
match_stmt!(static A: i32 = 10;);
match_stmt!(fn f() {});
match_stmt!(struct foo(););
match_stmt!(struct bar {} );
match_stmt!(type a = b;);
match_stmt!(macro_rules! test {} );
match_stmt!(test!{} test!{} ); // stats
match_stmt!(let a let b); // let a; let b; are stmts. WHY?
macro_rules! myvec{
( $( $x:stmt );* ) => {
{
let mut temp_vec = Vec::new();
$(
// temp_vec.push($x); // ERROR
temp_vec.push({$x});
)*
temp_vec
}
};
}
println!("{:?}", myvec!(10)); // [10]
println!("{:?}", myvec!(10; 20; 30)); // [10, 20, 30]
block
識別子
macro_rules! match_block {
($b:block) => ( println!("{} is a block.", stringify!($b)) );
($($b:block)+) => ( println!("{} are blocks.", stringify!($($b)+)) );
}
match_block!( {} );
match_block!({a + b;});
match_block!({} {});
tt
識別子
- トークン木 (token trees) がどういう物か分かりにくいので、引数のトークン木の数と、単一トークン木で分割した物を表示するマクロを書いてみた。
- 結果、リテラルや識別子1つ、キーワードや記号1つ、ブロック1つ等が単一トークン木であることが分かる。
macro_rules! count_tt {
() => ( 0 );
($t0:tt $($t:tt)*) => ( 1 + count_tt!($($t)*) );
}
macro_rules! split_tt {
($t:tt) => ( stringify!($t) );
($t:tt $($ts:tt)+) => ( concat!(stringify!($t), " | ", split_tt!($($ts)+)) );
}
macro_rules! info_tt {
() => ( println!("0 tt") );
($t:tt) => ( println!("1 tt = {}", stringify!($t)) );
($($t:tt)+) => ( println!("{} tts = {}", count_tt!($($t)+), split_tt!($($t)+)) );
}
info_tt!();
info_tt!(());
info_tt!(x);
info_tt!("Hello");
info_tt!(1 + 2);
info_tt!( (1 + 2) );
info_tt!(foo(x););
info_tt!( let a: i32 = 10; );
info_tt!( fn foo { test(); } );
info_tt!( struct foo(i32); );
info_tt!( struct bar {} );
info_tt!( { println!(Hello); } );
結果は
0 tt
1 tt = ( )
1 tt = x
1 tt = "Hello"
3 tts = 1 | + | 2
1 tt = ( 1 + 2 )
3 tts = foo | ( x ) | ;
7 tts = let | a | : | i32 | = | 10 | ;
3 tts = fn | foo | { test ( ) ; }
4 tts = struct | foo | ( i32 ) | ;
3 tts = struct | bar | { }
1 tt = { println ! ( Hello ) ; }
手続きマクロ Procedural Macros
- 上記のマッチャーによるマクロとは異なり、コンパイル時に呼び出され、構文木を直接変換する関数によるマクロ機能が存在する。
- これを手続きマクロ (Procedural Macros)、またはコンパイラプラグインと呼ぶ。
- パターンマッチによるマクロより非常に柔軟なマクロが記述できる。
- しかし扱いは格段に難しく、デバッグ困難なバグの温床ともなりうる。
std::*
の主要なマクロ
- 以下の標準マクロのソースは大体
src/libcore/
以下にあるが、特殊な処理のマクロはコンパイラ組み込みであることが多い。
特定型処理
-
vec!
-Vec<T>
生成に使われる。
プリント用
-
format!
- フォーマッティングを行う。Seestd::fmt
-
{}
-trait Display
を呼ぶ -
{:?}
-trait Debug
を呼ぶ - 他
o,x,X,p,b,e,E
に合わせた trait がある。See alsostd::fmt
-
String
型を返す
-
-
format_args!
- 関数
std::fmt::format
の引数であるstd::fmt::Arguments
型を返すマクロ - マクロ
format_args!
を用いることで、関数format
はマクロformat!
と同等の機能を持つ assert_eq!(std::fmt::format(format_args!("{} + {} == {}", 1, 2, 1+2)), "1 + 2 == 3");
- 関数
-
print!
,println!
- C言語のprintf()
的な。標準出力std::io::stdout
へと書き込む。-
eprint!
,eprintln!
- 標準出力では無く、標準エラー出力std::io::stderr
へと書き込む。
-
-
write!
,writeln!
- C言語のfprintf()
的な。ライター型に書き込む。
デバッグ用
-
assert!
,assert_eq!
- C言語のassert()
的な。アサーション失敗時に表示されるメッセージを、println!
の形式で与えることも出来る。assert!(a + b == 30);
assert!(a + b == 30, "a = {}, b = {}", a, b);
assert_eq!(a + b, 30);
assert_eq!(a + b, 30, "a = {}, b = {}", a, b);
-
assert_eq!
に与える変数は、std::fmt::Debug
トレイトの実装が要求されることがあるので注意。
-
debug_assert!
,debug_assert_eq!
- 上と似てるが、最適化時に除去される。
エラー処理
-
try!
-Result
型からOk
の内部値を取り出す。もしErr
ならpanic
を起こす。 -
panic!
-panic
を起こし処理を停止する。値やフォーマットメッセージを与えることも可能。See src.panic!();
panic!("this is a terrible mistake!");
panic!(4); // panic with the value of 4 to be collected elsewhere
panic!("1 + 2 = {}", 1 + 2);
特殊宣言
-
unreachable!
- 到達しないことを宣言する。到達するとpanic
を起こす。- 最適化のために使われるのは、
std::mem::
unreachable
関数の方。 - 値やフォーマットメッセージを与えることも可能。See src
unreachable!();
unreachable!("Unreachable!!!!");
- 最適化のために使われるのは、
-
unimplemented!
- 現在未実装であることを宣言する。- 値やフォーマットメッセージを与えることも可能。See src
コンパイル時処理
-
file!
,module_path!
,line!
,column!
- ファイル名、モジュールパス、行、カラムを取得する。-
file!()
,module_path!()
- 戻り値の型は&'static str
-
line!()
,column!()
-u32
-
-
stringify!
- 引数を評価せずにそのまま&'static str
に変換する。assert_eq!(stringify!(1 + 1), "1 + 1");
-
concat!
- 引数を結合して&'static str
に変換するassert_eq!(concat!("test", 10, 'b', true), "test10btrue");
-
concat_idents!
- [Unstable] 識別子 (identifier) を結合する。C言語のfoo##bar
みたいな? -
include!
,include_bytes!
,include_str!
- コンパイル時に外部ファイルを読み込む。 -
env!
,option_env!
- 環境変数を取得 -
cfg!
- 構成フラグを取得して真偽値を返す。See 1st/Jpn-
cgf!(debug_assertions)
-debug_assert!
が呼ばれるときにtrue
-
#![feature(core_intrinsics)]
fn get_type_of<T>(_: &T) -> &'static str {
unsafe { std::intrinsics::type_name::<T>() }
}
macro_rules! print_ident {
($v:ident) => (println!("{}: {} = {:?}", stringify!($v), get_type_of(&$v), $v));
}
fn main() {
if cfg!(debug_assertions) {
println!("Here is called when `debug_assertions` flag is on.");
}
let file = file!();
let mod_path = module_path!();
let line = line!();
let column = column!();
print_ident!(file); // file: &str = "src/main.rs"
print_ident!(mod_path); // mod_path: &str = "playground"
print_ident!(line); // line: u32 = 15
print_ident!(column); // column: u32 = 17
}
マルチスレッド関連
-
select!
- [Unstable] thread_local!
モジュールシステム
-
mod.rs
/lib.rs
(/main.rs
) の名のファイルは、同じ階層における他の.rs
ファイル(モジュール)の公開情報を記述する特別なファイル。
mod.rs
/lib.rs
-
mod.rs
ファイルには以下のような記述が並ぶ。-
pub
修飾子は、当然pub(super)
のように可視範囲を指定できる。See 可視性. mod mod_name
pub mod mod_name
use path
-
pub use path
-
pub use self::path::item
- 等のリネームはよく使う。ファイル分割によって長くなったパスを省略するため。
-
mod { items }
-
- 以下は例。
mod a;
pub mod b;
mod c;
mod d;
mod bar;
// ここの `self` は階層 `foo` を意味する。
use self::c::C; // fooモジュール内において「foo::c::C」の代わりに「foo::C」と書けるようになる。
pub use self::d::D; // 「foo::d::D」の代わりに「foo::D」と。
pub use self::bar::x::X; // 「foo::bar::x::X」の代わりに「foo::X」と。
pub use self::bar::y::Y as BarY; // 「foo::bar::y::Y」の代わりに「foo::BarY」と。
pub mod baz {
pub use self::bar::z::Z; // 「foo::bar::z::Z」の代わりに「foo::baz::Z」と。
}
pub mod x;
use super::a::A; // ここの `super` は階層 `foo::bar` を意味する。
struct X(A);
mod foo;
//use self::foo::a; // <- private Module
use self::foo::b::*;
use self::foo::D; // foo::d::D; も可能
可視性 visibility
- キーワード
pub
を用いた修飾で、可視性を記述することが出来る。 -
pub(〜)
の形式で、公開範囲を限定できる。-
pub(in mod_name)
- キーワードin
を用いて、指定モジュール内で可視 -
pub(crate_name)
- 指定クレート内で可視 -
pub(self)
- 修飾されるアイテムが有るモジュール内で可視 -
pub(super)
- 修飾されるアイテムが有るモジュールの上位モジュール内で可視
-
属性 Attributes
次のように、#![〜]
, #[〜]
で様々な属性を設定できる。
-
#![〜]
- 記された箇所を囲むアイテムに適用される。up↑的な。(inner attribute)- 冒頭(ファイル冒頭、関数本体部分の出だし、etc)に置く必要がある。
-
#[〜]
- 記された箇所に続くアイテムに適用される。next→的な。
// モジュール or クレートに作用
#![crate_type = "lib"]
// 続く関数 `test_foo` に作用
#[test]
fn test_foo() {
/* ... */
}
// 続くモジュール `bar` に作用
#[cfg(target_os="linux")]
mod bar {
/* ... */
}
// 続く型 `int8_t` に作用
#[allow(non_camel_case_types)]
type int8_t = i8;
lintチェック属性 Lint check attributes
lintチェック C について、
-
allow(C)
- C 違反を許可する(警告を出さない) -
deny(C)
- C 違反についてエラーを出す -
forbid(C)
-deny(C)
とほぼ同様だが、lintレベルの変更下で違いが出る -
warn(C)
- C 違反について警告を出す
主なlintチェックとして以下がある。
non_snake_case
dead_code
unused_variables
- ...
#[inline]
インライン展開についてコンパイラーにヒントを与える。Cのinline
キーワード的な
#[inline]
-
#[inline(always)]
- 「常に」インライン展開するべきと要請する。- コンパイラに任せた方が性能向上が見込めるので、使用には要注意。
-
#[inline(never)]
- 「決して」インライン展開するべきでないと要請する
#[derive]
- カスタム型(構造体や列挙型)に対し、以下のトレイトを自動的に実装する。とても便利。
-
PartialEq
,PartialOrd
,Eq
,Ord
-std::cmp
より -
Clone
-std::clone
より Copy
Hash
Default
Zero
-
Debug
-println!
等のフォーマッティングにおいて{:?}
で呼び出される - 以上の
derive
のソースはここ:src/libsyntax_ext/deriving/
-
- v1.15より、手続きマクロを用いたカスタム derive が可能となった。See Procedural Macros (and custom Derive)
#[derive(Debug, Clone, Copy)]
struct Foo {
id: i32,
val: f32,
ch: char,
}
let foo1 = Foo { id: 10, val: 1.23, ch: '♡'};
let foo2 = foo1; // copy foo1
println!("{:?}", foo1); // Foo { id: 10, val: 1.23, ch: '♡' }
println!("{:?}", foo2); // Foo { id: 10, val: 1.23, ch: '♡' }
#[feature]
- 不安定な非標準機能の使用を可能とする。
- 不安定ゆえ Stable, Beta チャネルにおいてはエラー ([E0554]) となり、Nightly チャネルのみ使用可能。
-
$ rustup default nightly
,$ rustup run nightly cargo run
等が必要
-
- 例:サンプル「型名表示」
#[cfg]
Conditional compilation
cfg
属性で条件付きコンパイルが可能。その条件としては、
- ターゲットOS/アーキテクチャ/OSファミリー/プラットフォーム/エンディアン/ポインタサイズ/ベンダー/……
- テスト
- デバッグ・アサーション
- などなど、リファレンスに色々書いてある
またany
,all
,not
などの補助関数も条件に対して用いることが出来る。
// The function is only included in the build when compiling for macOS
#[cfg(target_os = "macos")]
fn macos_only() { /* ... */ }
// This function is only included when either foo or bar is defined
#[cfg(any(foo, bar))]
fn needs_foo_or_bar() { /* ... */ }
// This function is only included when compiling for a unixish OS with a 32-bit
// architecture
#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() { /* ... */ }
// This function is only included when foo is not defined
#[cfg(not(foo))]
fn needs_not_foo() { /* ... */ }
test
属性 The test
attribute
関数に#[test]
とtest
属性を与えることで、テスト関数であることを指定する。テストは$ cargo test
コマンドにより行われる。
関連アイテム
-
should_panic
属性 - 失敗するべきテストに与えられる属性-
expected
パラメータ - 失敗した際に与えられたメッセージに含まれるべき文字列を指定するパラメータ
-
-
ignore
属性 - デフォルトでは省略されるテストに与えられる属性 -
#[cfg(test)]
- モジュールにこの条件付きコンパイル属性を与えることで、テスト時のみにテスト部分がコンパイルされ、コンパイル時間・バイナリサイズの節約になる。
#[cfg(test)]
mod a_test {
#[test]
fn it_works() {
assert_eq!(1 + 1, 2);
}
#[test]
#[should_panic(expected = "assertion failed")]
fn it_failed() {
assert_eq!(1 + 1, 0);
}
#[test]
#[ignore]
fn long_time_needed() {
for i in 0i64.. {
assert!(i >= 0);
}
}
}
/* cargo test の結果
running 3 tests
test a_test::long_time_needed ... ignored
test a_test::it_works ... ok
test a_test::it_failed ... ok
test result: ok. 2 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out
*/
もしテストに失敗したら、test result: ok.
が test result: FAILED.
となる。
モジュール
以下
- イテレータ型 -
std::iter::Iterator
トレイトを実装した構造体。 - リーダー型 -
std::io::Read
トレイトを実装した構造体。 - ライター型 -
std::io::Write
トレイトを実装した構造体。 - [Buf]リーダー型 -
std::io::Read
orstd::io::BufRead
トレイトを実装した構造体。
を意味することにする。他の「○○型」は、適当に『○○を意味する/表す構造体』程度の意味。
std::prelude
デフォで読み込まれている(use
されている)アイテム群。
バージョン1 (v1
) においては以下の要素がデフォで使える。
- 構造体
- 列挙型(とその要素)
-
Option
,Some
,None
-std::option
-
Result
,Ok
,Err
-std::result
-
- トレイト
-
Copy
,Send
,Sized
,Sync
-std::marker
-
Drop
,Fn
,FnMut
,FnOnce
-std::ops
-
drop
-std::mem:
-
ToOwned
-std::borrow
-
Clone
-std::clone
-
PartialEq
,PartialOrd
,Eq
,Ord
-std::cmp:
-
AsRef
,AsMut,
Into,From
-std::convert:
-
Default
-std::default
-
Iterator
,Extend
,IntoIterator
,DoubleEndedIterator
,ExactSizeIterator
-std::iter
-
SliceConcatExt
-std::slice:
-
ToString
-std::string
-
std::convert
型変換のためのトレイト群。
トレイト
-
AsRef
- メソッドas_ref(&self)
で型T
の参照&T
を返す型をAsRef<T>
で統一的に扱うためのトレイト。- 例えば
&str
とString
は:AsRef<str>
でまとめて扱える。 -
as_ref
とstd::borrow
::
Borrow
トレイトのborrow
の違いについてはコチラを参照:Borrow and AsRef, Borrow と AsRef -
Option
やResult
のas_ref()
とは別物だが、あちらも重要。
- 例えば
-
AsMut
-AsRef
の mutable 版。 -
From<T>
- 型変換関係。 -
Into<T>
- 同上。fn into(self) -> T
- 型
U
がトレイトFrom<T>
を実装すると、型T
はトレイトInto<U>
を自動的に実装する。
-
TryFrom
- [Experimental] (try_from
#33417) 失敗するかも知れない型変換を扱うトレイト。Result
型を返すことで、変換失敗時にはエラー情報も乗せられる。fn try_from(value: T) -> Result<Self, Self::Error>
-
type Error
- 関連型。エラータイプを指定する。
-
TryInto
- [Experimental]fn try_into(self) -> Result<T, Self::Error>
type Error
std::ops
演算子オーバーロード。以下のトレイトを実装することでユーザ定義型に演算子操作を可能とする。
構造体 Structs
-
Range
-assert_eq!((3..5), std::ops::Range{ start: 3, end: 5 });
-
RangeFrom
-assert_eq!((2..), std::ops::RangeFrom{ start: 2 });
-
RangeTo
-assert_eq!((..5), std::ops::RangeTo{ end: 5 });
-
RangeFull
-assert_eq!((..), std::ops::RangeFull);
トレイト
以下の演算子オーバーロード用のトレイトがある。比較演算子についてはstd::cmp
を参照。
単項算術・論理演算子
二項算術演算子
二項ビット演算子
算術代入演算子
ビット代入演算子
-
BitOrAssign
-|=
-
BitAndAssign
-&=
-
BitXorAssign
-^=
-
ShlAssign
-<<=
-
ShrAssign
->>=
特殊演算子
-
Deref
- Special traits の一つ。参照外し演算子*
-
DerefMut
-*a = b;
-
Drop
- Special traits の一つ。いわゆるデストラクタ -
Index
- 添字演算子let v = foo[n]
-
IndexMut
-foo[0] = x;
関数呼び出し演算子
クロージャを受けるトレイトにもなる。
std::option
option
モジュールはかなり頻繁に使われる基本モジュールの一つ。
あるか、ないか (Some(x)
or None
) を表す Option<T>
がその主要なクラス。
モダンな言語でよくある Nullable.
if let Some(ref x) = xxx {
x.do_something();
} else {
unreachable!();
}
のようなコードは、
let x = xxx.as_ref().unwrap();
x.do_something();
で代用できることに留意。
列挙型 Enums
-
Option<T>
None
Some(T)
Option<T>
のメソッド
判定メソッド
-
fn is_some(&self) -> bool
-Some
値ならtrue
-
fn is_none(&self) -> bool
-None
値ならtrue
変換メソッド
-
fn as_ref(&self) -> Option<&T>
-
Some(x)
->Some(&x)
-
None
->None
-
-
fn as_mut(&mut self) -> Option<&mut T>
-
Some(x)
->Some(&mut x)
-
None
->None
-
-
fn map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U
-
Some(x)
->Some(f(x))
-
None
->None
-
-
fn map_or<U, F>(self, def: U, f: F) -> U where F: FnOnce(T) -> U
-
Some(x)
->f(x)
-
None
->def
-
-
fn map_or_else<U, D, F>(self, def: D, f: F) -> U where D: FnOnce() -> U, F: FnOnce(T) -> U
-
Some(x)
->f(x)
-
None
->def()
-
assert_eq!(Some(&"Hello"), Some("Hello").as_ref());
assert_eq!(None, (None as Option<String>).as_ref());
assert_eq!(Some(&mut "Hello"), Some("Hello").as_mut());
assert_eq!(None, (None as Option<String>).as_mut());
assert_eq!(Some(5), Some("Hello").map(|s| s.len()));
assert_eq!(None, (None as Option<String>).map(|s| s.len()));
assert_eq!(5, Some("Hello").map_or(10, |s| s.len()));
assert_eq!(10, (None as Option<String>).map_or(10, |s| s.len()));
assert_eq!(5, Some("Hello").map_or_else(|| 10, |s| s.len()));
assert_eq!(10, (None as Option<String>).map_or_else(|| 10, |s| s.len()));
Result
型変換メソッド
-
fn ok_or<E>(self, err: E) -> Result<T, E>
-
Some(x)
->Ok(x)
-
None
->Err(err)
-
-
fn ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E
-
Some(x)
->Ok(x)
-
None
->Err(err())
-
取り出しメソッド
-
fn unwrap(self) -> T
-
Some(x)
->x
-
None
->panic
-
-
fn expect(self, msg: &str) -> T
-
Some(x)
->x
-
None
->panic
with メッセージmsg
-
-
fn unwrap_or(self, def: T) -> T
-
Some(x)
-> x -
None
->def
-
-
fn unwrap_or_else<F>(self, f: F) -> T where F: FnOnce() -> T
-
Some(x)
->x
-
None
->f()
-
-
fn unwrap_or_default(self: Option<T>) -> T where T: Default
-
Some(x)
->x
-
None
->T::default()
-
イテレータメソッド
fn iter(&self) -> Iter<T>
fn iter_mut(&mut self) -> IterMut<T>
let a = Some(5);
let mut iter = a.iter();
assert_eq!(Some(&5), iter.next());
assert_eq!(None, iter.next());
let b: Option<i32> = None;
assert_eq!(None, b.iter().next());
let mut c = Some(5);
if let Some(v) = c.iter_mut().next() {
assert_eq!(v, &mut 5);
*v = 10;
}
assert_eq!(Some(10), c);
論理メソッド
-
fn and<U>(self, optb: Option<U>) -> Option<U>
-
self
,optb
共にSome
->optb
- otherwise ->
None
-
-
fn and_then<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> Option<U>
-map_or/_else
と似ている-
Some(x)
->f(x)
-
None
->None
-
-
fn or(self, optb: Option<T>) -> Option<T>
-
Some(x)
->Some(x)
-
None
->optb
-
-
fn or_else<F>(self, f: F) -> Option<T> where F: FnOnce() -> Option<T>
-
Some(x)
->Some(x)
-
None
->f()
-
assert_eq!(Some(5), Some(3).and(Some(5)));
assert_eq!(None, Some(3).and(None as Option<i32>));
assert_eq!(None, (None as Option<i32>).and(Some(5)));
assert_eq!(None, (None as Option<i32>).and(None as Option<i32>));
assert_eq!(Some(9), Some(3).and_then(|v| if v >= 0 { Some(v * v) } else { None }));
assert_eq!(None, Some(-3).and_then(|v| if v >= 0 { Some(v * v) } else { None }));
assert_eq!(None, (None as Option<i32>).and_then(|v| if v >= 0 { Some(v * v) } else { None }));
assert_eq!(Some(3), Some(3).or(Some(5)));
assert_eq!(Some(3), Some(3).or(None));
assert_eq!(Some(5), (None as Option<i32>).or(Some(5)));
assert_eq!(None, (None as Option<i32>).or(None));
assert_eq!(Some(3), Some(3).or_else(|| Some(5)));
assert_eq!(Some(3), Some(3).or_else(|| None));
assert_eq!(Some(5), (None as Option<i32>).or_else(|| Some(5)));
assert_eq!(None, (None as Option<i32>).or_else(|| None));
その他メソッド
-
fn take(&mut self) -> Option<T>
- 要素を捨ててNone
になる -
fn cloned(self: Option<&'a T>) -> Option<T> where T: Clone
-clone()
して参照外し
let mut a = Some(5);
assert_eq!(Some(5), a.take());
assert_eq!(None, a);
assert_eq!(None, a.take());
assert_eq!(Some(3), Some(&3).cloned());
assert_eq!(None, (None as Option<&i32>).cloned());
-
fn get_or_insert(&mut self, v: T) -> &mut T
-
None
なら引数を中身にし、Some
ならそのまま。 - そして
Some
の中身へのmut
参照を返値とする。
-
-
fn get_or_insert_with<F>(&mut self, f: F) -> &mut T where F: FnOnce() -> T
-
None
なら引数のクロージャを実行し、その返値を中身する。Some
ならそのまま。 - そして
Some
の中身へのmut
参照を返値とする。
-
let mut x = Some(10);
let mut y = None;
{
let x2 = x.get_or_insert(12);
let y2 = y.get_or_insert(12);
assert_eq!(*x2, 10);
assert_eq!(*y2, 12);
*x2 = 11;
*y2 = 13;
}
assert_eq!(x, Some(11));
assert_eq!(y, Some(13));
let mut x = Some(10);
let mut y = None;
let mut z = 1;
{
let x2 = x.get_or_insert_with(|| { z *= 2; 12 });
let y2 = y.get_or_insert_with(|| { z *= 3; 12 });
assert_eq!(*x2, 10);
assert_eq!(*y2, 12);
*x2 = 11;
*y2 = 13;
}
assert_eq!(x, Some(11));
assert_eq!(y, Some(13));
assert_eq!(z, 3);
std::result
option
モジュールと並び基本的なモジュール。
処理に成功した場合には欲しい戻り値をOk
で包み、処理に失敗した場合はそのエラー情報をErr
に包んで返す Result<T,E>
がその主要なクラス。
列挙型 Enums
-
Result<T,E>
Ok(T)
Err(E)
Result<T, E>
のメソッド
判定メソッド
-
fn is_ok(&self) -> bool
-Ok
値ならtrue
-
fn is_err(&self) -> bool
-Err
値ならtrue
変換メソッド
-
fn as_ref(&self) -> Result<&T, &E>
-
Ok(v)
->Ok(&v)
-
Err(e)
->Err(&e)
-
-
fn as_mut(&mut self) -> Result<&mut T, &mut E>
-
Ok(v)
->Ok(&mut v)
-
Err(e)
->Err(&mut e)
-
-
fn map<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> U
-
Ok(v)
->Ok(op(v))
-
Err(e)
->Err(e)
-
-
fn map_err<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> F
-
Ok(v)
->Ok(v)
-
Err(e)
->Err(op(e))
-
Option
型変換メソッド
-
fn ok(self) -> Option<T>
-
Ok(v)
->Some(v)
-
Err(e)
->None
-
-
fn err(self) -> Option<E>
-
Ok(v)
->None
-
Err(e)
->Some(e)
-
取り出しメソッド
-
fn unwrap(self) -> T where E: Debug
-
Ok(v)
->v
-
Err
->panic
-
-
fn expect(self, msg: &str) -> T
-
Ok(v)
->v
-
Err
->panic
with メッセージmsg
-
-
fn unwrap_or(self, optb: T) -> T
-
Ok(v)
->v
-
Err
->optb
-
-
fn unwrap_or_else<F>(self, op: F) -> T where F: FnOnce(E) -> T
-
Ok(v)
->v
-
Err(e)
->op(e)
-
-
fn unwrap_err(self) -> E where T: Debug
-
Ok
->panic
-
Err(e)
->e
-
-
fn expect_err(self, msg: &str) -> E
-
Ok
->panic
with メッセージmsg -
Err(e)
->e
-
-
fn unwrap_or_default(self) -> T where T: Default
-
Ok(v)
->v
-
Err
->T::default()
-
イテレータメソッド
fn iter(&self) -> Iter<T>
fn iter_mut(&mut self) -> IterMut<T>
let a: Result<i32,&str> = Ok(5);
let mut iter = a.iter();
assert_eq!(Some(&5), iter.next());
assert_eq!(None, iter.next());
let b: Result<i32,&str> = Err("err");
assert_eq!(None, b.iter().next());
let mut c: Result<i32,&str> = Ok(5);
if let Some(v) = c.iter_mut().next() {
assert_eq!(v, &mut 5);
*v = 10;
}
assert_eq!(Ok(10), c);
論理メソッド
-
fn and<U>(self, res: Result<U, E>) -> Result<U, E>
-
Ok
->res
-
Err(e)
->Err(e)
-
-
fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>
-
Ok(v)
->op(v)
-
Err(e)
->Err(e)
-
-
fn or<F>(self, res: Result<T, F>) -> Result<T, F>
-
Ok(v)
->Ok(v)
-
Err
->res
-
-
fn or_else<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> Result<T, F>
-
Ok(v)
->Ok(v)
-
Err(e)
->op(e)
-
type Res1<'a> = Result<i32,&'a str>;
type Res2<'a,'b> = Result<&'a str,&'b str>;
// and()
assert_eq!(Ok("ok2"), (Ok(2) as Res1).and(Ok("ok2") as Res2));
assert_eq!(Err("er2"), (Ok(2) as Res1).and(Err("er2") as Res2));
assert_eq!(Err("er1"), (Err("er1") as Res1).and(Ok("ok2") as Res2));
assert_eq!(Err("er1"), (Err("er1") as Res1).and(Err("er2") as Res2));
// and_then()
assert_eq!(Ok('A'), (Ok(65) as Res1).and_then(|v: i32| if 0x21 <= v && v <= 0x7E { Ok(char::from(v as u8)) } else { Err("fail") }));
assert_eq!(Err("fail"), (Ok(0) as Res1).and_then(|v: i32| if 0x21 <= v && v <= 0x7E { Ok(char::from(v as u8)) } else { Err("fail") }));
assert_eq!(Err("err"), (Err("err") as Res1).and_then(|v: i32| if 0x21 <= v && v <= 0x7E { Ok(char::from(v as u8)) } else { Err("fail") }));
assert_eq!(Err("err"), (Err("err") as Res1).and_then(|v: i32| if 0x21 <= v && v <= 0x7E { Ok(char::from(v as u8)) } else { Err("fail") }));
// or
assert_eq!(Ok(2), (Ok(2) as Res1).or(Ok(5) as Result<i32, i32>));
assert_eq!(Ok(2), (Ok(2) as Res1).or(Err(-2)));
assert_eq!(Ok(5), (Err("err") as Res1).or(Ok(5) as Result<i32, i32>));
assert_eq!(Err(-2), (Err("err") as Res1).or(Err(-2)));
// or_else
assert_eq!(Ok(1), (Ok(1) as Res1).or_else(|e| if e.len() < 4 { Ok(e.len() as i32) } else { Err(e) }));
assert_eq!(Ok(3), (Err("abc") as Res1).or_else(|e| if e.len() < 4 { Ok(e.len() as i32) } else { Err(e) }));
assert_eq!(Err("abcde"), (Err("abcde") as Res1).or_else(|e| if e.len() < 4 { Ok(e.len() as i32) } else { Err(e) }));
std::error
トレイト Traits
-
Error
-
fn description(&self) -> &str
- 必須メソッド。
-
std::io
読み書き関連。
モジュール Modules
-
prelude
- 専ら
use std::io::prelude::*;
で使う。
- 専ら
構造体 Structs
カプセラー構造体2
-
BufReader
- リーダー型を包み、内部バッファーを用いるリーダー型とするための構造体。-
BufRead
トレイトも自動的に実装される。
-
-
BufWriter
- ライター型を包み、内部バッファーを用いるライター型とするための構造体。 -
LineWriter
- ライター型を包み、一行おきにフラッシュを行うライター型とするための構造体。 -
Cursor
- 適当な型を包んで トレイトSeek
を実装するための構造体。-
Cursor<T> where T: AsRef<[u8]>
については、既にトレイトSeek
の実装が済んでいる。
-
イテレータ型
-
Bytes
- トレイトRead
のメソッドbytes()
で生成される、リーダーから1バイトずつ読み込み、バイト文字u8
を返すイテレータ型。 -
Lines
- 構造体BufReader
のメソッドlines()
で返される、一行ずつに分割されたBufRead
を返すイテレータ型。 -
Split
- 構造体BufReader
のメソッドsplit()
で返される、与えたバイト文字で分割されたBufRead
を返すイテレータ型。
リーダー/ライター型
-
Chain
- トレイトRead
のメソッドchain()
で生成される、[Buf]リーダーの読み込み終了後に与えた[Buf]リーダー型の読み込みが始まる[Buf]リーダー型。 -
Take
- トレイトRead
のメソッドtake()
で生成される、現在地から与えたバイト数分のデータを返す[Buf]リーダー型。 -
Repeat
- 関数repeat()
で生成される、与えられたバイト文字を無限に繰り返すリーダー型。 -
Empty
- 関数empty()
で生成される、常にEOF
を返す、空のリーダー型。 -
Sink
- 関数sink()
で生成される、dev/null
と似た出力のゴミ捨て場となるライター型。正しい書き込みバイト数を返す。 -
Stdin
- 標準入力構造体 -
Stdout
- 標準出力構造体 -
Stderr
- 標準エラー出力構造体 StdinLock
StdoutLock
StderrLock
その他
Error
IntoInnerError
-
Chars
- [Unstable]
トレイト Traits
-
Read
- 読み-
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
- 必須メソッド。データをバッファーに読み込み、読んだバイト数をResult
に包んで返す。
-
-
BufRead
- 内部バッファを持つリーダー。トレイトRead
を継承している。-
fn fill_buf(&mut self) -> Result<&[u8]>
- 必須メソッド。内部バッファーにデータを読み込み、そのバッファーの&[u8]
スライスをResult
に包んで返す。 -
fn consume(&mut self, amt: usize)
- 必須メソッド。与えられたバイト数分、内部バッファーからデータを捨てる?BufReader
の実装では、内部のカーソルを進めるだけ。
-
-
Write
- 書き-
fn write(&mut self, buf: &[u8]) -> Result<usize>
- 必須メソッド。与えられたバッファーのデータを書き込み、書き込みに成功したバイト数をResult
に包んで返す。 -
fn flush(&mut self) -> Result<()>
- 必須メソッド。書き込みストリームをフラッシュする。
-
-
Seek
- カーソル移動-
fn seek(&mut self, pos: SeekFrom) -> Result<u64>
- 必須メソッド。与えられたポジションにシークし、先頭からの新しい位置をResult
に包んで返す。
-
列挙型 Enums
-
ErrorKind
-io
モジュールで用いられるエラー型。 -
SeekFrom
- シークに用いられる列挙型。-
Start(u64)
- 先頭からのバイト数。 -
End(i64)
- 終端からのバイト数。 -
Current(i64)
- 現在地点からのバイト数。
-
-
CharsError
- [Unstable]
関数 Functions
-
copy
- 与えたリーダー型を読み込み、EOF
を読むまで与えたライター型に書き込む関数。書き込みバイト数をResult
に包んで返す。 -
repeat
- 与えたバイト文字を繰り返す、リーダー型Repeat
のインスタンスを生成。 -
empty
- 空のリーダー型Empty
のインスタンスを生成。 -
sink
- 書き込みの捨て場となるライター型Sink
のインスタンスを生成。 -
stdin
- 標準入力構造体Stdin
のインスタンスを生成。 -
stdout
- 標準出力構造体Stdout
のインスタンスを生成。 -
stderr
- 標準エラー出力構造体Stderr
のインスタンスを生成。
std::fmt
format!
,println!
,print!
等で使われるフォーマッティング関連。
フォーマッティング
Formatting Parameters
トレイト Traits
フォーマッティングトレイト Formatting traits
フォーマッティングの際に呼び出されるトレイト。
-
Display
-{}
-
Debug
-{:?}
-
Binary
-{:b}
-
UpperExp
-{:E}
-
LowerExp
-{:e}
-
UpperHex
-{:X}
-
LowerHex
-{:x}
-
Octal
-{:o}
-
Pointer
-{:p}
I/Oトレイト
関数 Functions
std::cmp
相当性 (equality), 順序性 (order)、部分相当 (partial equality)、部分順序 (partial order) 関係。
列挙型 Enums
-
Ordering
- 順序関係を示す。Ordering::Less
Ordering::Equal
Ordering::Greater
トレイト Traits
-
PartialEq
-
fn eq(&self, other: &Rhs) -> bool
- 必須メソッド。二項演算子==
-
fn ne(&self, other: &Rhs) -> bool
- 二項演算子!=
-
-
PartialOrd
-
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>
- 必須メソッド -
fn lt(&self, other: &Rhs) -> bool
- 二項演算子<
-
fn le(&self, other: &Rhs) -> bool
- 二項演算子<=
-
fn gt(&self, other: &Rhs) -> bool
- 二項演算子>
-
fn ge(&self, other: &Rhs) -> bool
- 二項演算子>=
-
-
Eq
- トレイト
PartialEq
の二項関係==
が、「反射律」・「対称律」・「推移律」を完全に満たすとき、このトレイトの実装を宣言する。
- トレイト
-
Ord
- トレイト
PartialOrd
の二項関係<
,<=
,>
,>=
が、「完全律」・「反対称律」・「推移律」を完全に満たすとき、このトレイトの実装を宣言する。
- トレイト
関数 Functions
引数2つの最大値/最小値を返す関数。
std::iter
イテレータ関係。
構造体 Structs
以下全てイテレータ型。
トレイトIterator
のメソッドで生成されるイテレータ型。
-
Chain
- トレイトIterator
のメソッドchain()
で生成される、与えたイテレータを尻に結合したイテレータ型。 -
Cloned
- トレイトIterator
のメソッドcloned()
で生成される、複製されたイテレータ型。 -
Cycle
- トレイトIterator
のメソッドcycle()
で生成される、循環イテレータ型。 -
Enumerate
- トレイトIterator
のメソッドenumerate()
で生成される、イテレータの順番と要素をタプルで返すイテレータ型。 -
Filter
- トレイトIterator
のメソッドfilter()
で生成される、与えた述語FnMut(&Self::Item) -> bool
によりフィルターされた要素を返すイテレータ型。 -
FilterMap
- トレイトIterator
のメソッドfilter_map()
で生成される、与えた述語FnMut(Self::Item) -> Option<B>
で返された非None
要素を返すイテレータ型。 -
FlatMap
- トレイトIterator
のメソッドflat_map()
で生成される、与えた述語FnMut(Self::Item) -> U,
が返すU: IntoIterator
をフラットにして作られたイテレータ型。 -
Fuse
- トレイトIterator
のメソッドfuse()
で生成される、最初にNone
を返した時点で終了する(それ以降None
を返し続ける)イテレータ型。 -
Inspect
- トレイトIterator
のメソッドinspect()
で生成される、要素を取り出すとき、与えた関数F: FnMut(&Self::Item) -> ()
が適用されるイテレータ型。 -
Map
- トレイトIterator
のメソッドmap()
で生成される、与えた写像FnMut(Self::Item) -> B
により変換された結果を返すイテレータ型。 -
Peekable
- トレイトIterator
のメソッドpeekable()
で生成される、要素ののぞき見peek()
が可能なイテレータ型。 -
Rev
- トレイトIterator
のメソッドrev()
で生成される、逆順イテレータ。逆順に要素を取得できるDoubleEndedIterator
にのみ可能。 -
Scan
- トレイトIterator
のメソッドscan()
で生成される、与えた初期値と述語F: FnMut(&mut St, Self::Item) -> Option<B>
が返す要素を順次返すイテレータ型。メソッドfold()
に似ている。 -
Skip
- トレイトIterator
のメソッドskip()
で生成される、最初のn個の要素をスキップするイテレータ型。 -
SkipWhile
- トレイトIterator
のメソッドskip_while()
で生成される、与えた述語がP: FnMut(&Self::Item) -> bool
がtrue
を返す要素をスキップするイテレータ型。false
が返された時点でスキップは終了。 -
Take
- トレイトIterator
のメソッドtake()
で生成される、最初のn個の要素を取り出すイテレータ型。 -
TakeWhile
- トレイトIterator
のメソッドtake_while()
で生成される、与えた述語がP: FnMut(&Self::Item) -> bool
がtrue
を返す要素を取り出すイテレータ型。false
が返された時点で取り出しは終了。 -
Zip
- トレイトIterator
のメソッドzip()
で生成される、与えたイテレータとの2つの要素を順次タプルを返すイテレータ型。返される要素の数は、2つのイテレータの最小数。
関数で生成されるイテレータ型。
-
Empty
- 関数std::iter::empty
が返す空イテレータ型。 -
Once
- 関数std::iter::once()
で生成される、1要素イテレータ型。 -
Repeat
- 関数std::iter::repeat
で生成される、1要素を無限に繰り返す循環イテレータ型。
トレイト Traits
-
Iterator
- イテレータの満たすべきトレイト-
fn next(&mut self) -> Option<Self::Item>
- 必須メソッド
-
-
DoubleEndedIterator
- 両端イテレータ-
fn next_back(&mut self) -> Option<Self::Item>
- 必須メソッド。 -
fn for_each<F>(self, f: F) where F: FnMut(Self::Item) -> ()
- イテレータの各要素について引数のクロージャを回す。副作用が望みの時用。
-
-
FromIterator
- イテレータからの変換。 -
IntoIterator
- イテレータへの変換。-
self
を消費 (consume) してイテレータへと変換されることに注意。-
self
を借用して要素の参照のイテレータを返すiter()
メソッドとの違いに注意。
-
-
-
ExactSizeIterator
- 正確な大きさを与えるイテレータ。 -
Extend
- イテレータに引数のイテレータを付け加える。-
fn extend<T>(&mut self, iter: T)
- 必須メソッド。
-
関数 Functions
-
empty
- 空のイテレータ型Empty
のインスタンスを生成。 -
once
- 1要素のイテレータ型Once
のインスタンスを生成。 -
repeat
- 1要素を無限に繰り返す循環イテレータ型、Repeat
のインスタンスを生成。
std::collections
モジュール Modules
構造体 Structs
-
BTreeMap
- B木連想配列 -
BTreeSet
- B木集合 -
BinaryHeap
- 二分ヒープ -
HashMap
- ハッシュマップ -
HashSet
- ハッシュ集合 -
LinkedList
- 連結リスト -
VecDeque
- 両端キュー
列挙型 Enum
-
Bound
- [Unstable]
std::hash
ハッシュ関係。
トレイト Traits
-
Hash
-HashMap
,HashSet
で用いられるハッシュを与える。-
fn hash<H>(&self, state: &mut H) where H: Hasher
- 必須メソッド。state
にハッシュを与え更新する。
-
-
Hasher
- ハッシュを計算・保持するためのトレイト。-
fn finish(&self) -> u64
- 計算したハッシュ結果を返す。 -
fn write(&mut self, bytes: &[u8])
- バイト列を受け取り内部ハッシュを更新する。
-
BuildHasher
std::fs
ファイル操作関連。
構造体 Structs
-
File
- ファイル読み書き。C言語のFILE
的な。-
std::io
のRead
,Write
,Seek
トレイトを実装しており、読み書きシークが可能。
-
-
ReadDir
- ディレクトリ読み込み。 DirBuilder
DirEntry
-
FileType
- 構造体DirEntry
,Metadata
のメソッドfile_type()
により生成されるファイルタイプ型。 -
Metadata
- 関数std::fs::metadata()
において、パスを与えると得られるメタデータ型。 OpenOptions
Permissions
関数 Functions
-
canonicalize
- 与えたパスを正規化してstd::io::Result
で包んで返す。 -
copy
- 与えたfrom
パスにあるファイルをto
パスにコピーする(上書きする)。コピーしたバイト数をstd::io::Result
で包んで返す。 -
create_dir
- 与えたパスにディレクトリを作成する。 -
create_dir_all
- 与えたパスにディレクトリを再帰的に作成する。 -
hard_link
- 与えたsrc
パスのファイルをdst
パスにハードリンクを作る。 -
metadata
- 与えたパスからメタデータ型Metadata
を返す。 -
read_dir
- 与えたパスのディレクトリからReadDir
をstd::io::Result
で包んで返す。 -
read_link
- 与えたパスのシンボリック・リンクから示しているパスPathBuf
を取得し、std::io::Result
で包んで返す。 -
remove_dir
- 与えたパスの(空の)ディレクトリを削除して、std::io::Result<()>
を返す。 -
remove_dir_all
- 与えたパスのディレクトリを中身ごと削除して、std::io::Result<()>
を返す。 -
remove_file
- 与えたパスのファイルを削除して、std::io::Result<()>
を返す。 -
rename
- 与えたfrom
パスにあるファイルをto
パスにリネームする、std::io::Result<()>
を返す。 -
set_permissions
- 与えたパスのファイル/ディレクトリのパーミッションを設定し、std::io::Result<()>
を返す。 -
soft_link
- [Deprecated] -
symlink_metadata
- 与えたパスを、シンボリックリンクを辿ることなくメタデータ型Metadata
を取得しstd::io::Result
で包んで返す。
std::path
構造体 Structs
-
Path
- パスのスライス(文字列における&str
と同類) -
PathBuf
- 可変なパス(文字列におけるString
と同類) -
Iter
- パスの各部分パス (OsStr
) を走るイテレータ。 -
Components
- パスの各コンポーネント (enum Component
) を走るイテレータ。 Display
PrefixComponent
StripPrefixError
列挙型 Enums
-
Component
- パスのコンポーネントを表す。Prefix(PrefixComponent<'a>)
RootDir
CurDir
ParentDir
Normal(&'a OsStr)
-
Prefix
- Windows only のパスのプレフィックス(C:
など)
定数 Constants
-
MAIN_SEPARATOR
- (現在のプラットフォームでの)パス・セパレータ。
関数 Functions
-
is_separator
- 与えた文字型c
が現在のプラットフォームでファイルパスのセパレータであるか判別する。
std::ffi
FFI (Foreign function interface) 関連。
構造体 Structs
std::slice
構造体 Structs
以下全てイテレータ型
-
Chunks
- プリミティブ型slice
のメソッドchunks()
において生成される、与えた個数分イテレータの要素をタプルにして返すイテレータ型。 -
ChunksMut
- プリミティブ型slice
のメソッドchunks_mut()
において生成される、与えた個数分イテレータの要素をmutableなタプルにして返すイテレータ型 -
Iter
- 様々な構造体のメソッドiter()
において生成される、イテレータ型。 -
IterMut
- 様々な構造体のメソッドiter_mut()
において生成される、mutableなイテレータ型。 -
Split
- 様々な構造体のsplit()
メソッドにおいて生成される、与えた述語F: FnMut(&T) -> bool
がtrue
の要素を排除し、残された部分をスライスして返すイテレータ。 -
SplitMut
- 様々な構造体のメソッドsplit_mut()
において生成される、与えた述語F: FnMut(&T) -> bool
がtrue
の要素を排除し、残された部分を mutable なスライスにして返すイテレータ型。 -
SplitN
- 様々な構造体のメソッドsplitn()
において生成される、与えた述語F: FnMut(&T) -> bool
がtrue
の要素を取り除き、取り除いた箇所で分割し与えた数n個のスライスにして返すイテレータ型。 -
SplitNMut
- 様々な構造体のメソッドsplitn_mut()
において生成される、与えた述語F: FnMut(&T) -> bool
がtrue
の要素を取り除き、取り除いた箇所で分割し与えた数n個の mutable なスライスにして返すイテレータ型。 -
RSplitN
- 様々な構造体のメソッドrsplitn()
において生成される、SplitN
の逆順イテレータ型。 -
RSplitNMut
- 様々な構造体のメソッドrsplitn_mut()
において生成される、mutableな逆順イテレータ型。 -
Windows
- プリミティブ型slice
のメソッドwindows()
において生成される、イテレータの要素配列を与えたn個のタプルにして順次返すイテレータ型。nがイテレータ要素より大きいとき、このWindows
イテレータは値を返さない。
トレイト Traits
-
SliceConcatExt
- [Unstable]
関数 Functions
-
from_raw_parts
- 与えられたポインタとサイズから、スライス参照を生成する。unsafe fn from_raw_parts<T>(p: *const T, len: usize) -> &'a [T]
from_raw_parts_mut
std::mem
メモリ関連。
定数 Constants
[Unstables]
関数 Functions
-
replace
- mutable な場所にある値を、与えられた新しい値に代入し、古い値を返す。 -
swap
- mutable な場所にある2つの値を交換する。 -
unreachable
- [Experimental] 未定義動作を起こすことで、この関数が用いられる箇所にたどり着くコードが存在しないとコンパイラに指示し、その様に最適化が行われる。-
unreachable!
マクロとは全くの別物である。
-
型サイズ/変数配置/型情報についての関数
-
size_of
,size_of_val
- 与えた型/引数のサイズ(バイト)を返す -
align_of
,align_of_val
- 与えた型/引数の最小アライメントを返す。 -
discriminant
- 列挙型T
の参照を引数に取り、列挙型の各ヴァリアントについてユニークな値Discriminant<T>
を返す。
use std::mem::{size_of, size_of_val, align_of, align_of_val, needs_drop};
assert_eq!(size_of::<()>(), 0);
assert_eq!(size_of::<char>(), 4);
assert_eq!(size_of::<i64>(), 8);
assert_eq!(size_of_val(b"1234567890"), 10);
assert_eq!(size_of_val(&0u8), 1);
assert_eq!(size_of_val(&0.0f32), 4);
assert_eq!(align_of::<()>(), 1);
assert_eq!(align_of::<[u8; 4]>(), 1);
assert_eq!(align_of::<i16>(), 2);
assert_eq!(align_of_val(&0u8), 1);
assert_eq!(align_of_val(&0u16), 2);
drop
関係関数
-
forget
- デストラクタの呼び出し等を回避する。- 危険な
std::mem::uninitialized()
で作った構造体を最終処理するときなどに使用。
- 危険な
-
drop
- 値を処分(強制Drop
)する。Copy
を実装している型には作用しない(関数引数に move して値を処分するため)。 -
needs_drop
-Drop
が必要か否かを返す。最適化に用いる。
use needs_drop;
assert!(needs_drop::<i32>(), false);
assert!(needs_drop::<&str>(), false);
assert!(needs_drop::<String>(), true);
assert!(needs_drop::<Box<i32>>(), true);
unsafe
な関数
-
transmute
- 型を変換する。サイズはチェックするが、不完全。 -
transmute_copy
- 型を変換してコピー。 -
uninitialized
- デフォルトの初期化チェッカーを迂回する。 -
zeroed
- 指示した型についてゼロフィルされた値を返す。
構造体 Structs
-
Discriminant
- 列挙型の各ヴァリアントを区別する不透過型。Eq
やHash
等のトレイトを実装する。関数discriminant
の返値。
#[derive(Debug)]
enum Foo {
A(&'static str),
B(i8),
C(i8),
}
fn main() {
use Foo::*;
use std::mem::discriminant;
println!("{:?}", discriminant(&A("Hello"))); // Discriminant(0)
println!("{:?}", discriminant(&A("World"))); // Discriminant(0)
println!("{:?}", discriminant(&B(0))); // Discriminant(1)
println!("{:?}", discriminant(&B(1))); // Discriminant(1)
println!("{:?}", discriminant(&C(0))); // Discriminant(2)
println!("{:?}", discriminant(&C(1))); // Discriminant(2)
struct Bar(u8, u8);
println!("{:?}", discriminant(&Bar(0, 1))); // Discriminant(0)
println!("{:?}", discriminant(&Bar(2, 3))); // Discriminant(0)
}
共用体 Unions
std::ptr
生ポインタ操作。
構造体 Structs
[Unstable]
関数 Functions
-
copy
- C言語のmemcpy
的な。 copy_nonoverlapping
-
drop_in_place
- デストラクタを強制実行する。 -
null
- ヌルポインタを作る。 -
null_mut
- mutable なヌルポインタを作る。 -
read
- ポインタから値を読み込む。 read_volatile
-
replace
- mutable な場所にある値を、与えられた新しい値に代入し、古い値を返す。- 生ポインタを扱う点以外は、
std::mem::replace
とほぼ同じ。
- 生ポインタを扱う点以外は、
-
swap
- mutable な場所にある2つの値を交換する。 -
write
- ポインタに値を書き込む。 write_bytes
write_volatile
-
read_and_drop
- [Unstable]
std::boxed
ヒープ上にアロケイトされた型。
構造体 Structs
-
Box
- 与えた型をヒープ上にアロケイトし包む。 -
ExchangeHeapSingleton
- [Unstable] -
IntermediateBox
- [Unstable]
定数 Constants
[Unstable]
トレイト Traits
-
FnBox
- [Unstable]
std::cell
構造体 Structs
スマートポインタ型
-
Cell
-Copy
を実装した型の容れ物。 Ref
RefCell
RefMut
UnsafeCell
エラー型
列挙型 Enums
-
BorrowState
- [Unstable]
std::rc
構造体 Structs
std::borrow
列挙型 Enums
-
Cow<'a, B>
- COW (clone-on-write) を行うスマートポインタ型Borrowed(&'a B)
Owned(<B as ToOwned>::Owned)
トレイト Traits
-
Borrow
- 借用の抽象化。See BorrowとAsRef -
BorrowMut
- 同上 -
ToOwned
-Clone
と似ているが、Borrow
な関係にある型間の変換をも行う(str
->String
等の)
std::clone
トレイト Traits
-
Clone
- 明示的にオブジェクトを複製するためのトレイト。
std::marker
構造体 Structs
-
PhantomData
- C++のテンプレート黒魔術みたく、コンパイル時に色々とするために使う?
トレイト Traits
-
Copy
- Special traits の一つ。ビットコピー時に使う?move
ではなくcopy
して値を使うときに用いる。専ら#[derive(Copy)]
で実装? -
Sized
- Special traits の一つ。コンパイル時に構造体のサイズが固定値で決定していることを意味する。- ジェネリクスで
<T: Sized>
,<T: ?Sized>
のような制約で見かける。 -
?Sized
は「Sized でも Sized じゃなくてもいいよ、気にしないよ」らしい。
- ジェネリクスで
-
Send
- ? -
Sync
- ? -
Reflect
- [Unstable] -
Unsize
- [Unstable]
std::default
トレイト Traits
-
Default
-Default::default()
でデフォルト値を一律に扱う。
std::env
モジュール Modules
-
consts
- 現在のターゲットについての定数群を持つモジュール。
構造体 Structs
-
Args
- プロセスの引数をOption<String>
で返すイテレータ。 -
ArgsOs
- 上のString
をOsString
で。 -
Vars
- 環境変数をOption<(String, String)>
で返すイテレータ。 -
VarsOs
- 上のString
をOsString
で。 -
JoinPathsError
- 関数join_paths
で失敗したときに返る -
SplitPaths
- パスが:
で結合したPATH
環境変数をパースして、分割されたパスをOption<PathBuf>
で返すイテレータ。
列挙型 Enums
-
VarError
-std::env::*
の関数群で使われるエラー型。
関数 Functions
-
args
-Args
を返す。 -
args_os
-ArgsOs
を返す。 -
vars
-Vars
を返す。 -
vars_os
-VarsOs
を返す。 -
var
- 与えたキーから環境変数の内容をResult<String, VarError>
で返す。 -
var_os
- 上のString
をOsString
で。 -
current_dir
- カレント・ワーキング・ディレクトリのパスをResult<PathBuf>
で返す。 -
set_current_dir
- カレント・ワーキング・ディレクトリのパスをセットする。 -
current_exe
- 実行ファイルのパスをResult<PathBuf>
で返す。 -
home_dir
- ホームディレクトリのパスをOption<PathBuf>
で返す。 -
join_paths
-PATH
環境変数にパスを追加する。 -
set_var
- 環境変数を追加する。 -
remove_var
- 環境変数を削除する。 -
split_paths
-AsRef<OsStr>
なパスをSplitPaths
に分割する。 -
temp_dir
- テンポラリ・ディレクトリのパスをPathBuf
で返す。
std::any
構造体 Structs
-
TypeId
- 型のユニークIDを表す。
トレイト Traits
-
Any
- 動的型付けをエミュレイトするためのトレイト。-
fn get_type_id(&self) -> TypeId
- [Unstable]必須メソッド。
-
std::num
数値型の追加関連。
構造体 Structs
-
ParseFloatError
-f32
やf64
によるトレイトstd::str::FromStr
の実装におけるエラー型。 -
ParseIntError
- 関数i8::from_str_radix
におけるエラー型。 -
Wrapping
- オーバーフローしないよう数値型をラップする型。(ただしゼロ除算ではpanicする) -
TryFromIntError
- [Unstable]
列挙型 Enums
-
FpCategory
-f64
,f32
のメソッドclassify()
が返す、浮動小数点数の分類のための列挙型。Nan
Infinite
Zero
Subnormal
Normal
トレイト Traits
[Deprecated],[Unstable]
std::time
構造体 Structs
-
Duration
- 時間 (span of time) を表す型。粒度はナノセカンド。 -
Instant
- 時間経過を測る型。Duration
を返す。 -
SystemTime
- システムクロックを測る。 -
SystemTimeError
-SystemTime::duration_since()
で使われるエラー型。
定数 Constants
-
UNIX_EPOCH - エポックタイムを
SystemTime
型で返す。
std::sync
モジュール Modules
構造体 Structs
-
Arc
-Rc
のアトミック版。 Barrier
BarrierWaitResult
Condvar
Mutex
MutexGuard
Once
PoisonError
RwLock
RwLockReadGuard
RwLockWriteGuard
WaitTimeoutResult
Weak
OnceState
StaticCondvar
StaticMutex
StaticRwLock
列挙型 Enums
定数 Constants
-
ONCE_INIT
-Once
の初期値。
型定義 Type Definitions
-
LockResult
type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
-
TryLockResult
type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
std::thread
構造体 Structs
-
Builder
- スレッド・ビルダー -
JoinHandle
- LocalKey
Thread
関数 Functions
-
current
- 現在のスレッドThread
を返す。 -
panicking
- スレッドがpanicより巻き戻って (unwinding) しているかを返す。 -
park
- スレッドを一時停止 (park) させる。 -
park_timeout
- -
park_timeout_ms
- [Deprecated] -
sleep
- 与えた時間 (Duration
型) スリープする。 -
sleep_ms
- [Deprecated] -
spawn
- 新しいスレッドを作り、JoinHandle
を返す。 -
yield_now
- OSのスレッド・スケジューラーに対し、(現在のスレッドの)タイムスライスを終了させる。
use std::{thread, time};
let a_sec = time::Duration::from_secs(1);
println!("Wait a second.");
thread::sleep(a_second);
println!("Now.");
型定義 Type Definitions
-
Result
-JoinHandle::join()
で使われる。type Result<T> = Result<T, Box<Any + Send + 'static>>;
std::net
TCP/UDP 通信関連。
std::sync::atomic
構造体 Structs
列挙型 Enums
-
Ordering
Relaxed
Release
Acquire
AcqRel
SeqCst
定数 Constants
関数 Functions
-
fence - 順序
atomic::Ordering
を与えフェンスとして働く。
std::sync::mpsc
複数プロデューサー・単独コンシューマーの、FIFO (First-In-First-Out) キュー伝達関係。
構造体 Structs
IntoIter
Iter
Receiver
RecvError
SendError
Sender
SyncSender
-
Handle
- [Unstable] -
Select
- [Unstable]
列挙型 Enums
TryRecvError
TrySendError
-
RecvTimeoutError
- [Unstable]
関数 Functions
-
channel
- 新しい非同期チャンネル(Sender<T>, Receiver<T>)
を返す。 -
sync_channel
- 新しい同期チャンネル(SyncSender<T>, Receiver<T>)
を返す。
サンプル
型名表示
デバッグのため型名を表示したいときは、関数std::intrinsics::type_name
を使う。
関数type_name()
は与えられた型パラメータT
の型名を&'static str
で返す。
#![feature(core_intrinsics)]
fn get_type_of<T>(_: &T) -> &'static str {
unsafe { std::intrinsics::type_name::<T>() }
}
let a = &10.0f32;
println!("{:?}'s type name is {}", a, get_type_of(&a));
型変換
失敗するかも知れない変換は、std::convert::TryFrom
トレイトを使う。
#![feature(try_from)]
use std::convert::TryFrom;
println!("{:?}", u8::try_from(32i32));
数値型から C-like 列挙型へ
#[derive(Debug, Copy, Clone)]
enum Color {
Red = 0xFF0000,
Green = 0x00FF00,
Blue = 0x0000FF,
}
use std::convert::TryFrom;
impl TryFrom<u32> for Color {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
use Color::*;
let color = match unsafe { std::mem::transmute(value) } {
Red => Red,
Green => Green,
Blue/* means _ */ => Blue,
};
if value == color as u32 {
Ok(color)
} else {
Err(format!("can't convert 0x{:08X} to Color", value))
}
}
}
カスタム範囲..
構文実装
Step
トレイトを実装させることで、任意の構造体について範囲構文がイテレータとなる。
#![feature(step_trait)]
#[derive(PartialEq,PartialOrd,Clone,Debug)]
struct Foo {
val: i32,
}
impl Foo {
fn new(v: i32) -> Self {
Self { val: v }
}
}
use std::iter::Step;
impl Step for Foo {
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if start <= end {
Some((end.val - start.val) as usize)
} else {
None
}
}
fn replace_one(&mut self) -> Self {
self.val = 1;
self.clone()
}
fn replace_zero(&mut self) -> Self {
self.val = 0;
self.clone()
}
fn sub_one(&self) -> Self {
Self::new(self.val - 1)
}
fn add_one(&self) -> Self {
Foo::new(self.val + 1)
}
fn add_usize(&self, n: usize) -> Option<Self> {
Some(Foo::new(self.val + n as i32))
}
}
fn main() {
let x: std::ops::Range<Foo> = Foo::new(1) .. Foo::new(10);
for a in Foo::new(0) .. Foo::new(4) {
println!("{:?}", a);
}
}
関数呼び出し演算子オーバーロード
FnOnce
-> FnMut
-> Fn
の順での実装が必要とされる。
以下では Args=(&String,)
という、&String
のみを引数とする関数を実装している。
-
FnOnce
のみの実装の場合、self
は消費 (consume) されるので、続けて関数呼び出しを行うことは出来ない。- 呼び出されるのは、当然
call_once
- 呼び出されるのは、当然
-
FnOnce
,FnMut
のみの実装の場合、複数回関数呼び出しは出来るが、let mut foo = ...
とする必要がある。- 呼び出されるのは、全て
call_mut
-
let foo = ...
でcall_once
を呼ぶことは出来ない。-
Foo::call_once(foo, (&text,));
でcall_once
を呼ぶことは出来る。
-
- 呼び出されるのは、全て
-
FnOnce
,FnMut
,Fn
全て実装の場合、複数回関数呼び出しが出来る- 呼び出されるのは、全て
call
。-
let mut foo = ...
としても、call_mut
は呼び出されない。-
Foo::call_mut(&mut foo, (&text,));
でcall_mut
を呼ぶことは出来る。 -
Foo::call_once(foo, (&text,));
でcall_once
を呼ぶことは出来る。
-
-
foo(&text)
の代わりに、Foo::call(&foo, (&text,));
で呼ぶことが出来る。
-
- 呼び出されるのは、全て
#![feature(fn_traits, unboxed_closures)]
use std::ops::{FnOnce, FnMut, Fn};
struct Foo { }
impl<'a> FnOnce<(&'a String,)> for Foo {
type Output = ();
extern "rust-call" fn call_once(self, (arg,): (&String,)) -> Self::Output {
println!("Hello {}! at once", arg);
}
}
impl<'a> FnMut<(&'a String,)> for Foo {
extern "rust-call" fn call_mut(&mut self, (arg,): (&String,)) -> Self::Output {
println!("Hello {}! as mut", arg);
}
}
impl<'a> Fn<(&'a String,)> for Foo {
extern "rust-call" fn call(&self, (arg,): (&String,)) -> Self::Output {
println!("Hello {}!", arg);
}
}
fn main() {
let foo = Foo { };
let text = "World".to_string();
foo(&text); // Hello World!
Foo::call(&foo, (&text,)); // Hello World!
Foo::call_mut(&mut foo, (&text,)); // Hello World! as mut
Foo::call_once(foo, (&text,)); // Hello World! at once
}
1要素タプル構造体を用いた、非自前構造体のメソッド実装
標準ライブラリなどの外部に書かれたカスタム型には、自分ではメソッドを書くことが出来ない。
そこで、その型の参照を包んだ1要素タプル構造体を作ることで、最小のコストでメソッドを追加することが出来る。参考: How to print! an Option>?
例として、Option<Box<T>>
型のラッパーを書く。これはわざわざラッパーを書くまでもない例だが、参考までに。
struct Node<T> {
val: T,
node: Option<Box<Node<T>>>,
}
struct Wrapper<'a, T: 'a>(&'a Option<Box<T>>);
impl<T> Node<T> {
fn new(val: T, node: Option<Box<Node<T>>>) -> Self {
Node {
val,
node,
}
}
fn node_wrapper(&self) -> Wrapper<Self> {
Wrapper(&self.node)
}
}
use std::fmt::*;
impl<T> Display for Node<T> where T: Display {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{{val: {}, node: {}}}", self.val, self.node_wrapper()).unwrap();
Ok(())
}
}
impl<'a, T> Display for Wrapper<'a, T> where T: Display {
fn fmt(&self, f: &mut Formatter) -> Result {
match *(self.0) {
Some(ref body) => write!(f, "{}", body),
None => write!(f, "none!"),
}.unwrap();
Ok(())
}
}
fn main() {
let node = Node::new(1, None);
let node = Node::new(2, Some(Box::new(node)));
println!("{}", node);
}
構造体のスライス変換
use std::ops;
use std::mem;
#[derive(Debug)]
struct AType {
a: i32,
b: i32,
}
impl ops::Deref for AType {
type Target = [i32];
fn deref(&self) -> &Self::Target {
let array: &[i32; 2] = unsafe { mem::transmute(self) };
&array[..]
}
}
fn main() {
let a = AType { a: 10, b: 20 };
println!("a: {:?}", a);
let b: &[i32] = &a;
println!("b: {:?}", b);
}
unreachable()
を用いた最適化
std::mem::unreachable
を用いて最適化が行えるのは上で説明したが、その実際のサンプル。
#![feature(unreachable)]
#![crate_type = "lib"]
use std::mem;
pub fn unreachable_test_fn(v: i32) -> i32 {
let rem = v % 3;
if rem == 0 {
577
} else if v == 1 {
2999
} else {
unsafe {
mem::unreachable();
}
}
}
このテスト関数に最適化を行うと、if rem == 0 { 577 } else { 2999 }
と変換されることが期待される。
extern crate unreachable_test;
use self::unreachable_test::unreachable_test_fn;
fn main() {
println!("f(0) = {}", unreachable_test_fn(0));
println!("f(1) = {}", unreachable_test_fn(1));
println!("f(2) = {}", unreachable_test_fn(2));
println!("f(3) = {}", unreachable_test_fn(3));
println!("f(4) = {}", unreachable_test_fn(4));
println!("f(5) = {}", unreachable_test_fn(5));
}
-
-O
オプションは最適化 (-C opt-level=2
) を指示する。- 最適化を指示しない場合、
mem::unreachable()
に達すると stack overflow 等のエラーが生じる。
- 最適化を指示しない場合、
-
-L
オプションはライブラリの有る場所を指示する。
$ rustc -O unreachable_test.rs
$ rustc -L . unreachable_main.rs
$ ./unreachable_main
f(0) = 577
f(1) = 2999
f(2) = 2999
f(3) = 577
f(4) = 2999
f(5) = 2999
期待通りの挙動が示された。