Skip to content

Commit 4c13d25

Browse files
committed
Add E0746 explanation to the index
1 parent 93293c5 commit 4c13d25

File tree

4 files changed

+140
-2
lines changed

4 files changed

+140
-2
lines changed

src/librustc_error_codes/error_codes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ E0742: include_str!("./error_codes/E0742.md"),
414414
E0743: include_str!("./error_codes/E0743.md"),
415415
E0744: include_str!("./error_codes/E0744.md"),
416416
E0745: include_str!("./error_codes/E0745.md"),
417+
E0746: include_str!("./error_codes/E0746.md"),
417418
;
418419
// E0006, // merged with E0005
419420
// E0008, // cannot bind by-move into a pattern guard
@@ -608,5 +609,4 @@ E0745: include_str!("./error_codes/E0745.md"),
608609
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
609610
E0727, // `async` generators are not yet supported
610611
E0739, // invalid track_caller application/syntax
611-
E0746, // `dyn Trait` return type
612612
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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

src/test/ui/error-codes/E0746.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ LL | fn bar() -> impl Trait {
2424

2525
error: aborting due to 2 previous errors
2626

27+
For more information about this error, try `rustc --explain E0746`.

src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,5 @@ LL | fn bat() -> impl Trait {
116116

117117
error: aborting due to 9 previous errors
118118

119-
Some errors have detailed explanations: E0277, E0308.
119+
Some errors have detailed explanations: E0277, E0308, E0746.
120120
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)