チュートリアル

Rust基礎:トレイト

Rust入門トレイト
広告エリア

トレイトとは

型が持つべき振る舞いを定義する仕組みです。他言語のインターフェースに相当します。

// トレイトの定義
trait Summary {
    fn summarize(&self) -> String;
}

// 構造体
struct Article {
    title: String,
    author: String,
    content: String,
}

// トレイトの実装
impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{} by {}", self.title, self.author)
    }
}

let article = Article {
    title: String::from("Rust入門"),
    author: String::from("太郎"),
    content: String::from("Rustは..."),
};

println!("{}", article.summarize());

デフォルト実装

trait Summary {
    fn summarize(&self) -> String {
        String::from("(続きを読む...)")
    }

    fn summarize_author(&self) -> String;

    // デフォルト実装から他のメソッドを呼び出し
    fn full_summary(&self) -> String {
        format!("{}: {}", self.summarize_author(), self.summarize())
    }
}

struct Tweet {
    username: String,
    content: String,
}

impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }

    // summarize はデフォルト実装を使用
}

トレイト境界

引数としてのトレイト

// impl Trait 構文
fn notify(item: &impl Summary) {
    println!("速報: {}", item.summarize());
}

// トレイト境界構文
fn notify<T: Summary>(item: &T) {
    println!("速報: {}", item.summarize());
}

// 複数のトレイト
fn notify(item: &(impl Summary + Display)) {
    // ...
}

fn notify<T: Summary + Display>(item: &T) {
    // ...
}

where句

fn some_function<T, U>(t: &T, u: &U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{
    // ...
}

戻り値としてのトレイト

fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("user"),
        content: String::from("Hello!"),
    }
}

ジェネリクスとトレイト

use std::cmp::PartialOrd;

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    println!("最大: {}", largest(&numbers));

    let chars = vec!['y', 'm', 'a', 'q'];
    println!("最大: {}", largest(&chars));
}

標準トレイト

Display と Debug

use std::fmt;

struct Point {
    x: f64,
    y: f64,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl fmt::Debug for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Point")
            .field("x", &self.x)
            .field("y", &self.y)
            .finish()
    }
}

let p = Point { x: 1.0, y: 2.0 };
println!("{}", p);    // Display: (1, 2)
println!("{:?}", p);  // Debug: Point { x: 1.0, y: 2.0 }

Clone と Copy

#[derive(Clone)]
struct Person {
    name: String,
    age: u32,
}

let p1 = Person { name: String::from("太郎"), age: 25 };
let p2 = p1.clone();  // 明示的なクローン

// Copy トレイト(スタック上のコピー)
#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1;  // コピー(p1は引き続き使用可能)

PartialEq と Eq

#[derive(PartialEq)]
struct Point {
    x: f64,
    y: f64,
}

let p1 = Point { x: 1.0, y: 2.0 };
let p2 = Point { x: 1.0, y: 2.0 };
println!("{}", p1 == p2);  // true

PartialOrd と Ord

#[derive(PartialEq, PartialOrd)]
struct Score {
    value: i32,
}

let a = Score { value: 85 };
let b = Score { value: 90 };
println!("{}", a < b);  // true

From と Into

struct Wrapper(String);

impl From<String> for Wrapper {
    fn from(s: String) -> Self {
        Wrapper(s)
    }
}

impl From<&str> for Wrapper {
    fn from(s: &str) -> Self {
        Wrapper(s.to_string())
    }
}

let w1: Wrapper = String::from("hello").into();
let w2 = Wrapper::from("world");

トレイトオブジェクト

trait Draw {
    fn draw(&self);
}

struct Circle {
    radius: f64,
}

impl Draw for Circle {
    fn draw(&self) {
        println!("円を描画(半径: {})", self.radius);
    }
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Draw for Rectangle {
    fn draw(&self) {
        println!("長方形を描画({}x{})", self.width, self.height);
    }
}

// トレイトオブジェクトを使用
fn draw_all(shapes: &[&dyn Draw]) {
    for shape in shapes {
        shape.draw();
    }
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let rect = Rectangle { width: 10.0, height: 5.0 };

    let shapes: Vec<&dyn Draw> = vec![&circle, &rect];
    draw_all(&shapes);
}

関連型

trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

struct Counter {
    count: u32,
    max: u32,
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < self.max {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

実践例:シリアライズ可能なトレイト

trait Serialize {
    fn serialize(&self) -> String;
}

trait Deserialize: Sized {
    fn deserialize(data: &str) -> Option<Self>;
}

#[derive(Debug)]
struct User {
    id: u32,
    name: String,
}

impl Serialize for User {
    fn serialize(&self) -> String {
        format!("{}:{}", self.id, self.name)
    }
}

impl Deserialize for User {
    fn deserialize(data: &str) -> Option<Self> {
        let parts: Vec<&str> = data.split(':').collect();
        if parts.len() != 2 {
            return None;
        }
        let id = parts[0].parse().ok()?;
        let name = parts[1].to_string();
        Some(User { id, name })
    }
}

fn main() {
    let user = User { id: 1, name: String::from("太郎") };
    let serialized = user.serialize();
    println!("シリアライズ: {}", serialized);

    if let Some(user2) = User::deserialize(&serialized) {
        println!("デシリアライズ: {:?}", user2);
    }
}

演算子オーバーロード

use std::ops::Add;

#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

let p1 = Point { x: 1.0, y: 2.0 };
let p2 = Point { x: 3.0, y: 4.0 };
let p3 = p1 + p2;
println!("{:?}", p3);  // Point { x: 4.0, y: 6.0 }

まとめ

  • traitで振る舞いを定義
  • impl Trait for Typeで実装
  • デフォルト実装でボイラープレートを削減
  • トレイト境界でジェネリクスを制約
  • impl Traitで戻り値の型を抽象化
  • dyn Traitでトレイトオブジェクト
  • 関連型でトレイト内で型を定義
  • 標準トレイトを実装して機能を追加

これでRust基礎は完了です。次は所有権編でRust特有のメモリ管理を深く学びましょう。

広告エリア