From 6fd0e82ac05c24327bae5c89ae0a6fd8e2a802fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 5 Jan 2019 16:32:40 -0800 Subject: [PATCH] Update question 5 for universe transition --- docs/questions.js | 10 +++---- questions/005-trait-resolution-hrtb.md | 37 +++++++++++++++++--------- questions/005-trait-resolution-hrtb.rs | 21 ++++++++------- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/docs/questions.js b/docs/questions.js index bc1102b..51b5114 100644 --- a/docs/questions.js +++ b/docs/questions.js @@ -28,11 +28,11 @@ var questions = { "explanation": "

This question demonstrates two different meanings of ...

\n

In expression position, .. is the syntax for constructing various types of\nranges. Here the expression (0, 1, ..) is a tuple with three elements, the\nthird one having type RangeFull.

\n

On the other hand in a pattern, .. is used to mean "any number of elements".\nSo the pattern (.., x, y) matches a tuple with 2 or more elements, binding the\nsecond-last one to x and the last one to y.

\n

Coming out of the first line of main, we have x = 1 and y = (..). Thus the\nvalue printed is going to be b"066"[..][1].

\n

The expression b"066" is a byte-string literal of type &'static [u8; 3]\ncontaining the three ASCII bytes b'0', b'6', b'6'.

\n

When we slice the byte-string with RangeFull we get a dynamically sized slice\n[u8] of length 3. Next we access element 1 of the slice, which is the byte\nb'6' of type u8. When printed, we see the decimal representation of the byte\nvalue of the ASCII digit 6, which is the number 54.

\n" }, "5": { - "code": "trait Trait {\n fn f(self);\n}\n\nimpl Trait for fn(T) {\n fn f(self) {\n print!(\"1\");\n }\n}\n\nimpl Trait for fn(&T) {\n fn f(self) {\n print!(\"2\");\n }\n}\n\nfn main() {\n let a: fn(_) = |_: u8| {};\n let b: fn(_) = |_: &u8| {};\n let c: fn(&_) = |_: &u8| {};\n a.f();\n b.f();\n c.f();\n}\n", - "difficulty": 2, - "answer": "112", - "hint": "

If you are familiar with higher-rank trait bound syntax, try desugaring\nall the types in the impl signatures and types in main into their fully\nexplicit form.

\n", - "explanation": "

The first impl applies to function pointers of type fn(T) where T is any\nsingle concrete type. The second impl applies to function pointers of\nhigher-ranked type for<'a> fn(&'a T) for some concrete type T that outlives\n'a.

\n

Inside of main, the compiler is going to use type inference to substitute all\noccurrences of _ in a type by some concrete type.

\n

For the closure a we infer _ = u8, yielding the closure type fn(u8) taking\nan argument of type u8 and returning ().

\n

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

\n

And finally for c we infer _ = u8, yielding the higher-ranked closure type\nfor<'a> fn(&'a u8).

\n

Framed in this way, it follows that the trait method calls at the end of main\nprint 112.

\n" + "code": "trait Trait {\n fn p(self);\n}\n\nimpl Trait for fn(T) {\n fn p(self) {\n print!(\"1\");\n }\n}\n\nimpl Trait for fn(&T) {\n fn p(self) {\n print!(\"2\");\n }\n}\n\nfn f(_: u8) {}\nfn g(_: &u8) {}\n\nfn main() {\n let a: fn(_) = f;\n let b: fn(_) = g;\n let c: fn(&_) = g;\n a.p();\n b.p();\n c.p();\n}\n", + "difficulty": 3, + "answer": "error", + "hint": "

The answer is different for Rust versions 1.0 through 1.32 vs 1.33+. The answer\naccepted as correct here is the one for compilers 1.33+.

\n", + "explanation": "

This is a rare example of a Rust program that used to compile. This code\ncompiles and runs successfully with every Rust version 1.0 through 1.32,\nprinting the output 112. The reasoning on those compilers is as follows.

\n

The first impl applies to function pointers of type fn(T) where T is any\nsingle concrete type. The second impl applies to function pointers of\nhigher-ranked type for<'a> fn(&'a T) for some concrete type T that\noutlives 'a.

\n

Inside of main, the compiler is going to use type inference to substitute all\noccurrences of _ in a type by some concrete type.

\n

For the function pointer a we infer _ = u8, yielding the function pointer\ntype fn(u8) taking an argument of type u8 and returning ().

\n

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

\n

And finally for c we infer _ = u8, yielding the higher-ranked function\npointer type for<'a> fn(&'a u8).

\n

Framed in this way, it follows that the trait method calls at the end of main\nprint 112.

\n

The compiler's reasoning changed in Rust version 1.33 as part of the "universe\ntransition" and this program no longer compiles. Under the new model the first\nimpl applies to all three function pointers. If the second impl didn't exist,\nthe program would compile and print 111. But with both impls present these are\nconsidered conflicting impls and the program fails to compile.

\n" }, "6": { "code": "use std::mem;\n\nfn main() {\n let a;\n let a = a = true;\n print!(\"{}\", mem::size_of_val(&a));\n}\n", diff --git a/questions/005-trait-resolution-hrtb.md b/questions/005-trait-resolution-hrtb.md index 86da19c..b182426 100644 --- a/questions/005-trait-resolution-hrtb.md +++ b/questions/005-trait-resolution-hrtb.md @@ -1,32 +1,43 @@ -Answer: 112 -Difficulty: 2 +Answer: error +Difficulty: 3 # Hint -If you are familiar with [higher-rank trait bound][hrtb] syntax, try desugaring -all the types in the impl signatures and types in `main` into their fully -explicit form. - -[hrtb]: https://doc.rust-lang.org/nomicon/hrtb.html +The answer is different for Rust versions 1.0 through 1.32 vs 1.33+. The answer +accepted as correct here is the one for compilers 1.33+. # Explanation +This is a rare example of a Rust program that *used to* compile. This code +compiles and runs successfully with every Rust version 1.0 through 1.32, +printing the output `112`. The reasoning on those compilers is as follows. + The first impl applies to function pointers of type `fn(T)` where `T` is any single concrete type. The second impl applies to function pointers of -higher-ranked type `for<'a> fn(&'a T)` for some concrete type `T` that outlives -`'a`. +[higher-ranked] type `for<'a> fn(&'a T)` for some concrete type `T` that +outlives `'a`. + +[higher-ranked]: https://doc.rust-lang.org/nomicon/hrtb.html Inside of `main`, the compiler is going to use type inference to substitute all occurrences of `_` in a type by some concrete type. -For the closure `a` we infer `_ = u8`, yielding the closure type `fn(u8)` taking -an argument of type `u8` and returning `()`. +For the function pointer `a` we infer `_ = u8`, yielding the function pointer +type `fn(u8)` taking an argument of type `u8` and returning `()`. For `b` we infer `_ = &'x u8` for some concrete lifetime `'x` that will ultimately feed into the borrow checker. The type of `b` is `fn(&'x u8)`. -And finally for `c` we infer `_ = u8`, yielding the higher-ranked closure type -`for<'a> fn(&'a u8)`. +And finally for `c` we infer `_ = u8`, yielding the higher-ranked function +pointer type `for<'a> fn(&'a u8)`. Framed in this way, it follows that the trait method calls at the end of `main` print `112`. + +The compiler's reasoning changed in Rust version 1.33 as part of the ["universe +transition"] and this program no longer compiles. Under the new model the first +impl applies to all three function pointers. If the second impl didn't exist, +the program would compile and print `111`. But with both impls present these are +considered conflicting impls and the program fails to compile. + +["universe transition"]: https://github.com/rust-lang/rust/issues/56105 diff --git a/questions/005-trait-resolution-hrtb.rs b/questions/005-trait-resolution-hrtb.rs index 4d6d537..7786a25 100644 --- a/questions/005-trait-resolution-hrtb.rs +++ b/questions/005-trait-resolution-hrtb.rs @@ -1,24 +1,27 @@ trait Trait { - fn f(self); + fn p(self); } impl Trait for fn(T) { - fn f(self) { + fn p(self) { print!("1"); } } impl Trait for fn(&T) { - fn f(self) { + fn p(self) { print!("2"); } } +fn f(_: u8) {} +fn g(_: &u8) {} + fn main() { - let a: fn(_) = |_: u8| {}; - let b: fn(_) = |_: &u8| {}; - let c: fn(&_) = |_: &u8| {}; - a.f(); - b.f(); - c.f(); + let a: fn(_) = f; + let b: fn(_) = g; + let c: fn(&_) = g; + a.p(); + b.p(); + c.p(); }