問題解決: 問題を分解する

// 著作権 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

// 問題: GUI API を実装する

// 問い: 描画 API にとって有用な最小限の振る舞いは何でしょうか?
pub trait DrawApi {
    fn arc(&self, center: [f32; 2], radius: f32, start_angle: f32, end_angle: f32);
    fn line(&self, start: [f32; 2], end: [f32; 2]);
}

pub struct TextDraw;

impl DrawApi for TextDraw {
    fn arc(&self, center: [f32; 2], radius: f32, start_angle: f32, end_angle: f32) {
        println!("arc of radius ")
    }

    fn line(&self, start: [f32; 2], end: [f32; 2]) { /* ... */
    }
}

// 問い: ユーザーにとって良い API とは何でしょうか?

pub trait Draw {
    fn draw<T: DrawApi>(&self, surface: &mut T);
}

pub struct Rect {
    start: [f32; 2],
    end: [f32; 2],
}

impl Draw for Rect {
    fn draw<T: DrawApi>(&self, surface: &mut T) {
        surface.line([self.start[0], self.start[1]], [self.end[0], self.start[1]]);
        surface.line([self.end[0], self.start[1]], [self.end[0], self.end[1]]);
        surface.line([self.end[0], self.end[1]], [self.start[0], self.end[1]]);
        surface.line([self.start[0], self.end[1]], [self.start[0], self.start[1]]);
    }
}
  • 問題を分解することにはすでに長けているはずですが、おそらく OOP スタイルの手法に慣れているでしょう。

これは大きな変化ではなく、物事へのアプローチの順序を入れ替える必要がある だけです。

  • まずはジェネリクスとトレイト、または enum のいずれかで問題を解いてみましょう。

    問題に特定の型の集合が必要ですか? その場合、この問題を解く最もクリーンな 方法は enum かもしれません。

    問題は本当に関与する型の詳細を気にしていますか、それとも 振る舞いに注目できますか?

  • 何かを実装するための実用最小限の知識を見つけることを軸に 問題解決を組み立てましょう。

    このユースケースに対して、すでにトレイトは存在しますか? あるなら、それを使いましょう!

  • 本当に異種コレクションが必要なら、使いましょう! Rust にそれが存在するのには 理由があります。

    XY 問題に注意してください: ある問題は 1 つの解決策で最も簡単に対処できる ように見えるかもしれませんが、それでは根本原因に対処できず、新たな 難しい問題が将来現れる原因になるかもしれません。

    つまり、トレイトオブジェクトによる動的ディスパッチが本当に必要なものだと 確信してから、それを使うと決めてください。

    トレイトが本当に必要なものだと確信してから、それを使うと決めてください。