函数参数实现两个特质

huangapple go评论93阅读模式
英文:

Function argument implementing two traits

问题

pub trait Router {
    fn route(&self);
}

pub trait Library {
    fn do_math(&self);
}

pub struct Pair1 {}

impl Library for Pair1 {
    fn do_math(&self) {
        println!("Doing math");
    }
}

impl Router for Pair1 {
    fn route(&self) {
        println!("Routing");
    }
}

pub struct Pair2 {}

impl Library for Pair2 {
    fn do_math(&self) {
        println!("Doin math");
    }
}

impl Router for Pair2 {
    fn route(&self) {
        println!("Routin");
    }
}

pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}

pub fn my_fn(pair: &mut Box<dyn RouterAndLibrary>) {
    println!("Hello");
}

pub fn wrapper_fn() {
    let pair = Pair1 {};
    my_fn(&mut Box::new(pair));
}
英文:

So let's take this code with two structs implementing two traits. How can I make a function that takes as argument any struct that implement both traits ?

pub trait Router {
    fn route(&amp;self);
}

pub trait Library {
    fn do_math(&amp;self);
}
pub struct Pair1 {}

impl Library for Pair1 {
    fn do_math(&amp;self) {
        println!(&quot;Doing math&quot;);
    }
}

impl Router for Pair1 {
    fn route(&amp;self) {
        println!(&quot;Routing&quot;);
    }
}

pub struct Pair2 {}

impl Library for Pair2 {
    fn do_math(&amp;self) {
        println!(&quot;Doin math&quot;);
    }
}

impl Router for Pair2 {
    fn route(&amp;self) {
        println!(&quot;Routin&quot;);
    }
}

Here is the solution I tried:


pub trait RouterAndLibrary: Router + Library {}
impl&lt;T: Router + Library&gt; RouterAndLibrary for T {}

pub fn my_fn(
    pair: &amp;mut Box&lt;dyn RouterAndLibrary&gt;
) {
    println!(&quot;Hello&quot;);
}

pub fn wrapper_fn() {
    let pair = Pair1 {};
    my_fn(pair);
}

But this throws an error: mismatched types expected mutable reference &quot;&amp;mut Box&lt;(dyn RouterAndLibrary + &#39;static)&gt; found struct Pair1.

答案1

得分: 3

如评论中所指出,尽量在可能的情况下使用泛型而不是特征对象。静态分发的泛型使这种情况变得容易。

pub fn my_fn<T: Router + Library>(pair: &T) {
    println!("Hello");
}

这是简单的解决方案,通常情况下,应该优先考虑使用泛型。

如果你确实需要一个 dyn 特征对象,那么你已经发现了最佳的方法。在特征对象类型中不能要求多个特征(例外情况是 SendSync,它们已经内置到编译器中),因为宽指针中的虚拟分派表与单个特征关联。所以我们需要创建一个新的特征,其中包含正确的分派信息。我们可以很容易地这样做,而且还可以提供一个通用实现,就像你已经发现的那样。

pub trait RouterAndLibrary: Router + Library {}
impl<T: Router + Library> RouterAndLibrary for T {}

你对 Box 有点过分了。引用本身可以包含不定大小的类型,所以你的 dyn 解决方案将如下所示。

pub fn my_fn(pair: &dyn RouterAndLibrary) {
    println!("Hello");
}

在任何情况下,我们都可以这样调用函数

my_fn(&pair);

前一个函数签名将进行单态化(monomorphize),而后者将自动为我们生成一个特征对象,无需显式转换。

英文:

As noted in the comments, you should probably favor generics instead of trait objects when possible. Statically-dispatched generics make this situation easy.

pub fn my_fn&lt;T: Router + Library&gt;(pair: &amp;T) {
    println!(&quot;Hello&quot;);
}

That's the easy solution, and generally you should favor generics when possibly.

If you truly need a dyn trait object, then you've already discovered the best way to do it. You can't require multiple traits in a trait object type (with the exception of Send and Sync, which are baked into the compiler), because the virtual dispatch table in the wide pointer is associated to a single trait. so we need to make a new trait that contains the right dispatch information. We can easily do that and provide a blanket implementation, as you've already discovered.

pub trait RouterAndLibrary: Router + Library {}
impl&lt;T: Router + Library&gt; RouterAndLibrary for T {}

You just got a little carried away with Box. References themselves can contain unsized types, so your dyn solution would look like this.

pub fn my_fn(pair: &amp;dyn RouterAndLibrary) {
    println!(&quot;Hello&quot;);
}

In either case, we can call the function as

my_fn(&amp;pair);

The former function signature will monomorphize, and the latter will automatically produce a trait object for us with no explicit cast required.

huangapple
  • 本文由 发表于 2023年8月4日 05:16:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76831654.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定