函数参数实现两个特质

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

Function argument implementing two traits

问题

  1. pub trait Router {
  2. fn route(&self);
  3. }
  4. pub trait Library {
  5. fn do_math(&self);
  6. }
  7. pub struct Pair1 {}
  8. impl Library for Pair1 {
  9. fn do_math(&self) {
  10. println!("Doing math");
  11. }
  12. }
  13. impl Router for Pair1 {
  14. fn route(&self) {
  15. println!("Routing");
  16. }
  17. }
  18. pub struct Pair2 {}
  19. impl Library for Pair2 {
  20. fn do_math(&self) {
  21. println!("Doin math");
  22. }
  23. }
  24. impl Router for Pair2 {
  25. fn route(&self) {
  26. println!("Routin");
  27. }
  28. }
  29. pub trait RouterAndLibrary: Router + Library {}
  30. impl<T: Router + Library> RouterAndLibrary for T {}
  31. pub fn my_fn(pair: &mut Box<dyn RouterAndLibrary>) {
  32. println!("Hello");
  33. }
  34. pub fn wrapper_fn() {
  35. let pair = Pair1 {};
  36. my_fn(&mut Box::new(pair));
  37. }
英文:

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 ?

  1. pub trait Router {
  2. fn route(&amp;self);
  3. }
  4. pub trait Library {
  5. fn do_math(&amp;self);
  6. }
  7. pub struct Pair1 {}
  8. impl Library for Pair1 {
  9. fn do_math(&amp;self) {
  10. println!(&quot;Doing math&quot;);
  11. }
  12. }
  13. impl Router for Pair1 {
  14. fn route(&amp;self) {
  15. println!(&quot;Routing&quot;);
  16. }
  17. }
  18. pub struct Pair2 {}
  19. impl Library for Pair2 {
  20. fn do_math(&amp;self) {
  21. println!(&quot;Doin math&quot;);
  22. }
  23. }
  24. impl Router for Pair2 {
  25. fn route(&amp;self) {
  26. println!(&quot;Routin&quot;);
  27. }
  28. }

Here is the solution I tried:

  1. pub trait RouterAndLibrary: Router + Library {}
  2. impl&lt;T: Router + Library&gt; RouterAndLibrary for T {}
  3. pub fn my_fn(
  4. pair: &amp;mut Box&lt;dyn RouterAndLibrary&gt;
  5. ) {
  6. println!(&quot;Hello&quot;);
  7. }
  8. pub fn wrapper_fn() {
  9. let pair = Pair1 {};
  10. my_fn(pair);
  11. }

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

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

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

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

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

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

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

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

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

  1. 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.

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

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.

  1. pub trait RouterAndLibrary: Router + Library {}
  2. 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.

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

In either case, we can call the function as

  1. 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:

确定