diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index c17cb7dd9f1..180ccb15977 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -414,6 +414,7 @@ E0742: include_str!("./error_codes/E0742.md"), E0743: include_str!("./error_codes/E0743.md"), E0744: include_str!("./error_codes/E0744.md"), E0745: include_str!("./error_codes/E0745.md"), +E0746: include_str!("./error_codes/E0746.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard @@ -608,5 +609,4 @@ E0745: include_str!("./error_codes/E0745.md"), E0726, // non-explicit (not `'_`) elided lifetime in unsupported position E0727, // `async` generators are not yet supported E0739, // invalid track_caller application/syntax - E0746, // `dyn Trait` return type } diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md new file mode 100644 index 00000000000..538c9d720d7 --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0746.md @@ -0,0 +1,137 @@ +Return types cannot be `dyn Trait`s as they must be `Sized`. + +Erroneous code example: + +```compile_fail,E0746 +trait T { + fn bar(&self); +} +struct S(usize); +impl T for S { + fn bar(&self) {} +} + +// Having the trait `T` as return type is invalid because bare traits do not +have a statically known size: +fn foo() -> dyn T { + S(42) +} +``` + +To avoid the error there are a couple of options. + +If there is a single type involved, you can use [`impl Trait`]: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +// The compiler will select `S(usize)` as the materialized return type of this +// function, but callers will only be able to access associated items from `T`. +fn foo() -> impl T { + S(42) +} +``` + +If there are multiple types involved, the only way you care to interact with +them is through the trait's interface and having to rely on dynamic dispatch is +acceptable, then you can use [trait objects] with `Box`, or other container +types like `Rc` or `Arc`: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +struct O(&'static str); +impl T for O { + fn bar(&self) {} +} + +// This now returns a "trait object" and callers are only be able to access +// associated items from `T`. +fn foo(x: bool) -> Box { + if x { + Box::new(S(42)) + } else { + Box::new(O("val")) + } +} +``` + +Finally, if you wish to still be able to access the original type, you can +create a new `enum` with a variant for each type: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +# struct O(&'static str); +# impl T for O { +# fn bar(&self) {} +# } +enum E { + S(S), + O(O), +} + +// The caller can access the original types directly, but it needs to match on +// the returned `enum E`. +fn foo(x: bool) -> E { + if x { + E::S(S(42)) + } else { + E::O(O("val")) + } +} +``` + +You can even implement the `trait` on the returned `enum` so the callers +*don't* have to match on the returned value to invoke the associated items: + +``` +# trait T { +# fn bar(&self); +# } +# struct S(usize); +# impl T for S { +# fn bar(&self) {} +# } +# struct O(&'static str); +# impl T for O { +# fn bar(&self) {} +# } +# enum E { +# S(S), +# O(O), +# } +impl T for E { + fn bar(&self) { + match self { + E::S(s) => s.bar(), + E::O(o) => o.bar(), + } + } +} +``` + +If you decide to use trait objects, be aware that these rely on +[dynamic dispatch], which has performance implications, as the compiler needs +to emit code that will figure out which method to call *at runtime* instead of +during compilation. Using trait objects we are trading flexibility for +performance. + +[`impl Trait`]: https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits +[trait objects]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types +[dynamic dispatch]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch diff --git a/src/test/ui/error-codes/E0746.stderr b/src/test/ui/error-codes/E0746.stderr index 0cffd108226..1c88ce64749 100644 --- a/src/test/ui/error-codes/E0746.stderr +++ b/src/test/ui/error-codes/E0746.stderr @@ -24,3 +24,4 @@ LL | fn bar() -> impl Trait { error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0746`. diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index d51cd6aeae6..ff7438e9aff 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -116,5 +116,5 @@ LL | fn bat() -> impl Trait { error: aborting due to 9 previous errors -Some errors have detailed explanations: E0277, E0308. +Some errors have detailed explanations: E0277, E0308, E0746. For more information about an error, try `rustc --explain E0277`.