// dictionary
タプル構造体.
タプル構造体(tuple struct)は、フィールドに名前を付けずに定義する構造体です。各フィールドは.0・.1のように位置で参照し、タプル型と同じ感覚で扱えます。フィールド名を付けるほどでもないけれど、値のまとまりに型としての名前は与えたい、という場面で使います。
定義すると型名と同じ名前のコンストラクタが同時に作られるため、インスタンスはPoint(3, 5)のように関数呼び出しの形で生成します1。
struct Point(i32, i32);
fn main() {
let shop = Point(3, 5); // お店の位置(x, y)
println!("x座標: {}, y座標: {}", shop.0, shop.1);
}Playgroundで開くタプル/構造体との違い
タプル・タプル構造体・構造体の違いは次のとおりです。
| フィールド名 | 型としての名前 | 分解の書き方 | |
|---|---|---|---|
| タプル | なし(位置) | なし | let (x, y) = p; |
| タプル構造体 | なし(位置) | あり | let Point(x, y) = p; |
| 構造体 | あり | あり | let Point { x, y } = p; |
タプルと違い、分解の際は型名を書く必要があります。
ニュータイプパターン
フィールドが1つだけのタプル構造体で既存の型を包み、意味の違いを型として表す書き方はニュータイプパターンと呼ばれます。素のu32(整数型)のままでは取り違えてもコンパイラが気づけない値も、包んでしまえば別の型として区別されます。
struct PriceExcludingTax(u32); // 税抜き価格
struct PriceIncludingTax(u32); // 税込み価格
fn print_total(price: PriceIncludingTax) {
println!("お会計は{}円です", price.0);
}
fn main() {
let price = PriceExcludingTax(1000);
print_total(price); // エラー: E0308(税抜きを税込みとして渡している)
}Playgroundで開く補足
コンストラクタは関数として持ち回せる
コンストラクタは実体が関数のため、mapのような高階関数へそのまま渡せます。
struct Yen(u32);
fn main() {
let prices = vec![120, 250, 380];
let yens: Vec<Yen> = prices.into_iter().map(Yen).collect(); // Yenを関数として渡す
println!("2つ目: {}円", yens[1].0);
}Playgroundで開くメソッドの定義やDebug出力も同じ
タプル構造体にもimplブロックでメソッドを定義でき、#[derive(Debug)]も同じように使えます。{:?}での出力はPoint(3, 5)の形になります(コンソール出力も参照)。
#[derive(Debug)]
struct Point(i32, i32);
impl Point {
fn distance_from_origin(&self) -> f64 {
((self.0 * self.0 + self.1 * self.1) as f64).sqrt()
}
}
fn main() {
let shop = Point(3, 4);
println!("{shop:?}"); // Point(3, 4)
println!("{}", shop.distance_from_origin()); // 5
}Playgroundで開くフィールドを持たない構造体
フィールドリストごと省略したstruct Marker;は、タプル構造体ではなくユニット様構造体と呼ばれる別の形になります。
Footnotes
-
The Rust Reference: Structs — 型の定義に加えて、値の名前空間に同名のコンストラクタを定義すると述べています。 ↩
この辞書が使われているページ
backlinks 5