構造体

struct はより複雑なデータ型を作る方法の1つです。例えば、もし私たちが2次元空間の座標に関する計算を行っているとして、 xy 、両方の値が必要になるでしょう。

fn main() { let origin_x = 0; let origin_y = 0; }
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 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 ではありません。

いつものように、 letstruct のインスタンスを作ることができますが、ここでは 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); }
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 の初期化時には、値の一部を他の構造体からコピーしたいことを示す .. を含めることができます。例えば、

fn main() { struct Point3d { x: i32, y: i32, z: i32, } let mut point = Point3d { x: 0, y: 0, z: 0 }; point = Point3d { y: 1, .. point }; }
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を与えていますが、xzは古い値を維持します。どれかのstructと同じ値を作る他にも、この構文を新たな値の作成に使用でき、明示することなく値のコピーが行えます。

fn main() { 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 }; }
let origin = Point3d { x: 0, y: 0, z: 0 };
let point = Point3d { z: 1, x: 2, .. origin };

タプル構造体

Rustには「タプル構造体」と呼ばれる、タプルstruct のハイブリットのようなデータ型があります。タプル構造体自体には名前がありますが、そのフィールドには名前がありません。

fn main() { struct Color(i32, i32, i32); struct Point(i32, i32, i32); }
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 を使ったほうが良いです。 ColorPoint はこのようにも書けます。

fn main() { struct Color { red: i32, blue: i32, green: i32, } struct Point { x: i32, y: i32, z: i32, } }
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_length10 を束縛します。

Unit-like 構造体

あなたは全くメンバを持たない struct を定義できます。

fn main() { struct Electron; let x = Electron; }
struct Electron;

let x = Electron;

空のタプルである () は時々 unit と呼ばれ、それに似ていることからこのような構造体を unit-like と呼んでいます。タプル構造体のように、これは新しい型を定義します。

これは単体でもごくまれに役立ちます(もっとも、時々型をマーク代わりとして役立てる程度です)が、他の機能と組み合わせることにより便利になります。例えば、ライブラリはあなたにイベントを処理できる特定のトレイトが実装されたストラクチャの作成を要求するかもしれません。もしそのストラクチャの中に保存すべき値が何もなければ、あなたはダミーのデータを作成する必要はなく、ただunit-likeな struct を作るだけで良いのです。