Rust Memo
Rust 本家
Rust本家
ドキュメント
標準ライブラリのリファレンス
Playground
Rust By Example (RBE)
Introduction - Rust By Example
.
この記事は主に RBE をベースにしている。
Rustインストール後は rustup doc
でローカルのドキュメントをブラウザで表示できる。
Hello World など
Hello World
fn main() { pirntln!("Hello World!"); }
FizzBuzz
fn main() { fizz_buzz(); } fn fizz_buzz() { for i in 1..=100 { let mut s = ""; if i % 15 == 0 { s = "FizzBuzz"; } else if i % 3 == 0 { s = "Fizz" } else if i % 5 == 0 { s = "Buzz" } if (s.len() != 0) { println!("{}: {}", i, s); } } }
Rustc
rustc は、Rust のコンパイラ。
通常は直接使わず、 cargo 経由で実行される。
Cargo
cargo (カーゴ)は Rust のプログラムをビルドするツール。
Rust の外部パッケージ(crate (クレート)と呼ばれる)のパッケージマネージャでもある。
crate は https://crates.io で公開されている。
Cargo.toml に、追加で必要な crate を記述しておくと cargo はビルドに先立ち crate.io からその crate をダウンロードする。
(※ 自分で書くプログラムも crate と言う)
$ cargo new my_app # 作業環境の作成 $ cargo new --lib my_lib # ライブラリ作成用作業環境の作成 $ cd my_app $ cargo build # デバッグビルド ==> target/debug/my_app ができる。 $ cargo build --release # リリースビルド ==> target/release/my_app ができる $ cargo check # ビルドせず、コードのチェックだけしてビルドはしない。 $ cargo run # 実行 (./target/debug/my_app でも実行できる) $ cargo doc # target/doc にドキュメントを生成 $ cargo -h # cargo の使い方 $ cargo build -h # cargo build の使い方
コメント
コメント
// ラインコメント /* ブロックコメント */ /* * ブロックコメント */
Doc Comment
///
で Doc コメントを書くことができる。 関数や、構造体に Doc コメントを書いておくと、cargo doc
で生成されるドキュメントにその記述が反映される。
生成される場所: target/doc/my_app/index.html
Doc コメント内では Markdown がサポートされる。
Documentation - Rust By Example
/// 四角形を表す構造体 struct Rectangle { left: u32, top: u32, width: u32, height: u32, }
文字列のフォーマットとプリント
Rust では、コンソールへの出力や、文字列のフォーマットは println! や format! といったマクロを使う。(! がつくものはマクロ)
Rust の関数は現状可変個引数に対応してないのでマクロでやるしかない。
format!
String 型の文字列にフォーマット結果を書き込んで返す
print!
format! と同じだけど、標準出力にプリントする
println!
print! と同じだけど、改行も出力する
eprint!
標準エラー出力にプリントする
eprintln!
eprint! と同じだけど、改行も出力
fn fmt() { let s = format!("{}", "Hello"); print!("{}\n", s); println!("{} World", "Hello"); println!("{} + {} = {}", 1, 2, 3); println!("My name is {0}, {1} {0}.", "Bond", "James"); println!("My name is {bond}, {james} {bond}.", bond="Bond", james="James"); println!("dec: {num}, oct: {num:o}, hex: {num:x}, bin: {num:b}", num=15); println!("{:?}", ("Hello", "World")); #[derive(Debug)] struct St(i32); println!("{:?}", St(32)); println!("{:#?}", St(32)); }
Hello Hello World 1 + 2 = 3 My name is Bond, James Bond. My name is Bond, James Bond. dec: 15, oct: 17, hex: f, bin: 1111 ("Hello", "World") St(32) St( 32, )
"{:?}"
はデバッグ用のフォーマット指定。
"{:#?}"
も同じだが、構造体のメンバなど、見やすく表示してくれる(Pretty Print)
デバッグ用のフォーマットは、デフォルトでは、標準ライブラリ std
が提供する型にしか対応していない。
struct や enum で作るユーザー定義型は fmt::Debug
トレイトを実装しないといけないのだが #[derive(Debug)]
とすることで自動的に実装を提供できる。
(必要な時に自動的にやってくれるようになるとよいのだけど)
"{}"
に対する有効なテキストは fmt::Display
トレイトを自分で実装する必要がある。
プリミティブ
スカラー型
符号付き整数
i8 i16 i32 i64 i128 isize /// ポインタのバイト数ぶんの幅をもつ
小数点以下がない数値はデフォルトで i32
になる。
符号無し整数
u8 u16 u32 u64 u128 usize /// ポインタのバイト数ぶんの幅をもつ
浮動小数点数
f32 f64
小数点以下を含む数値はデフォルトで f64
になる
文字型(Unicode)
char
bool型
bool
ユニット型
()
空のタプルだけがユニット型になれる。何もないことを表すのに使われる。
複合型
配列
[1, 2, 3]
配列は同じ型のデータを複数保持する
タプル
(1, true)
タプルは異なる型のデータを複数保持する
変数に使う型
変数を宣言するとき、: <型名>
という形で、変数名の後ろに型を指定してもよい。指定しなくても多くの場合は型推論(inference)が働いて自動的に型が決定される。
fn variable() { // 型を指定してもよい let result: bool = true; // なくてもよい(true を代入しているので bool 型とわかる) let result = true; // 数値はデフォルトで i32 let num = 15; // 小数点付きの数値はデフォルトで f64 let float = 100.0; // 型サフィックス付き数値をいれるとその型になる。 let unsigned = 15u32; // 型推論システムが見ているのは初期値だけではない。 let mut var = 100; // i32 型のように思えるが、この段階ではまだ型は未確定で… var = 1_000_000_000u64; // ここで u64型が確定する。 // 下記のコードはエラー。型は途中で変更できない。 // var = true; // でも、同じ名前の変数を新たに導入して以降のコードで古い var を見えなくする // こと(シャドーイング)で型を変更したように見せかけることは可能。 let var = true; }
リテラル (Literals)
Literals and operators - Rust By Example
タプル (Tuples)
配列とスライス (Arrays and Slices)
Arrays and Slices - Rust By Example
カスタムタイプ (Custom Types)
Custom Types - Rust By Example
カスタムタイプは 2つ。struct と enum
構造体 (Struct)
列挙型 (Enum)
use宣言
Cのような Enum も可能
Enum を使ったリンクトリスト
Testcase: linked-list - Rust By Example
定数 (constants)
変数の束縛 (Variable Bindings)
Variable Bindings - Rust By Example
let
変数はデフォルトでは書き換え不可 (Mutability)
スコープとシャドーイング (Scope and Shadowing)
Scope and Shadowing - Rust By Example
初期化の遅延 (Declare first)
Declare first - Rust By Example
滅多に使わない
初期化忘れのコンパイルエラーになりやすい
変数を一時的に不変にする (Freezing)
型 (Types)
組み込み型を別の組み込み型に変換する (Casting)
リテラルで型を指定 (Literals)
数値リテラルは、接尾辞として型を指定することで、型を明示することができる。 これをアノテーション(annotation:注釈) と呼んでいる様子。
let a = 255u8; let b = 0x100u16;
アノテーションがない場合、その変数の使い方をもとに型推論によって型が決まる。
なんら制約がない場合は、整数は i32 、浮動小数点数は f64 になる。
型推論 (Inference)
Rust の型推論エンジンはかなり頭がよいので、初期化コードだけでなく、初期化後の変数の使い方も見て型を決定する。
// 型アノテーションによって elem が u8 であることはこの場で確定。 let elem = 5u8; // 空のベクタ(可変長の配列) let mut vec = Vec::new(); // この時点では、まだベクタに格納される型は未確定なので、vec の型も未確定。 // コンパイラは、なんからのベクタであることだけを知っている。(Vec<_>) // ベクタに最初の要素をセット vec.push(elem); // ここで初めてベクタの型が確定する。(Vec<u8>)
型に別名をつける (Aliasing)
type
で型に別名をつけることができる。- 型は
UpperCamelCase
でないとウォーニングが出る。 #[allow(non_camel_case_types)]
というアトリビュートを指定すると、キャメルケース以外でもOK
type UInt = u32; #[allow(non_camel_case_types)] type uint32_t = u32;
type
の目的は、ボイラープレート (boilerplate) なコードを減らすため。
ボイラープレートとは、言語仕様からくる要請によって、プログラマが繰り返し書かされる同じコードのこと。
ジェネリックを使うと、型がどんどん複雑になるので短い名前をつける機能は必須になってくる。
アトリビュート (Attribute: 属性)
アトリビュートは、クレート、モジュール、アイテム(関数や構造体など)につけるメタデータ(追加情報)。
下記の用途で使う。
- 条件付きコンパイル
- クレートの名前、バージョン、タイプ(binary or library)を指定する
- ウォーニング(lints)の抑止
- コンパイラの機能(マクロ、globインポートなど)を有効にする
- 外部ライブラリとのリンク
- 関数を Unit テスト用とマークする
- 関数に、ベンチマークの一部になるもの、とマークする
クレート全体に適用する場合はファイルの先頭などで #!
をつけて指定する。(#!
は Inner attributes)
#![cfg(...)]
モジュールやアイテム(関数や構造体など)につける場合は !
をつけずアイテムの前に置く。(#
は Outer attributes)
#[cfg(...)] fn func() {}
アトリビュートには、いくつかの書き方で設定値を指定できる
#[attribute = "value"] #[attribute(key = "value")] #[attribute(value)]
アトリビュートは複数の値を持つこともできる。
#[attribute(For, Bar, Buz)]
複数行に渡って記述することも可能。
#[attribute(value0, value1, value2, value3, value4, value5, value6, value7)]
アトリビュートの詳細はこちら
Attributes - The Rust Reference
使わないコードへの warning を抑止するアトリビュート(dead_code)
コンパイラは使われていない関数には unused function とウォーニングを出す。 下記のアトリビュートを指定することで抑止できる。(ただ、現実の開発では使われていない関数は削除するほうがよい)
#[alllow(dead_code)] fn unused_func() { }
クレート全体に適用するには !
付きで書いておく
#![allow(unused)]
TBD: あれ dead_code と unused の違いは?
クレートにつけるアトリビュート
crate_type
アトリビュートでクレートがバイナリクレートか、ライブラリクレートを指定する。
ライブラリのタイプも指定できる。(TBD: それなに?)
#![crate_type = "lib"]
crate_name
アトリビュートでクレートの名前を指定する。
#![crate_name = "RingBuffer"]
ただし Cargo を使う Rust プロジェクトでは crate_type
, crate_name
は意味を持たない。(Cargo.toml で管理するためかな?)
たいていの Rust プロジェクトとは Cargo を使って管理するので、これらのアトリビュートはほとんど使われない様子。
crate_type
アトリビュートを使うときは rustc
の --crate-type
オプションは必要ない。
$ rustc mylib.rs
$ ls lib*
libmylib.rlib
crate_type
アトリビュートがない場合 rustc がエラーになる
$ rustc mylib.rs error[E0601]: `main` function not found in crate `mylib` --> mylib.rs:3:1 | 3 | / pub fn hello_mylib() { 4 | | println!("mylib"); 5 | | } | |_^ consider adding a `main` function to `mylib.rs` error: aborting due to previous error For more information about this error, try `rustc --explain E0601`.
cfg アトリビュート
条件付きコンパイルを制御する方法は cfg アトリビュートのほか、cfg_attr アトリビュートと cfg! マクロがある。
cfg アトリビュートによる条件コンパイル
通常は、条件コンパイルはコードの可読性を下げるのでアプリケーションを書くときは使わないように書くのが正しい。
が、HWリソースを節約したいときなどには、動かさないコードは実行バイナリに含めたくないので必ず必要になる機能。
#[cfg(target_os = "linux")] fn who_am_i() { println!("Linux"); } #[cfg(target_os = "windows")] fn who_am_i() { println!("Windows"); } /// any: foo または bar が定義されている場合コンパイルされる #[cfg(any(foo, bar))] fn needs_foo_or_bar() { } /// all: unix ファミリー、かつ、32bitアーキテクチャの場合のみ #[cfg(all(unix, target_pointer_width = "32"))] fn memcpy4byte() { } /// not: foo が定義されていないこと。 #[cfg(not(foo))] fn need_not_foo() { }
cfg_attr アトリビュート
cfg_attr アトリビュートは、第一引数が真なら、第二引数以降をそれぞれ '#[' と']' で囲んでアトリビュートを作り、自らをそのアトリビュートで置き換える。
うーん。うまい使い方が思いつかない。
cfg!() マクロによる条件コンパイル
fn main() { cond_print(); } fn cond_print() { let endian = if cfg!(target_endian = "big") { "Big Endian" } else if cfg!(target_endian = "little") { "Little Endian" } else { "??" }; println!("{}", endian); }
cfg アトリビュートで任意のオプション(Configration Option)を使う方法
rustc
の --cfg
オプションで任意の値を渡すことが可能。
$ rustc --cfg 'verbose' --cfg 'my_option="foo"'
この例では、コード中のアトリビュート #[cfg(verbose)]
と #[cfg(my_option="foo")
が有効になる。
feature オプションは Cargo で予約
feature
オプションは Cargo によって設定される Configuration Option なので、使わない方がよさそう。
Cargo.toml で [features] を設定する方法はこちら
Features - The Cargo Book
組み込みの cfg オプション
- target_arch
- target_feature
- target_os
- target_family
- unix
- windows
- target_env
- target_endian
- target_pointer_width
- target_vendor
- test
- debug_assertions
- proc_macro
詳細はこちら
Conditional compilation - The Rust Reference