|
| 1 | +Return types cannot be `dyn Trait`s as they must be `Sized`. |
| 2 | + |
| 3 | +Erroneous code example: |
| 4 | + |
| 5 | +```compile_fail,E0746 |
| 6 | +trait T { |
| 7 | + fn bar(&self); |
| 8 | +} |
| 9 | +struct S(usize); |
| 10 | +impl T for S { |
| 11 | + fn bar(&self) {} |
| 12 | +} |
| 13 | +
|
| 14 | +// Having the trait `T` as return type is invalid because bare traits do not |
| 15 | +have a statically known size: |
| 16 | +fn foo() -> dyn T { |
| 17 | + S(42) |
| 18 | +} |
| 19 | +``` |
| 20 | + |
| 21 | +To avoid the error there are a couple of options. |
| 22 | + |
| 23 | +If there is a single type involved, you can use [`impl Trait`]: |
| 24 | + |
| 25 | +``` |
| 26 | +# trait T { |
| 27 | +# fn bar(&self); |
| 28 | +# } |
| 29 | +# struct S(usize); |
| 30 | +# impl T for S { |
| 31 | +# fn bar(&self) {} |
| 32 | +# } |
| 33 | +// The compiler will select `S(usize)` as the materialized return type of this |
| 34 | +// function, but callers will only be able to access associated items from `T`. |
| 35 | +fn foo() -> impl T { |
| 36 | + S(42) |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +If there are multiple types involved, the only way you care to interact with |
| 41 | +them is through the trait's interface and having to rely on dynamic dispatch is |
| 42 | +acceptable, then you can use [trait objects] with `Box`, or other container |
| 43 | +types like `Rc` or `Arc`: |
| 44 | + |
| 45 | +``` |
| 46 | +# trait T { |
| 47 | +# fn bar(&self); |
| 48 | +# } |
| 49 | +# struct S(usize); |
| 50 | +# impl T for S { |
| 51 | +# fn bar(&self) {} |
| 52 | +# } |
| 53 | +struct O(&'static str); |
| 54 | +impl T for O { |
| 55 | + fn bar(&self) {} |
| 56 | +} |
| 57 | +
|
| 58 | +// This now returns a "trait object" and callers are only be able to access |
| 59 | +// associated items from `T`. |
| 60 | +fn foo(x: bool) -> Box<dyn T> { |
| 61 | + if x { |
| 62 | + Box::new(S(42)) |
| 63 | + } else { |
| 64 | + Box::new(O("val")) |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +Finally, if you wish to still be able to access the original type, you can |
| 70 | +create a new `enum` with a variant for each type: |
| 71 | + |
| 72 | +``` |
| 73 | +# trait T { |
| 74 | +# fn bar(&self); |
| 75 | +# } |
| 76 | +# struct S(usize); |
| 77 | +# impl T for S { |
| 78 | +# fn bar(&self) {} |
| 79 | +# } |
| 80 | +# struct O(&'static str); |
| 81 | +# impl T for O { |
| 82 | +# fn bar(&self) {} |
| 83 | +# } |
| 84 | +enum E { |
| 85 | + S(S), |
| 86 | + O(O), |
| 87 | +} |
| 88 | +
|
| 89 | +// The caller can access the original types directly, but it needs to match on |
| 90 | +// the returned `enum E`. |
| 91 | +fn foo(x: bool) -> E { |
| 92 | + if x { |
| 93 | + E::S(S(42)) |
| 94 | + } else { |
| 95 | + E::O(O("val")) |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +You can even implement the `trait` on the returned `enum` so the callers |
| 101 | +*don't* have to match on the returned value to invoke the associated items: |
| 102 | + |
| 103 | +``` |
| 104 | +# trait T { |
| 105 | +# fn bar(&self); |
| 106 | +# } |
| 107 | +# struct S(usize); |
| 108 | +# impl T for S { |
| 109 | +# fn bar(&self) {} |
| 110 | +# } |
| 111 | +# struct O(&'static str); |
| 112 | +# impl T for O { |
| 113 | +# fn bar(&self) {} |
| 114 | +# } |
| 115 | +# enum E { |
| 116 | +# S(S), |
| 117 | +# O(O), |
| 118 | +# } |
| 119 | +impl T for E { |
| 120 | + fn bar(&self) { |
| 121 | + match self { |
| 122 | + E::S(s) => s.bar(), |
| 123 | + E::O(o) => o.bar(), |
| 124 | + } |
| 125 | + } |
| 126 | +} |
| 127 | +``` |
| 128 | + |
| 129 | +If you decide to use trait objects, be aware that these rely on |
| 130 | +[dynamic dispatch], which has performance implications, as the compiler needs |
| 131 | +to emit code that will figure out which method to call *at runtime* instead of |
| 132 | +during compilation. Using trait objects we are trading flexibility for |
| 133 | +performance. |
| 134 | + |
| 135 | +[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits |
| 136 | +[trait objects]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types |
| 137 | +[dynamic dispatch]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch |
0 commit comments