注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
struct
はより複雑なデータ型を作る方法の1つです。例えば、もし私たちが2次元空間の座標に関する計算を行っているとして、 x
と y
、両方の値が必要になるでしょう。
let origin_x = 0; let origin_y = 0;
struct
でこれら2つを1つのデータ型にまとめることができます。
struct Point { x: i32, y: i32, } fn main() { let origin = Point { x: 0, y: 0 }; // origin: Point println!("The origin is at ({}, {})", origin.x, origin.y); }
ここで多くの情報が出てきましたから、順番に見ていきましょう。まず、 struct
キーワードを使って構造体とその名前を宣言しています。慣習により、構造体は初めが大文字のキャメルケースで記述しています。 PointInSpace
であり、 Point_In_Space
ではありません。
いつものように、 let
で struct
のインスタンスを作ることができますが、ここでは key: value
スタイルの構文でそれぞれのフィールドに値をセットしています。順序は元の宣言と同じである必要はありません。
最後に、作成された構造体のフィールドは名前を持つため、 origin.x
というようにドット表記でアクセスできます。
Rustの他の束縛のように、 struct
が持つ値はイミュータブルがデフォルトです。 mut
を使うと値をミュータブルにできます。
struct Point { x: i32, y: i32, } fn main() { let mut point = Point { x: 0, y: 0 }; point.x = 5; println!("The point is at ({}, {})", point.x, point.y); }
これは The point is at (5, 0)
と出力されます。
Rustは言語レベルでフィールドのミュータビリティに対応していないため、以下の様に書くことはできません。
fn main() { struct Point { mut x: i32, y: i32, } }struct Point { mut x: i32, y: i32, }
ミュータビリティは束縛に付与できる属性であり、構造体自体に付与できる属性ではありません。もしあなたがフィールドレベルのミュータビリティを使うのであれば、初めこそ奇妙に見えるものの、非常に簡単に実現できる方法があります。以下の方法で少しの間だけミュータブルな構造体を作ることができます。
struct Point { x: i32, y: i32, } fn main() { let mut point = Point { x: 0, y: 0 }; point.x = 5; // let point = point; // this new binding can’t change now let point = point; // この新しい束縛でここから変更できなくなります // point.y = 6; // this causes an error point.y = 6; // これはエラーになります }struct Point { x: i32, y: i32, } fn main() { let mut point = Point { x: 0, y: 0 }; point.x = 5; let point = point; // この新しい束縛でここから変更できなくなります point.y = 6; // これはエラーになります }
struct
の初期化時には、値の一部を他の構造体からコピーしたいことを示す ..
を含めることができます。例えば、
struct Point3d { x: i32, y: i32, z: i32, } let mut point = Point3d { x: 0, y: 0, z: 0 }; point = Point3d { y: 1, .. point };
ここではpoint
に新しいy
を与えていますが、x
とz
は元の値のままです。コピー先は元の構造体と同じである必要はなく、この構文で新しい構造体を作ることもできます。その場合、指定しなかったフィールドは元の構造体からコピーされます。
let origin = Point3d { x: 0, y: 0, z: 0 }; let point = Point3d { z: 1, x: 2, .. origin };
Rustには「タプル構造体」と呼ばれる、タプルと struct
のハイブリットのようなデータ型があります。タプル構造体自体には名前がありますが、そのフィールドには名前がありません。
struct Color(i32, i32, i32); struct Point(i32, i32, i32);
これら2つは同じ値を持つ同士であったとしても等しくありません。
fn main() { struct Color(i32, i32, i32); struct Point(i32, i32, i32); let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }let black = Color(0, 0, 0); let origin = Point(0, 0, 0);
ほとんどの場合タプル構造体よりも struct
を使ったほうが良いです。 Color
や Point
はこのようにも書けます。
struct Color { red: i32, blue: i32, green: i32, } struct Point { x: i32, y: i32, z: i32, }
今、私たちはフィールドの位置ではなく実際のフィールドの名前を持っています。良い名前は重要で、 struct
を使うということは、実際に名前を持っているということです。
訳注: 原文を元に噛み砕くと、「タプルはフィールドの並びによって区別され、構造体はフィールドの名前によって区別されます。これはタプルと構造体の最たる違いであり、構造体を持つことは名前を付けられたデータの集まりを持つことに等しいため、構造体における名前付けは重要です。」といった所でしょうか。
ただし、タプル構造体が非常に便利な場合も あります。要素が1つだけの場合です。要素の値と区別でき、独自の意味を表現できるような新しい型を作成できることから、私たちはこれを「newtype」パターンと呼んでいます。
fn main() { struct Inches(i32); let length = Inches(10); let Inches(integer_length) = length; println!("length is {} inches", integer_length); }struct Inches(i32); let length = Inches(10); let Inches(integer_length) = length; println!("length is {} inches", integer_length);
上記の通り、標準のタプルと同じように let
を使って分解することで内部の整数型を取り出すことができます。
このケースでは let Inches(integer_length)
が integer_length
に 10
を代入します。
全くメンバを持たない struct
を定義することもできます。
struct Electron; let x = Electron;
このような構造体は「unit-like」であると言われます。空のタプルであり「unit」とも呼ばれる ()
とよく似ているからです。タプル構造体と同様に、 unit-like 構造体も新しい型を定義します。
これは単体では滅多に役に立ちません(マーカ型として使える場合もあります)が、他の機能と組み合わせると便利な場合があります。例えば、ライブラリがイベントを処理する特定のトレイトを実装する構造体の作成を要求するかもしれません。もしその構造体の中に保存すべき値が何もなければ、単にunit-likeな struct
を作るだけで良いのです。