struct / フィールド定義
| 対応: | Rust 1.0(2015) |
|---|
Rustの構造体(struct)は複数の値をまとめたデータ型です。フィールドに名前を付ける通常の構造体、名前のないタプル構造体、フィールドのないユニット構造体の3種類があります。
構文
// 通常の構造体(フィールドに名前があります)。
struct Point {
x: f64,
y: f64,
}
// タプル構造体(フィールドに名前がありません)。
struct Color(u8, u8, u8);
// ユニット構造体(フィールドがありません)。
struct Marker;
// インスタンス生成です。
let p = Point { x: 1.0, y: 2.5 };
let c = Color(255, 128, 0);
let m = Marker;
// フィールドのアクセスです。
println!("{}", p.x); // 1
println!("{}", c.0); // 255(タプル構造体はインデックスでアクセス)
構造体の種類
| 種類 | 概要 |
|---|---|
| struct 名前 { フィールド: 型, ... } | 名前付きフィールドを持つ通常の構造体です。 |
| struct 名前(型, 型, ...); | 名前なしフィールドのタプル構造体です。 |
| struct 名前; | フィールドなしのユニット構造体です(トレイトのマーカーなどに使います)。 |
| let mut s = 構造体 { ... } | mut を付けると全フィールドが変更可能になります。 |
| フィールドショートハンド | 変数名とフィールド名が同じ場合は『{ x, y }』と省略できます。 |
| struct 構造体2 { .., ..struct1 } | 残りのフィールドを別のインスタンスからコピーします(更新構文)。 |
サンプルコード
構造体(struct)の定義と基本的なフィールドアクセスを確認するサンプルコードです。
sample_struct_definition.rs
// 呪術師の情報を表す構造体です。
struct Sorcerer {
name: String,
email: String,
grade: u32,
active: bool,
}
// タプル構造体の例です。
struct Point(f64, f64);
struct Meters(f64);
fn build_sorcerer(email: String, name: String) -> Sorcerer {
Sorcerer {
// フィールドショートハンド: 変数名とフィールド名が同じなので省略します。
email,
name,
grade: 1,
active: true,
}
}
fn main() {
// インスタンスを生成します。
let user1 = Sorcerer {
name: String::from("五条悟"),
email: String::from("gojo_satoru@wp-p.info"),
grade: 1,
active: true,
};
println!("呪術師: {}", user1.name);
println!("メール: {}", user1.email);
println!("等級: {}", user1.grade);
// mut でフィールドを変更します(構造体全体がmutになります)。
let mut user2 = build_sorcerer(
String::from("ryomen_sukuna@wp-p.info"),
String::from("両面宿儺"),
);
user2.grade = 0;
println!("両面宿儺 の等級更新後: {}", user2.grade);
// 構造体の更新構文: 一部のフィールドだけ変えて新しいインスタンスを作ります。
let user3 = Sorcerer {
email: String::from("itadori_yuji@wp-p.info"),
name: String::from("虎杖悠仁"),
..user1 // 残りのフィールドをuser1からコピーします。
};
println!("user3 の grade: {}", user3.grade); // 1(user1からコピー)
println!("user3 active: {}", user3.active); // true
// タプル構造体の使い方です。
let p = Point(3.0, 4.0);
let d = Meters(5.2);
println!("Point: ({}, {})", p.0, p.1);
println!("距離: {} m", d.0);
// タプル構造体は分割代入も使えます。
let Point(x, y) = p;
println!("x={}, y={}", x, y);
}
コンパイルして実行すると次のように出力されます。
rustc struct_definition.rs ./struct_definition 呪術師: 五条悟 メール: gojo_satoru@wp-p.info 等級: 1 両面宿儺 の等級更新後: 0 user3 の grade: 1 user3 active: true Point: (3, 4) 距離: 5.2 m x=3, y=4
よくあるミス
よくあるミス1: 更新構文でString型フィールドの所有権が移動
更新構文(..other)でString型フィールドがコピーされる際、Copyを実装していないStringは所有権が移動します。移動後は元のインスタンスからそのフィールドにアクセスできません。
struct_def_mistake1.rs
#[derive(Debug)]
struct Sorcerer {
name: String,
grade: u32,
}
fn main() {
let s1 = Sorcerer {
name: String::from("五条悟"),
grade: 1,
};
let s2 = Sorcerer {
grade: 0,
..s1 // s1.nameの所有権がs2に移動します。
};
println!("s2: {:?}", s2);
// s1.nameはもう使えません。
// println!("{}", s1.name); // コンパイルエラー
// gradeはCopyなのでs1.gradeはまだ使えます。
println!("s1.grade: {}", s1.grade);
}
rustc struct_def_mistake1.rs
./struct_def_mistake1
s2: Sorcerer { name: "五条悟", grade: 0 }
s1.grade: 1
元のインスタンスを保持したい場合はclone()してから更新構文を使います。
struct_def_fix1.rs
#[derive(Debug, Clone)]
struct Sorcerer {
name: String,
grade: u32,
}
fn main() {
let s1 = Sorcerer {
name: String::from("五条悟"),
grade: 1,
};
let s2 = Sorcerer {
grade: 0,
..s1.clone() // cloneしてからs1.nameをコピーします。
};
// s1もs2も両方使えます。
println!("s1: {:?}", s1);
println!("s2: {:?}", s2);
}
rustc struct_def_fix1.rs
./struct_def_fix1
s1: Sorcerer { name: "五条悟", grade: 1 }
s2: Sorcerer { name: "五条悟", grade: 0 }
よくあるミス2: mutを付け忘れてフィールド変更不可
Rustでは構造体インスタンスを変更するには変数をmutとして宣言する必要があります。個別のフィールドだけをmutableにすることはできません(構造体全体がmutになります)。
struct_def_mistake2.rs
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 1.0, y: 2.0 }; // mutなし
// フィールドを変更しようとするとエラーになります。
p.x = 3.0; // コンパイルエラー: cannot assign to `p.x`, as `p` is not declared as mutable
}
rustc struct_def_mistake2.rs error[E0594]: cannot assign to `p.x`, as `p` is not declared as mutable
変数宣言にmutを追加します。
struct_def_fix2.rs
struct Point {
x: f64,
y: f64,
}
fn main() {
let mut p = Point { x: 1.0, y: 2.0 }; // mutを付けます。
p.x = 3.0;
p.y = 4.0;
println!("({}, {})", p.x, p.y); // (3, 4)
}
rustc struct_def_fix2.rs ./struct_def_fix2 (3, 4)
概要
Rustの構造体はデフォルトでは『Debug』などのトレイトを実装していません。『println!("{:?}", s)』でデバッグ出力するには『#[derive(Debug)]』属性を追加する必要があります。
構造体の更新構文(『..other』)では、String型などのフィールドは所有権が移動します。移動後のインスタンスからそのフィールドにはアクセスできなくなります。
構造体へのメソッド追加は『impl / メソッド定義』を、自動トレイト実装は『#[derive(Debug, Clone, PartialEq)]』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。