扩展关于战略模式实施的 SO 回答问题

Extending On An SO Answered Question About Strategy Pattern Implementation

正如曾经如此有用且无处不在的 Shepmaster 所回答的那样,有人可以帮助我解决我遇到的语法障碍吗? 在之前的answerStrategy::execute()Context::do_things()return中().

如果 returned 泛型类型,如何实现?还是我错过了 Rust 的一些基本观点?

我尝试了以下代码,但目前卡在了:


struct Context<S> {
    strategy: S,
}

impl<S> Context<S>
where
    S: Strategy,
{
    fn do_things(&self) -> T {
        println!("Common preamble");
        self.strategy.execute()
    }
}

trait Strategy<T> {
    fn execute(&self) -> T;
}

struct ConcreteStrategyA;

impl Strategy<AStruct> for ConcreteStrategyA {
    fn execute(&self) -> AStruct {
        println!("ConcreteStrategyA");
        AStruct::default()
    }
}

struct AStruct {
    id: u32,
}

impl Default for AStruct {
    ...
}

struct ConcreteStrategyB;

impl Strategy<BStruct> for ConcreteStrategyB {
    fn execute(&self) -> BStruct {
        println!("ConcreteStrategyB");
        BStruct::default()
    }
}

struct BStruct {
    id: u32,
}

impl Default for BStruct {
    ...
}

我不知道把 T 放在哪里 Context::do_things() -> T

我环顾四周,还有一些其他的 samples return ()

在线tinkering

感谢阅读。

我认为更频繁地使用 Strategy<T> 是关键:

impl<S, T> Context<S, T>
where
    S: Strategy<T>,
{
    fn do_things(&self) -> T {
        println!("Common preamble");
        self.strategy.execute()
    }
}

看这里:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a3a14895e22ca57f5f96c855b7046257

根据您要执行的操作,最好使用 associated type 而不是泛型。在关联类型和泛型参数之间进行选择时,如果 caller 可以选择要使用的类型,则应使用泛型。如果 实现 确定类型,则您应该使用关联类型。

虽然有例外(例如Into::into),但大多数情况下,如果一个类型只出现在方法的return中,那么这很好地表明它可能是一个关联类型。 OTOH 如果类型用于参数,那么它很有可能应该是通用的。

在您的情况下,使用关联类型将如下所示:

struct Context<S> {
    strategy: S,
}

impl<S> Context<S>
where
    S: Strategy,
{
    fn do_things(&self) -> S::Output {
        println!("Common preamble");
        self.strategy.execute()
    }
}

trait Strategy {
    type Output;
    fn execute(&self) -> Self::Output;
}

struct ConcreteStrategyA;

impl Strategy for ConcreteStrategyA {
    type Output = AStruct;
    fn execute(&self) -> AStruct {
        println!("ConcreteStrategyA");
        AStruct::default()
    }
}

#[derive (Default)]
struct AStruct {
    id: u32,
}

struct ConcreteStrategyB;

impl Strategy for ConcreteStrategyB {
    type Output = BStruct;
    fn execute(&self) -> BStruct {
        println!("ConcreteStrategyB");
        BStruct::default()
    }
}

#[derive (Default)]
struct BStruct {
    id: u32,
}

Playground