今日の内容
- 構造体とは
はじめに
前回はHashMapについて学びました。今回はRustでの構造体について学びます。
構造体とは
そもそも構造体とはなんでしょうか。構造体とはデータ型の要素を集めたものです。
詳しく見ていきましょう。
以下は一つの構造体です。
struct Student {
name: String,
grade: u8,
class: u8,
number: u32,
}
ブロックの中を見てください。「name」、「age」、「number」という名前の変数があり、それらの型が指定されています。ここで、異なる型が含まれていることに注目してください。構造体は、中に入れられる物の種類や数を自分で決めることができます。この「中に入れられる物」を フィールド と呼びます。
構造体はstruct
で定義します。
struct Student {
name: String,
grade: u8,
class: u8,
number: u32,
}
fn main(){
let student1 = Student {
name: String::from("山田 太郎"),
grade: 1,
class: 3,
number: 32,
};
let student2 = Student {
name: String::from("山田 花子"),
grade: 1,
class: 2,
number: 36,
};
println!("{}-{} ({}) {}", student1.grade, student1.class, student1.number, student1.name);
println!("{}-{} ({}) {}", student2.grade, student2.class, student2.number, student2.name);
}
/******** 実行結果 ********
1-3 (32) 山田 太郎
1-2 (36) 山田 花子
*************************/
このプログラムでは、構造体のオブジェクトを作成して、それぞれのフィールドを表示しています。構造体のオブジェクトを作成する場合は各フィールドを「key: value」という形で束縛します。オブジェクトのフィールドは、.
演算子を使って指定します。
タプル構造体とユニット構造体
時々タプル型を使用してきましたが、指定した要素で構成されたタプルに名前をつけられます。このようなタプルを タプル構造体 と呼びます。
struct Rgb(i32, i32, i32);
fn main(){
let black = Rgb(0, 0, 0);
println!("{} {} {}", black.0, black.1, black.2);
}
/******** 実行結果 ********
0 0 0
*************************/
フィールドを持たない空の構造体を ユニット構造体 と呼びます。これは、識別子として利用したり、トレイトだけ実装するなどに使えます。
struct ReadOnly;
struct WriteOnly;
fn action(mode: ReadOnly) {
println!("Hello, World!");
}
fn main() {
let mode = ReadOnly;
action(mode);
}
/******** 実行結果 ********
Hello, World!
*************************/
これはReadOnlyとWriteOnlyという名前のユニット構造体を定義しています。main関数内でaction関数を呼び出していますが、もし実引数の「mode」の型がReadOnly以外であればエラーになります。誤った型の利用を防ぐことができます。
impl
implについて学ぶ前にまずはメソッドについて軽く理解します。
メソッドは関数に似ています。引数と戻り値がありますが、メソッドは構造体の文脈で定義されるという点で関数と異なります。最初の引数は必ずselfになり、これはメソッドが呼び出されている構造体インスタンスを表します。
implは、Rustで構造体や列挙型に関連するメソッドや関数を定義するためのキーワードです。「implはその型に関連付けられた振る舞いを定義する場所」と考えればわかりやすいです。
struct Point {
x: i32,
y: i32,
}
impl Point {
fn move_point(&mut self, dx: i32, dy: i32){
self.x += dx;
self.y += dy;
}
fn distance_from_origin(&self) -> f64 {
((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()
}
}
fn main() {
let mut point = Point { x: 3, y: 4};
println!("Distance from origin: {}", point.distance_from_origin());
point.move_point(1, 2);
println!("Point moved to: ({}, {})", point.x, point.y)
}
/******** 実行結果 ********
Distance from origin: 5
Point moved to: (4, 6)
*************************/
- self: メソッド内で構造体のフィールドにアクセスするため使う
- &self -> インスタンスを参照する(データを変更しない)
- &mut self -> インスタンスを可変参照する(データを変更できる)
- self -> インスタンスの所有権を取る
また、implは同じ構造体に対して、複数定義できます。
関連関数
impl内で関数を定義します。引数にself
を取りません。これは、構造体に関連しているにもかかわらず、そのオブジェクトなしで呼び出せます。このような関数を 関連関数 と呼びます。
主に、「その構造体のオブジェクトを作るメソッド(new)」を定義する際に使われます。
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Self {
Point {x, y}
}
}
fn main() {
let point = Point::new(5, 10);
println!("Point created: ({}, {})", point.x, point.y);
}
/******** 実行結果 ********
Point created: (5, 10)
*************************/
おわりに
今回はRustでの構造体について学びました。次回は簡単なブラックジャックを作ってみたいと思います。
ご精読ありがとうございました。