構造体の定義
// 構造体の定義
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
// インスタンス化
let user = User {
name: String::from("太郎"),
email: String::from("taro@example.com"),
age: 25,
active: true,
};
// フィールドへのアクセス
println!("{}", user.name);
可変な構造体
let mut user = User {
name: String::from("太郎"),
email: String::from("taro@example.com"),
age: 25,
active: true,
};
user.age = 26;
フィールド初期化省略記法
fn create_user(name: String, email: String) -> User {
User {
name, // name: name の省略
email, // email: email の省略
age: 0,
active: true,
}
}
構造体更新記法
let user1 = User {
name: String::from("太郎"),
email: String::from("taro@example.com"),
age: 25,
active: true,
};
// user1 の一部を変更してuser2を作成
let user2 = User {
email: String::from("taro2@example.com"),
..user1 // 残りのフィールドはuser1から
};
タプル構造体
struct Color(u8, u8, u8);
struct Point(f64, f64, f64);
let black = Color(0, 0, 0);
let origin = Point(0.0, 0.0, 0.0);
println!("R: {}", black.0);
println!("x: {}", origin.0);
ユニット構造体
struct AlwaysEqual;
let subject = AlwaysEqual;
メソッド
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// メソッド
fn area(&self) -> u32 {
self.width * self.height
}
fn perimeter(&self) -> u32 {
2 * (self.width + self.height)
}
// 可変参照を取るメソッド
fn double(&mut self) {
self.width *= 2;
self.height *= 2;
}
// 所有権を取るメソッド
fn into_square(self) -> Rectangle {
let side = self.width.max(self.height);
Rectangle { width: side, height: side }
}
}
let mut rect = Rectangle { width: 10, height: 5 };
println!("面積: {}", rect.area());
println!("周囲: {}", rect.perimeter());
rect.double();
println!("2倍後の面積: {}", rect.area());
関連関数(コンストラクタ)
impl Rectangle {
// 関連関数(selfを取らない)
fn new(width: u32, height: u32) -> Rectangle {
Rectangle { width, height }
}
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
let rect = Rectangle::new(10, 5);
let square = Rectangle::square(10);
複数のimplブロック
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
列挙型(enum)
enum Direction {
North,
South,
East,
West,
}
let dir = Direction::North;
match dir {
Direction::North => println!("北"),
Direction::South => println!("南"),
Direction::East => println!("東"),
Direction::West => println!("西"),
}
データを持つ列挙型
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => println!("終了"),
Message::Move { x, y } => println!("移動: ({}, {})", x, y),
Message::Write(text) => println!("書き込み: {}", text),
Message::ChangeColor(r, g, b) => println!("色: ({}, {}, {})", r, g, b),
}
列挙型のメソッド
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("終了します"),
Message::Move { x, y } => println!("({}, {})に移動", x, y),
Message::Write(text) => println!("{}", text),
Message::ChangeColor(r, g, b) => println!("色を変更: RGB({},{},{})", r, g, b),
}
}
}
Option と Result
// Option<T>
enum Option<T> {
Some(T),
None,
}
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("太郎"))
} else {
None
}
}
match find_user(1) {
Some(name) => println!("見つかりました: {}", name),
None => println!("見つかりませんでした"),
}
// Result<T, E>
enum Result<T, E> {
Ok(T),
Err(E),
}
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("ゼロ除算"))
} else {
Ok(a / b)
}
}
match divide(10, 2) {
Ok(result) => println!("結果: {}", result),
Err(e) => println!("エラー: {}", e),
}
Option/Result のメソッド
let some_value: Option<i32> = Some(5);
// unwrap系
let value = some_value.unwrap(); // Noneならパニック
let value = some_value.unwrap_or(0); // Noneなら0
let value = some_value.unwrap_or_default(); // Noneならデフォルト値
// map
let doubled = some_value.map(|x| x * 2); // Some(10)
// and_then(フラットマップ)
let result = some_value.and_then(|x| {
if x > 0 { Some(x * 2) } else { None }
});
// is_some / is_none
if some_value.is_some() {
println!("値があります");
}
派生属性
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone();
println!("{:?}", p1); // Debug
println!("{}", p1 == p2); // PartialEq
実践例:銀行口座
#[derive(Debug)]
struct BankAccount {
account_number: String,
owner: String,
balance: f64,
}
#[derive(Debug)]
enum TransactionError {
InsufficientFunds,
InvalidAmount,
}
impl BankAccount {
fn new(owner: &str, initial_balance: f64) -> Self {
static mut COUNTER: u32 = 1000;
let account_number = unsafe {
COUNTER += 1;
format!("ACC-{}", COUNTER)
};
BankAccount {
account_number,
owner: owner.to_string(),
balance: initial_balance,
}
}
fn deposit(&mut self, amount: f64) -> Result<f64, TransactionError> {
if amount <= 0.0 {
return Err(TransactionError::InvalidAmount);
}
self.balance += amount;
Ok(self.balance)
}
fn withdraw(&mut self, amount: f64) -> Result<f64, TransactionError> {
if amount <= 0.0 {
return Err(TransactionError::InvalidAmount);
}
if amount > self.balance {
return Err(TransactionError::InsufficientFunds);
}
self.balance -= amount;
Ok(self.balance)
}
}
fn main() {
let mut account = BankAccount::new("山田太郎", 10000.0);
println!("{:?}", account);
match account.deposit(5000.0) {
Ok(balance) => println!("入金後残高: {}", balance),
Err(e) => println!("エラー: {:?}", e),
}
match account.withdraw(3000.0) {
Ok(balance) => println!("出金後残高: {}", balance),
Err(e) => println!("エラー: {:?}", e),
}
}
まとめ
structで名前付きフィールドの構造体- タプル構造体で位置ベースのフィールド
implでメソッドと関連関数を定義enumで列挙型を定義Option<T>で値の有無を表現Result<T, E>で成功/失敗を表現#[derive(...)]で自動実装
次回はトレイトについて学びます。