Skip to content

Commit 6fd0e82

Browse files
committed
Update question 5 for universe transition
1 parent f59fa1c commit 6fd0e82

File tree

3 files changed

+41
-27
lines changed

3 files changed

+41
-27
lines changed

docs/questions.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
1-
Answer: 112
2-
Difficulty: 2
1+
Answer: error
2+
Difficulty: 3
33

44
# Hint
55

6-
If you are familiar with [higher-rank trait bound][hrtb] syntax, try desugaring
7-
all the types in the impl signatures and types in `main` into their fully
8-
explicit form.
9-
10-
[hrtb]: https://doc.rust-lang.org/nomicon/hrtb.html
6+
The answer is different for Rust versions 1.0 through 1.32 vs 1.33+. The answer
7+
accepted as correct here is the one for compilers 1.33+.
118

129
# Explanation
1310

11+
This is a rare example of a Rust program that *used to* compile. This code
12+
compiles and runs successfully with every Rust version 1.0 through 1.32,
13+
printing the output `112`. The reasoning on those compilers is as follows.
14+
1415
The first impl applies to function pointers of type `fn(T)` where `T` is any
1516
single concrete type. The second impl applies to function pointers of
16-
higher-ranked type `for<'a> fn(&'a T)` for some concrete type `T` that outlives
17-
`'a`.
17+
[higher-ranked] type `for<'a> fn(&'a T)` for some concrete type `T` that
18+
outlives `'a`.
19+
20+
[higher-ranked]: https://doc.rust-lang.org/nomicon/hrtb.html
1821

1922
Inside of `main`, the compiler is going to use type inference to substitute all
2023
occurrences of `_` in a type by some concrete type.
2124

22-
For the closure `a` we infer `_ = u8`, yielding the closure type `fn(u8)` taking
23-
an argument of type `u8` and returning `()`.
25+
For the function pointer `a` we infer `_ = u8`, yielding the function pointer
26+
type `fn(u8)` taking an argument of type `u8` and returning `()`.
2427

2528
For `b` we infer `_ = &'x u8` for some concrete lifetime `'x` that will
2629
ultimately feed into the borrow checker. The type of `b` is `fn(&'x u8)`.
2730

28-
And finally for `c` we infer `_ = u8`, yielding the higher-ranked closure type
29-
`for<'a> fn(&'a u8)`.
31+
And finally for `c` we infer `_ = u8`, yielding the higher-ranked function
32+
pointer type `for<'a> fn(&'a u8)`.
3033

3134
Framed in this way, it follows that the trait method calls at the end of `main`
3235
print `112`.
36+
37+
The compiler's reasoning changed in Rust version 1.33 as part of the ["universe
38+
transition"] and this program no longer compiles. Under the new model the first
39+
impl applies to all three function pointers. If the second impl didn't exist,
40+
the program would compile and print `111`. But with both impls present these are
41+
considered conflicting impls and the program fails to compile.
42+
43+
["universe transition"]: https://github.com/rust-lang/rust/issues/56105
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
trait Trait {
2-
fn f(self);
2+
fn p(self);
33
}
44

55
impl<T> Trait for fn(T) {
6-
fn f(self) {
6+
fn p(self) {
77
print!("1");
88
}
99
}
1010

1111
impl<T> Trait for fn(&T) {
12-
fn f(self) {
12+
fn p(self) {
1313
print!("2");
1414
}
1515
}
1616

17+
fn f(_: u8) {}
18+
fn g(_: &u8) {}
19+
1720
fn main() {
18-
let a: fn(_) = |_: u8| {};
19-
let b: fn(_) = |_: &u8| {};
20-
let c: fn(&_) = |_: &u8| {};
21-
a.f();
22-
b.f();
23-
c.f();
21+
let a: fn(_) = f;
22+
let b: fn(_) = g;
23+
let c: fn(&_) = g;
24+
a.p();
25+
b.p();
26+
c.p();
2427
}

0 commit comments

Comments
 (0)