Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

トレイト(Trait)

共通の振る舞いを定義する「トレイト」について学びます。

トレイトとは

トレイトは型が持つべき振る舞い(メソッド)を定義します。他の言語の「インターフェース」に似ています。

#![allow(unused)]
fn main() {
trait Summary {
    fn summarize(&self) -> String;
}
}

トレイトの定義と実装

トレイトを定義

#![allow(unused)]
fn main() {
trait Greet {
    fn greet(&self) -> String;
}
}

型にトレイトを実装

struct Person {
    name: String,
}

impl Greet for Person {
    fn greet(&self) -> String {
        format!("こんにちは、{}です!", self.name)
    }
}

struct Robot {
    id: u32,
}

impl Greet for Robot {
    fn greet(&self) -> String {
        format!("ピポパポ、ロボット{}号です", self.id)
    }
}

fn main() {
    let person = Person { name: String::from("太郎") };
    let robot = Robot { id: 42 };

    println!("{}", person.greet());
    println!("{}", robot.greet());
}

デフォルト実装

トレイトにデフォルトの実装を提供できます。

#![allow(unused)]
fn main() {
trait Summary {
    fn summarize(&self) -> String {
        String::from("(詳細なし)")
    }
}

struct Article {
    title: String,
    content: String,
}

// デフォルト実装をそのまま使う
impl Summary for Article {}

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

// カスタム実装で上書き
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("@{}: {}", self.username, self.text)
    }
}
}

トレイト境界(引数で使う)

「このトレイトを実装した型」を引数に取れます。

#![allow(unused)]
fn main() {
// impl Trait 構文(簡潔)
fn notify(item: &impl Summary) {
    println!("速報!{}", item.summarize());
}

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

複数のトレイト境界

#![allow(unused)]
fn main() {
fn notify(item: &(impl Summary + Display)) {
    // SummaryとDisplayの両方を実装した型のみ
}

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

where句(複雑な場合)

#![allow(unused)]
fn main() {
fn some_function<T, U>(t: &T, u: &U)
where
    T: Summary + Clone,
    U: Clone + Debug,
{
    // ...
}
}

トレイトを返す

#![allow(unused)]
fn main() {
fn create_summarizable() -> impl Summary {
    Tweet {
        username: String::from("user"),
        text: String::from("Hello!"),
    }
}
}

標準ライブラリの重要なトレイト

Debug - デバッグ出力

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("{:?}", p);
}

Clone - 明示的なコピー

#[derive(Clone)]
struct Data {
    value: String,
}

fn main() {
    let d1 = Data { value: String::from("hello") };
    let d2 = d1.clone();
}

PartialEq - 等価比較

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

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point { x: 1, y: 2 };
    println!("{}", p1 == p2);  // true
}

Display - ユーザー向け表示

use std::fmt;

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

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

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("{}", p);  // (1, 2)
}

Default - デフォルト値

#[derive(Default)]
struct Config {
    debug: bool,
    max_connections: u32,
}

fn main() {
    let config = Config::default();
    // debug: false, max_connections: 0
}

derive マクロ

よく使うトレイトはderiveで自動実装できます。

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, PartialEq, Default)]
struct User {
    name: String,
    age: u32,
}
}

実践例: 動物の鳴き声

trait Animal {
    fn name(&self) -> &str;
    fn speak(&self) -> String;

    fn introduce(&self) -> String {
        format!("私は{}です。{}", self.name(), self.speak())
    }
}

struct Dog {
    name: String,
}

impl Animal for Dog {
    fn name(&self) -> &str {
        &self.name
    }

    fn speak(&self) -> String {
        String::from("ワンワン!")
    }
}

struct Cat {
    name: String,
}

impl Animal for Cat {
    fn name(&self) -> &str {
        &self.name
    }

    fn speak(&self) -> String {
        String::from("ニャー")
    }
}

fn main() {
    let dog = Dog { name: String::from("ポチ") };
    let cat = Cat { name: String::from("タマ") };

    println!("{}", dog.introduce());
    println!("{}", cat.introduce());
}

まとめ

概念説明
トレイト共通の振る舞いを定義
impl Trait for Type型にトレイトを実装
デフォルト実装トレイト内でデフォルトのメソッドを提供
トレイト境界「このトレイトを実装した型」という制約
derive標準トレイトの自動実装

確認テスト

Q1. トレイトの役割は?

Q2. #[derive(Debug)]の効果は?

Q3. 以下のコードで、Silent.speak()Loud.speak()の出力は?
trait Speak { fn speak(&self) -> String { String::from("...") } } struct Silent; impl Speak for Silent {} struct Loud; impl Speak for Loud { fn speak(&self) -> String { String::from("HELLO!") } }

Q4. fn show(item: Printable)がエラーになる理由は?

Q5. トレイト境界T: Clone + Debugは何を意味する?


次のドキュメント: 04_generics.md