プリミティブ型

Rust言語は「プリミティブ」であると考えられるかなりの数の型を持ちます。 これはそれらが言語に組み込まれていることを意味します。 標準ライブラリも同様にそれらの型の上に構築されたかなりの数の便利な型を提供していて、そのような方法でRustは構造化されています。しかし、プリミティブ型が最もプリミティブです。

ブーリアン型

Rustには bool と名付けられた組込みのブーリアン型があります。 それは truefalse という2つの値を持ちます。

fn main() { let x = true; let y: bool = false; }
let x = true;

let y: bool = false;

ブーリアンの一般的な使い方は、 if 条件 の中で用いるものです。

bool のさらなるドキュメントは 標準ライブラリのドキュメントの中 で見付けることができます。

char

char 型は1つのユニコードのスカラ値を表現します。 char はシングルクオート( ' )で作ることができます。

fn main() { let x = 'x'; let two_hearts = '💕'; }
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ビット符号あり数値です。

固定長型

固定長型はそれらの表現の中に特定のビット数を持ちます。 指定することのできるビット長は 8163264 です。 そのため、 u32 は符号なし32ビット整数、 i64 は符号あり64ビット整数です。

可変長型

Rustはそのサイズが実行しているマシンのポインタのサイズに依存する型も提供します。 それらの型はカテゴリとして「size」を使い、符号ありと符号なしの種類があります。 これが isizeusize という2つの型を作ります。

浮動小数点型

Rustは f32f64 という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 で初期化されます。

fn main() { let a = [0; 20]; // a: [i32; 20] }
let a = [0; 20]; // a: [i32; 20]

配列 a の要素の個数は a.len() で得ることができます。

fn main() { let a = [1, 2, 3]; println!("a has {} elements", 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行だけを参照したいことがあるかもしれません。 性質上、スライスは直接作られるのではなく、既存の変数束縛から作られます。 スライスは定義された長さを持ち、ミュータブルにもイミュータブルにもできます。

スライシング構文

様々なものからスライスを作るためには &[] の組合せを使うことができます。 & はスライスが参照と同じであることを示し、 [] はレンジを持ち、スライスの長さを定義します。

fn main() { let a = [0, 1, 2, 3, 4]; // let complete = &a[..]; // A slice containing all of the elements in a let complete = &a[..]; // aに含まれる全ての要素を持つスライス // let middle = &a[1..4]; // A slice of a: just the elements 1, 2, and 3 let middle = &a[1..4]; // 1、2、3のみを要素に持つaのスライス }
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;

この場合、 xi32 を受け取り i32 を戻す関数への「関数ポインタ」です。