構造体からRustの特徴であるトレイトベースのジェネリクスまでの流れを翻訳します。
次: 5.16. Method Syntax
誤訳や改善案等、見つけた場合はご指摘の程宜しくお願いします。
- 元記事 : https://doc.rust-lang.org/book/structs.html のRust1.6時点
- ライセンス : MIT license, Apache License 2.0.
- See LICENSE-APACHE, LICENSE-MIT, and COPYRIGHT for details.
構造体
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は言語レベルでフィールドのミュータブル化に対応していないため、以下の様に書くことはできません。
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; // この新しい束縛で変更不可にします
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
は古い値を維持します。どれかのstruct
と同じ値を作る他にも、この構文を新たな値の作成に使用でき、明示することなく値のコピーが行えます。
struct Point3d {
x: i32,
y: i32,
z: i32,
}
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つは同じ値を持つ同士であったとしても等しくありません。
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
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'パターンと呼んでいます。
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
を束縛します。
Unit-like 構造体
あなたは全くメンバを持たないstruct
を定義できます。
struct Electron;
空のタプルである()
は時々unit
と呼ばれ、それに似ていることからこのような構造体をunit-like
と呼んでいます。タプル構造体のように、これは新しい型を定義します。
これは単体でもごくまれに役立ちます(もっとも、時々型をマーク代わりとして役立てる程度です)が、他の機能と組み合わせることにより便利になります。例えば、ライブラリはあなたにイベントを処理できる特定のトレイトが実装されたストラクチャの作成を要求するかもしれません。もしそのストラクチャの中に保存すべき値が何もなければ、あなたはダミーのデータを作成する必要はなく、ただunit-likeなstruct
を作るだけで良いのです。