about summary refs log tree commit diff
path: root/src/librustc_error_codes/error_codes
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc_error_codes/error_codes')
-rw-r--r--src/librustc_error_codes/error_codes/E0746.md138
1 files changed, 138 insertions, 0 deletions
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..16b2722f0ea
--- /dev/null
+++ b/src/librustc_error_codes/error_codes/E0746.md
@@ -0,0 +1,138 @@
+Return types cannot be `dyn Trait`s as they must be `Sized`.
+
+Erroneous code example:
+
+```compile_fail,E0277
+# // FIXME: after E0746 is in beta, change the above
+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 {
+    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 know that the return type implements `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<dyn T> {
+    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