構造体

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は元の値のままです。コピー先は元の構造体と同じである必要はなく、この構文で新しい構造体を作ることもできます。その場合、指定しなかったフィールドは元の構造体からコピーされます。

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-like」であると言われます。空のタプルであり「unit」とも呼ばれる () とよく似ているからです。タプル構造体と同様に、 unit-like 構造体も新しい型を定義します。

これは単体では滅多に役に立ちません(マーカ型として使える場合もあります)が、他の機能と組み合わせると便利な場合があります。例えば、ライブラリがイベントを処理する特定のトレイトを実装する構造体の作成を要求するかもしれません。もしその構造体の中に保存すべき値が何もなければ、単にunit-likeな struct を作るだけで良いのです。