summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_error_codes/src/error_codes/E0746.md
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_error_codes/src/error_codes/E0746.md')
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0746.md139
1 files changed, 139 insertions, 0 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0746.md b/compiler/rustc_error_codes/src/error_codes/E0746.md
new file mode 100644
index 000000000..90755d47f
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0746.md
@@ -0,0 +1,139 @@
+An unboxed trait object was used as a return value.
+
+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
+// unboxed trait objects do not have a statically known size:
+fn foo() -> dyn T { // error!
+ S(42)
+}
+```
+
+Return types cannot be `dyn Trait`s as they must be `Sized`.
+
+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 know that the return type implements `T`.
+fn foo() -> impl T { // ok!
+ 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<dyn T> { // ok!
+ 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