注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
Rust言語は「プリミティブ」とみなされる多数の型を持ちます。 これはそれらが言語に組み込まれていることを意味します。 Rustの型は構造化されており、標準ライブラリでもプリミティブ型を用いて構築した数多くの便利な型を提供しています。 しかし、プリミティブ型が最もプリミティブ(基本的で素朴)な型です。
Rustには bool
と名付けられた組込みのブーリアン型があります。
それは true
と false
という2つの値を持ちます。
let x = true; let y: bool = false;
ブーリアンの一般的な使い方は、 if
条件 で用いるものです。
bool
の詳しいドキュメントは 標準ライブラリのドキュメント にあります。
char
char
型は1つのユニコードのスカラ値を表現します。
char
はシングルクオート( '
)で作られます。
let x = 'x'; let two_hearts = '💕';
char
が1バイトである他の言語と異なり、これはRustの char
が1バイトではなく4バイトであるということを意味します。
char
の詳しいドキュメントは 標準ライブラリのドキュメント にあります。
Rustにはいくつかのカテゴリにたくさんの種類の数値型があります。そのカテゴリは符号ありと符号なし、固定長と可変長、浮動小数点数と整数です。
それらの型はカテゴリとサイズという2つの部分から成ります。
例えば、 u16
はサイズ16ビットで符号なしの型です。
ビット数を大きくすれば、より大きな数値を扱うことができます。
もし数値リテラルがその型を推論させるものを何も持たないのであれば、以下のとおりデフォルトになります。
fn main() { // let x = 42; // x has type i32 let x = 42; // xはi32型を持つ // let y = 1.0; // y has type f64 let y = 1.0; // yはf64型を持つ }let x = 42; // xはi32型を持つ let y = 1.0; // yはf64型を持つ
これはいろいろな数値型のリストにそれらの標準ライブラリのドキュメントへのリンクを付けたものです。
それらをカテゴリ別に調べましょう。
整数型には符号ありと符号なしという2つの種類があります。
違いを理解するために、サイズ4ビットの数値を考えましょう。
符号あり4ビット整数は -8
から +7
までの数値を保存できます。
符号ありの数値は「2の補数表現」を使います。
符号なし4ビット整数は、マイナスを保存する必要がないため、 0
から +15
までの値を保存できます。
符号なし(訳注:unsigned)型はそれらのカテゴリに u
を使い、符号あり型は i
を使います。
i
は「整数(訳注:integer)」の頭文字です。
そのため、 u8
は8ビット符号なし数値、 i8
は8ビット符号あり数値です。
固定長型はそれらの表現に特定のビット数を持ちます。
指定することのできるビット長は 8
、 16
、 32
、 64
です。
そのため、 u32
は符号なし32ビット整数、 i64
は符号あり64ビット整数です。
Rustが提供する型には、そのサイズが実行しているマシンのポインタのサイズに依存するものもあります。
それらの型はカテゴリとして「size」を使い、符号ありと符号なしの種類があります。
これが isize
と usize
という2つの型を作ります。
Rustは f32
と f64
という2つの浮動小数点型を持ちます。
それらはIEEE-754単精度及び倍精度小数点数に対応します。
多くのプログラミング言語のように、Rustには何かのシーケンスを表現するためのリスト型があります。 最も基本的なものは 配列 、固定長の同じ型の要素のリストです。 デフォルトでは、配列はイミュータブルです。
fn main() { let a = [1, 2, 3]; // a: [i32; 3] let mut m = [1, 2, 3]; // m: [i32; 3] }let a = [1, 2, 3]; // a: [i32; 3] let mut m = [1, 2, 3]; // m: [i32; 3]
配列は [T; N]
という型を持ちます。
この T
記法については ジェネリクスのセクション で話します。
N
は配列の長さのためのコンパイル時の定数です。
配列の各要素を同じ値で初期化するための省略表現があります。
この例では、 a
の各要素は 0
で初期化されます。
let a = [0; 20]; // a: [i32; 20]
配列 a
の要素の個数は a.len()
で得られます。
let a = [1, 2, 3]; println!("a has {} elements", a.len());
配列の特定の要素には 添字記法 でアクセスできます。
fn main() { let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] println!("The second name is: {}", names[1]); }let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] println!("The second name is: {}", names[1]);
添字はほとんどのプログラミング言語と同じように0から始まります。そのため、最初の名前は names[0]
で2つ目の名前は names[1]
です。
前の例は The second name is: Brian
と表示します。
もし配列に含まれない添字を使おうとすると、エラーが出ます。配列アクセスは実行時に境界チェックを受けます。
他のシステムプログラミング言語では、そのような誤ったアクセスは多くのバグの源となります。
array
の詳しいドキュメントは 標準ライブラリのドキュメント にあります。
「スライス」は他のデータ構造への参照(又は「ビュー」)です。 それらはコピーすることなく配列の要素への安全で効率的なアクセスを許すために便利です。 例えば、メモリに読み込んだファイルの1行だけを参照したいことがあるかもしれません。 性質上、スライスは直接作られるのではなく、既存の変数束縛から作られます。 スライスは定義された長さを持ち、ミュータブルにもイミュータブルにもできます。
スライスは内部的には、データの先頭へのポインタとデータ長の組み合わせで表現されます。
&
と []
を組合せて使うと、様々なものからスライスが作れます。
&
はスライスが、参照と似たものであることを示します(参照については、後ほど詳細をカバー します)。
[]
はレンジを持ち、スライスの長さを定義します。
let a = [0, 1, 2, 3, 4]; let complete = &a[..]; // aに含まれる全ての要素を持つスライス let middle = &a[1..4]; // 1、2、3のみを要素に持つaのスライス
スライスは型 &[T]
を持ちます。
ジェネリクス をカバーするときにその T
について話すでしょう。
slice
の詳しいドキュメントは 標準ライブラリのドキュメント にあります。
str
Rustの str
型は最もプリミティブな文字列型です。
サイズ不定型 のように、それ単体ではあまり便利ではありませんが、 &str
のように参照の後ろに置かれたときに便利になります。
文字列 と 参照 についてカバーする際に、より正確に学びましょう。
str
の詳しいドキュメントは 標準ライブラリのドキュメント にあります。
タプルは固定サイズの順序ありリストです。 このようなものです。
fn main() { let x = (1, "hello"); }let x = (1, "hello");
丸括弧とコンマがこの長さ2のタプルを形成します。 これは同じコードですが、型注釈が付いています。
fn main() { let x: (i32, &str) = (1, "hello"); }let x: (i32, &str) = (1, "hello");
見てのとおり、タプルの型はタプルと同じように見えます。しかし、各位置には値ではなく型名が付いています。
注意深い読者は、タプルが異なる型の値を含んでいることにも気が付くでしょう。このタプルには i32
と &str
が入っています。
システムプログラミング言語では、文字列は他の言語よりも少し複雑です。
今のところ、 &str
を 文字列スライス と読みましょう。それ以上のことは後で学ぶでしょう。
もしそれらの持っている型と アリティ が同じであれば、あるタプルを他のタプルに割り当てられます。 タプルの長さが同じであれば、それらのタプルのアリティは同じです。
fn main() { let mut x = (1, 2); // x: (i32, i32) let y = (2, 3); // y: (i32, i32) x = y; }let mut x = (1, 2); // x: (i32, i32) let y = (2, 3); // y: (i32, i32) x = y;
タプルのフィールドには 分配束縛let を通じてアクセスできます。 これが例です。
fn main() { let (x, y, z) = (1, 2, 3); println!("x is {}", x); }let (x, y, z) = (1, 2, 3); println!("x is {}", x);
前にlet
文の左辺は、単なる束縛の割り当てよりも強力だと言ったときのことを覚えていますか。
ここで説明します。
let
の左辺にはパターンを書くことができ、もしそれが右辺とマッチしたならば、複数の束縛を一度に割り当てられます。
この場合、 let
が「分配束縛」、つまりタプルを「分解して」、要素を3つの束縛に割り当てます。
このパターンは非常に強力で、後で繰り返し見るでしょう。
コンマを付けることで要素1のタプルを丸括弧の値と混同しないように明示できます。
fn main() { // (0,); // single-element tuple (0,); // 1要素のタプル // (0); // zero in parentheses (0); // 丸括弧に囲まれたゼロ }(0,); // 1要素のタプル (0); // 丸括弧に囲まれたゼロ
タプルのフィールドにはインデックス構文でアクセスすることもできます。
fn main() { let tuple = (1, 2, 3); let x = tuple.0; let y = tuple.1; let z = tuple.2; println!("x is {}", x); }let tuple = (1, 2, 3); let x = tuple.0; let y = tuple.1; let z = tuple.2; println!("x is {}", x);
配列のインデックスと同じように、それは0から始まります。しかし、配列のインデックスと異なり、それは []
ではなく .
を使います。
タプルの詳しいドキュメントは 標準ライブラリのドキュメント にあります。
関数も型を持ちます! それらはこのようになります。
fn main() { fn foo(x: i32) -> i32 { x } let x: fn(i32) -> i32 = foo; }fn foo(x: i32) -> i32 { x } let x: fn(i32) -> i32 = foo;
この場合、 x
は i32
を受け取り i32
を戻す関数への「関数ポインタ」です。