Menu
Coddy logo textTech

パラメータを持つトレイト

CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 33/61。

これまで、私たちのトレイトメソッドは&selfのみをパラメータとして受け取ってきました。しかし、トレイトメソッドは通常のメソッドと同じように追加のパラメータを受け取ることができます。これにより、実際の操作においてより便利になります。

トレイトを定義する際、メソッドのシグネチャに必要なパラメータを含めることができます。

trait Calculator {
    fn add(&self, a: i32, b: i32) -> i32;
    fn multiply(&self, a: i32, b: i32) -> i32;
}

このCalculatorトレイトは、&selfに加えてそれぞれ2つの整数を受け取る2つのメソッドを要求します。このトレイトを実装する任意の型は、これらと全く同じシグネチャを持つ両方の操作を提供しなければなりません。

構造体でこれを実装する方法は次のとおりです。

struct BasicCalc;

impl Calculator for BasicCalc {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    
    fn multiply(&self, a: i32, b: i32) -> i32 {
        a * b
    }
}

これで、これらのメソッドを引数と共に使用できるようになります:

let calc = BasicCalc;
println!("{}", calc.add(5, 3));       // 8
println!("{}", calc.multiply(4, 7));  // 28

トレイトは各メソッドがどのようなパラメータを受け取るかを定義し、実装はそれらのパラメータがどのように使用されるかを定義します。これにより、異なる型が同じ操作を独自の方法で実行できるようになります。例えば、LoggingCalc は結果を返す前に各操作を出力することができます。

challenge icon

チャレンジ

簡単

異なるプロセッサが独自の方法でテキストを変換できる、文字列操作ツールキットを構築しましょう!TextProcessorトレイトを作成し、パラメータを受け取るメソッドを定義してから、2つの異なるプロセッサタイプに対してそれを実装します。

コードは3つのファイルに分けて構成します:

  • processor.rs: パラメータを受け取る2つのメソッドを持つ、パブリックな TextProcessor トレイトを定義します:
    • repeat(&self, text: &str, times: u32) -> String — 指定された回数だけテキストを繰り返します
    • truncate(&self, text: &str, max_len: usize) -> String — テキストを指定された最大長に短縮します
  • processors.rs: トレイトを実装する2つのパブリックな構造体を作成します:
    • SimpleProcessor — 各繰り返しの間にスペースを入れてテキストを繰り返し(例: "Hi" を3回繰り返すと "Hi Hi Hi" になる)、最大長で単純にカットして短縮するユニット構造体です
    • FancyProcessor — 各繰り返しの間に " * " を入れてテキストを繰り返し(例: "Hi" を3回繰り返すと "Hi * Hi * Hi" になる)、テキストが短縮された場合(元のテキストが max_len より長い場合のみ)に "..." を追加して短縮するユニット構造体です
  • main.rs: モジュールをまとめ、使用するプロセッサによって同じトレイトメソッドがどのように異なる結果を生み出すかを実演します。

メインファイルで両方のプロセッサを作成し、入力を使用してテストします。3つの入力(テキスト文字列、繰り返し回数、最大長)を受け取ります。

各プロセッサの動作を示す4行を出力してください:

Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}

例えば、入力が Hello34 の場合:

Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...

両方のプロセッサが同じ TextProcessor の規約を満たしていますが、それぞれが独自の方法でテキストを変換していることに注目してください。トレイトはメソッドが受け取るパラメータ(what)を定義し、各実装はそれらをどのように使用するか(how)を決定します!

処理するテキスト、繰り返し回数(u32 としてパース)、および短縮のための最大長(usize としてパース)の3つの入力を受け取ります。

チートシート

トレイトのメソッドは、&self 以外にも追加のパラメータを受け取ることができます:

trait Calculator {
    fn add(&self, a: i32, b: i32) -> i32;
    fn multiply(&self, a: i32, b: i32) -> i32;
}

パラメータを持つトレイトの実装:

struct BasicCalc;

impl Calculator for BasicCalc {
    fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
    
    fn multiply(&self, a: i32, b: i32) -> i32 {
        a * b
    }
}

引数を使用してトレイトのメソッドを呼び出す:

let calc = BasicCalc;
println!("{}", calc.add(5, 3));       // 8
println!("{}", calc.multiply(4, 7));  // 28

トレイトは各メソッドが受け取るパラメータが何であるかを定義し、実装はそれらのパラメータがどのように使用されるかを定義します。

自分で試してみよう

mod processor;
mod processors;

use processor::TextProcessor;
use processors::{SimpleProcessor, FancyProcessor};

fn main() {
    // 入力を読み込む
    let mut text = String::new();
    std::io::stdin().read_line(&mut text).expect("Failed to read line");
    let text = text.trim();
    
    let mut times_input = String::new();
    std::io::stdin().read_line(&mut times_input).expect("Failed to read line");
    let times: u32 = times_input.trim().parse().expect("Failed to parse times");
    
    let mut max_len_input = String::new();
    std::io::stdin().read_line(&mut max_len_input).expect("Failed to read line");
    let max_len: usize = max_len_input.trim().parse().expect("Failed to parse max_len");
    
    // プロセッサを作成する
    let simple = SimpleProcessor;
    let fancy = FancyProcessor;
    
    // TODO: プロセッサを使用してテキストを変換し、結果を出力する
    // 4行出力する:
    // Simple repeat: {result}
    // Simple truncate: {result}
    // Fancy repeat: {result}
    // Fancy truncate: {result}
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン