【问题标题】:Is there a way of calling a trait method on enum variants that have a field that implements the trait?有没有一种方法可以在具有实现特征的字段的枚举变体上调用特征方法?
【发布时间】:2022-12-31 10:41:11
【问题描述】:

我的枚举有 40 多个变体,其中大约一半实现了该特征,但这里有一个更简单的例子:

trait CheeseBoard {
    fn say_cheese(self);
}

struct Cheese {
    name: String,
}

impl CheeseBoard for Cheese {
    fn say_cheese(self) {
        println!("I am {}", self.name);
    }
}

struct Person {
    name: String,
}

impl CheeseBoard for Person {
    fn say_cheese(self) {
        println!("{} says cheese!", self.name);
    }
}

enum CheesyPerson {
    Cheese(Cheese),
    Person(Person),
    UncheesyNonperson,
}

fn main() {
    let _a = [
        CheesyPerson::Cheese(Cheese {
            name: "Gouda".into(),
        }),
        CheesyPerson::Person(Person {
            name: "Peer".into(),
        }),
        CheesyPerson::UncheesyNonperson,
    ];
    todo!("Call say_cheese on items in _a where the enum variant has exactly one field that implements the CheeseBoard trait.")
}

【问题讨论】:

  • 如果只有一半的变体具有实现该特征的字段,那么您对另一半做了什么?
  • 您在寻找 match 声明吗? for v in _a { match v { CheesyPerson::Cheese(x) => x.say_cheese(), CheesyPerson::Person(x) => x.say_cheese(), _ => {} } }
  • @Shepmaster 我认为 OP 希望避免列出所有变体的样板。
  • @ChayimFriedman 也许吧!然而,OP没有明确说,当我试图假设他们的意思并以某种方式弄错时,我被太多的 SO 海报大喊大叫。
  • 没有办法在 Rust 中做到这一点,比如说,编写一个只为您编写代码的宏。

标签: rust


【解决方案1】:

这在 Rust 中是不可能的;它甚至没有匹配“具有单一值的任何枚举可能性”的机制,更不用说实现特定特征的机制了。

我能想到的最干净的实现方法是使用返回 Option<&dyn CheeseBoard> 的辅助方法:

impl CheesyPerson {
    fn get_cheese_board(&self) -> Option<&dyn CheeseBoard> {
        match self {
            Self::Cheese(v) => Some(v),
            Self::Person(v) => Some(v),
            _ => None,
        }
    }
}

现在你可以这样做:

for v in _a.iter().filter_map(|v| v.get_cheese_board()) {
    v.say_cheese();
}

请注意,这需要更改您的 CheeseBoard::say_cheese 方法,因为现在它按值获取 self,并在此过程中消耗 CheeseBoard。该方法需要引用self

trait CheeseBoard {
    fn say_cheese(&self);
    //            ^
    // Add this to take self by reference
}

(Playground)

【讨论】:

    【解决方案2】:

    感谢@cdhowie,您的评论使我免于又一个小时的折磨!

    如果有人用另一层结构包装枚举,请确保您枚举,而不是复制它。例如,

    struct RoomForOne(CheesyPerson);
    
    impl RoomForOne {
        fn get_cheese_board(&self) -> Option<&dyn CheeseBoard> {
            match &self.0 { // & here is important!
                CheesyPerson::Cheese(v) => Some(v),
                CheesyPerson::Person(v) => Some(v),
                _ => None,
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2022-11-23
      • 1970-01-01
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-21
      • 1970-01-01
      • 2017-09-05
      相关资源
      最近更新 更多