構造体と列挙型
構造体 (struct)
複数の値をひとまとめにしたもの.他の言語のクラスやオブジェクトに近い.
struct User {
name: String,
age: i32,
}
fn main() {
let user = User {
name: String::from("Alice"),
age: 25,
};
println!("{} is {} years old", user.name, user.age);
}
メソッドを生やす (impl)
impl ブロックで構造体にメソッドを追加できる:
struct User {
name: String,
age: i32,
}
impl User {
fn greet(&self) {
println!("Hi, I'm {}!", self.name);
}
fn have_birthday(&mut self) {
self.age += 1;
}
}
fn main() {
let mut user = User {
name: String::from("Alice"),
age: 25,
};
user.greet(); // Hi, I'm Alice!
user.have_birthday();
println!("{}", user.age); // 26
}
メソッドの第一引数は「自分自身」:
| 書き方 | 意味 |
|---|---|
&self |
自分を読むだけ.変更しない |
&mut self |
自分を変更する |
語弊注意:
&は本来「参照」という重要な概念だけど,メソッドの&selfと&mut selfはほぼおまじない.「読むだけ」か「変更もする」か,そのくらいの理解で OK.
関連関数(コンストラクタ的なやつ)
self を取らない関数も impl に書ける.User::new(...) のように :: で呼ぶ:
impl User {
fn new(name: String, age: i32) -> User {
User { name, age }
}
}
fn main() {
let user = User::new(String::from("Bob"), 30);
user.greet();
}
User { name, age } は User { name: name, age: age } の省略形.変数名とフィールド名が同じなら省略できる.
列挙型 (enum)
enum は「このどれか」を表す型:
enum Direction {
Up,
Down,
Left,
Right,
}
fn main() {
let dir = Direction::Up;
match dir {
Direction::Up => println!("Going up!"),
Direction::Down => println!("Going down!"),
Direction::Left => println!("Going left!"),
Direction::Right => println!("Going right!"),
}
}
match と相性が抜群.すべてのバリアントを網羅しないとコンパイラに怒られるので,ケースの漏れを防げる.
データを持つ enum
enum の各バリアントにはデータを持たせられる.これが Rust の enum の真骨頂:
enum Shape {
Circle(f64), // 半径
Rectangle(f64, f64), // 幅, 高さ
}
fn area(shape: Shape) -> f64 {
match shape {
Shape::Circle(r) => 3.14 * r * r,
Shape::Rectangle(w, h) => w * h,
}
}
fn main() {
let c = Shape::Circle(5.0);
let r = Shape::Rectangle(3.0, 4.0);
println!("Circle: {}", area(c)); // 78.5
println!("Rectangle: {}", area(r)); // 12.0
}
match でバリアントを分岐しつつ,中のデータを取り出せる.
derive: おまじない
構造体や enum に #[derive(...)] を付けると便利な機能が自動で追加される:
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 1.0, y: 2.0 };
// Debug: {:?} で中身を表示できる
println!("{:?}", p); // Point { x: 1.0, y: 2.0 }
// Clone: .clone() でコピーできる
let p2 = p.clone();
// PartialEq: == で比較できる
println!("{}", p == p2); // true
}
| derive | 何ができるようになるか |
|---|---|
Debug |
{:?} で中身を表示 |
Clone |
.clone() でコピー |
PartialEq |
== で比較 |
とりあえずこの 3 つをセットで付けておけば困らない.
enum にも同じように使える:
#[derive(Debug, Clone, PartialEq)]
enum Color {
Red,
Green,
Blue,
}
fn main() {
let c = Color::Red;
println!("{:?}", c); // Red
}
これで自分のデータ型を作れるようになった.次は Vec や String といった「動的なやつら」と,Rust 特有の「所有権っぽい何か」に触れる.