Why does the compiler treat those two equivalent(?) lines differently? - rust

From what I understand, when x implements trait Foo,
the following two lines should be equivalent.
x.foo();
Foo::foo(&x);
However, I am facing a problem where the compiler accepts the first one, and rejects the second one, with a rather strange error message.
As usual, this example is available on the playground.
Consider the following two related traits.
pub trait Bar<'a> {
type BarError: Into<MyError>;
fn bar(&self) -> Result<(), Self::BarError>;
}
pub trait Foo: for<'a> Bar<'a> {
type FooError: Into<MyError>;
fn foo(&self) -> Result<(), Self::FooError>
where
for<'a> <Self as Bar<'a>>::BarError: Into<<Self as Foo>::FooError>;
}
This example is a bit complex, but I do need the lifetime parameter on Bar, and I can't have it on Foo. As a consequence:
I have to resort on Higher-Rank Trait Bounds (HRTB);
I can not rely on Bar::BarError in Foo (there are actually an infinite number of types Bar<'_>::BarError), so Foo must have its own FooError;
and so I need the complex trait bound in the foo method to convert BarErrors to FooErrors.
Now, let's implement Bar and Foo for a concrete type, e.g. Vec<i32>.
impl<'a> Bar<'a> for Vec<i32> {
type BarError = Never;
fn bar(&self) /* ... */
}
impl Foo for Vec<i32> {
type FooError = Never;
fn foo(&self) /* ... */
}
Note that Never is an empty enum, indicating that these implementations never fail. In order to comply with the trait definitions, From<Never> is implemented for MyError.
We can now demonstrate the problem: the following compiles like charm.
let x = vec![1, 2, 3];
let _ = x.foo();
But the following des not.
let x = vec![1, 2, 3];
let _ = Foo::foo(&x);
The error messages says:
error[E0271]: type mismatch resolving `<std::vec::Vec<i32> as Foo>::FooError == MyError`
--> src/main.rs:49:13
|
49 | let _ = Foo::foo(&x);
| ^^^^^^^^ expected enum `Never`, found struct `MyError`
|
= note: expected type `Never`
found type `MyError`
The compiler seems to believe that I wrote something like this (NB: this is not correct Rust, but just to give the idea).
let _ = Foo::<FooError=MyError>::foo(&x);
And this does not work because x implements Foo<FooError=Never>.
Why does the compiler adds this additional constraint? Is it a bug? If not, is it possible to write it otherwise so it compiles?
NB: you may wonder why I don't just stick to the first version (x.foo(&x)). In my actual situation, foo is actually named retain, which is also the name of a method in Vec. So I must use the second form, to avoid the ambiguity.
NB2: if I remove the HRTB in the declaration of method foo, both lines compile. But then I can not call Bar::bar in any implementation of Foo::foo, which is not an option for me. And changing foo to something like fn foo<'a>(&'a self) -> Result<(), <Self as Bar<'a>>::BarError) is not an option either, unfortunately.

From what I understand, when x implements trait Foo, the following two lines should be equivalent.
x.foo();
Foo::foo(&x);
This is true for an inherent method (one that is defined on the type of x itself), but not for a trait method. In your case the equivalent is <Vec<i32> as Foo>::foo(&x);.
Here is a playground link

Related

Tuple struct to function coercion : what are the lifetime parameters of said function?

I don't understand something about the feature that makes it possible to coerce a tuple struct to a function as in:
struct MyType(u8);
let optional_mytype: Option<MyType> = Some(12).map(MyType);
// ^^^^^^ here, MyType is treated as a function
In the example above, no lifetimes are involved: everything is easy. However, when the structure has a generic lifetime bound, I'm having troubles defining the exact signature of the function it would coerce to.
Here's a MRE of something I've attempted to do, and that doesn't work (sandbox link):
struct MyStruct<'a>(&'a ());
// This looks, to me, like the signature the "constructor" should have
type MyStructConstructor = for<'a> fn(&'a ()) -> MyStruct<'a>;
/// Coercion problem here
fn some_code() {
let mut constructor: MyStructConstructor = MyStruct;
}
and the error given by the type-checker:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:7:48
|
7 | let mut constructor: MyStructConstructor = MyStruct;
| ^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'a> fn(&'a ()) -> MyStruct<'a>`
found fn pointer `fn(&()) -> MyStruct<'_>`
And I honestly don't understand why my constructor type alias isn't valid, and why the type suggested by the error message is much more lax than it should be. I mean, the lifetime <'a> of my structure should be exactly equal to that of the unit reference that is contained by the structure, not a random one.
Here's a more concrete (still minimal) example of what I'm actually trying to do, if having context helps you.
Thanks in advance
In fact, the error message might be a bit misleading. '_ does not mean any lifetime goes, it just means a lifetime I haven't named explicitly, just as writing &() (which is the same as &'_ ()). Rust does that because that's not the issue.
To understand, let's actually annotate implicit generic lifetimes in your code:
fn some_code() {
let constructor: MyStructConstructor = MyStruct<'_>;
}
Notice how something is wrong? On the right hand side, you have something whose type is generic over a lifetime. On the left hand side, you don't.
In fact, a more appropriate constructor signature for MyStruct would be
struct MyStruct<'a>(&'a ());
type MyStructConstructor<'a> = fn(&'a ()) -> MyStruct<'a>;
fn some_code() {
let constructor: MyStructConstructor = MyStruct;
}
leaving the lifetime implicit — see the playground.
This compiles.
So now you may understand why MyStructConstructor was more generic than MyStruct: because for MyStruct, you have to specify a lifetime to begin with, that is, the actual type is, for a given 'a, MyStruct<'a>. Then, with that constructor, you are only capable of building objects of type MyStruct<'a>, given &'a (). On the other hand, with MyStructConstructor, given a reference to a () with any lifetime, you would be able to build a MyStruct of the same lifetime, which is more general.

Code working with elided lifetimes, not with explicit

The following code works fine:
fn get<F: Fn(&[u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
However, when I add explicit lifetime information to it, it doesn't:
fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
What lifetime does the compiler infer in the working code?
I'm using Rust 1.18.0.
The error message is:
error: borrowed value does not live long enough
--> test.rs:4:8
|
4 | f(&[1, 2, 3])
| ^^^^^^^^^ does not live long enough
5 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'inp as defined on the body at 3:49...
--> test.rs:3:50
|
3 | fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
| __________________________________________________^
4 | | f(&[1, 2, 3])
5 | | }
| |_^
Lifetimes in trait bounds are a bit special and the Fn family of traits has a special lifetime elision rule. We'll dive into that, but first, here it the correct explicitly annotated version:
fn get<F: for<'inp> Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
Oh gosh, what is this for<'inp> doing there? It's a so called higher ranked trait bound (HRTB) and it's used here to make 'inp universally quantiefied in regards to f. In order to fully understand that, we need to understand a bit of theory.
Who has the choice?
Let's take a look at an easy example:
fn bar<'a>(x: &'a u8) {}
Here, bar() is generic of the lifetime 'a. The syntax above reads: "choose any 'a and there is a bar() that will work with 'a". This means that we can choose any 'a we want, and bar() works! Who are "we"? "We" are the caller -- the one calling bar. This will be important later: the caller chooses the generic parameters. We can call bar() with a &'static u8 as well as with a reference that doesn't live as long.
Now you might ask: are there situations where the caller doesn't choose the generic parameter, but someone else does? Yes, there are! Sadly, it's a bit more difficult to understand, because it doesn't occur too often in today's Rust code. But let's try:
trait Bar<'a> {
fn bar(&self, x: &'a u8);
}
This is similar to the bar() function above, but now the lifetime parameter is defined on the trait, not the function. Let's use the trait:
fn use_bar<'a, B: Bar<'a>>(b: B) {
let local = 0u8;
b.bar(&local);
}
This doesn't compile, printing the same error as above. Why? The method b.bar() expects a reference of lifetime 'a. But who chooses 'a here? Exactly: the caller -- the caller of use_bar(), not the caller of bar()! So the caller of use_bar() could choose the 'static lifetime; in that case, it's easy to see that our &local doesn't fulfill the lifetime requirements.
Note that the caller of use_bar() chooses 'a as well as B. Once use_bar() is instantiated, B is a fixed type and B::bar() works only for one specific lifetime. This means the caller of bar() can't choose the lifetime, but bar() itself chose it!
What do we want instead? We want use_bar() to choose the lifetime of the bar() call. And we can do that with the for<> syntax:
fn use_bar<B: for<'a> Bar<'a>>(b: B) {
let local = 0u8;
b.bar(&local);
}
This works. What we say here is: "for any lifetime parameter 'a, B has to implement the trait Bar<'a>". Instead of: "there needs to exist a lifetime parameter 'a for which B implements Bar<'a>". It's all about who chooses which parameter.
Let's use the real names for it:
a generic parameter is universally quantified if the caller can choose it
a generic parameter is existentially quantified if the callee can choose it
What does Rust do?
To return to your example:
fn get<'inp, F: Fn(&'inp [u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
Here we have the same problem as above: the lifetime parameter of f is existentially quantified. The caller of f cannot choose the lifetime parameter. We can fix that with the for<> syntax as shown above.
When you omit the lifetimes:
fn get<F: Fn(&[u8]) -> u8>(f: F) -> u8 {
f(&[1, 2, 3])
}
The Rust compiler will do something special for the Fn family of traits. Your F: Fn(&[u8]) desugars to F: for<'a> Fn<(&'a [u8],)>. If you use Fn* traits with parameters that involve lifetimes, those lifetimes are automatically universally quantified, because that's usually what you want with higher order functions.
The infered code seems to be:
fn get<F : for<'inp> Fn(&'inp[u8]) -> u8>(f: F) -> u8 {
f(&[1,2,3])
}
Explanation

How can I implement std::convert::From such that it does not consume its input?

I have managed to make the Rust type checker go into an infinite loop. A very similar program compiles with no trouble. Why does the program I want not compile?
To save your time and effort, I have made minimal versions of the two programs that isolate the problem. Of course, the minimal version is a pointless program. You'll have to use your imagination to see my motivation.
Success
Let me start with the version that works. The struct F<T> wraps a T. The type Target can be converted from an F<T> provided T can.
struct F<T>(T);
impl<T> From<F<T>> for Target where Target: From<T> {
fn from(a: F<T>) -> Target {
let b = Target::from(a.0);
f(&b)
}
}
Here's an example caller:
fn main() {
let x = Target;
let y = F(F(F(x)));
let z = Target::from(y);
println!("{:?}", z);
}
This runs and prints "Target".
Failure
The function f does not consume its argument. I would prefer it if the From conversion also did not consume its argument, because the type F<T> could be expensive or impossible to clone. I can write a custom trait FromRef that differs from std::convert::From by accepting an immutable borrow instead of an owned value:
trait FromRef<T> {
fn from_ref(a: &T) -> Self;
}
Of course, I ultimately want to use From<&'a T>, but by defining my own trait I can ask my question more clearly, without messing around with lifetime parameters. (The behaviour of the type-checker is the same using From<&'a T>).
Here's my implementation:
impl<T> FromRef<F<T>> for Target where Target: FromRef<T> {
fn from_ref(a: &F<T>) -> Target {
let b = Target::from_ref(&a.0);
f(&b)
}
}
This compiles. However, the main() function doesn't:
fn main() {
let x = Target;
let y = F(F(F(x)));
let z = Target::from_ref(y);
println!("{:?}", z);
}
It gives a huge error message beginning:
error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
--> <anon>:26:13
|
26 | let z = Target::from_ref(y);
| ^^^^^^^^^^^^^^^^
|
= note: consider adding a `#![recursion_limit="128"]` attribute to your crate
= note: required because of the requirements on the impl of `FromRef<F<_>>` for `Target`
= note: required because of the requirements on the impl of `FromRef<F<F<_>>>` for `Target`
= note: required because of the requirements on the impl of `FromRef<F<F<F<_>>>>` for `Target`
etc...
What am I doing wrong?
Update
I've randomly fixed it!
The problem was that I forgot to implement FromRef<Target> for Target.
So I would now like to know: what was the compiler thinking? I still can't relate the problem to the error message.
You can't avoid consuming the input in the standard From/Into traits.
They are defined to always consume the input. Their definition specifies both input and output as owned types, with unrelated lifetimes, so you can't even "cheat" by trying to consume a reference.
If you're returning a reference, you can implement AsRef<T> instead. Or if your type is a thin wrapper/smart pointer, Deref<T>. You can provide methods as_foo()
If you're returning a new (owned) object, the convention is to provide to_foo() methods.

How do you create a Box<dyn Trait>, or a boxed unsized value in general?

I have the following code
extern crate rand;
use rand::Rng;
pub struct Randomizer {
rand: Box<Rng>,
}
impl Randomizer {
fn new() -> Self {
let mut r = Box::new(rand::thread_rng()); // works
let mut cr = Randomizer { rand: r };
cr
}
fn with_rng(rng: &Rng) -> Self {
let mut r = Box::new(*rng); // doesn't work
let mut cr = Randomizer { rand: r };
cr
}
}
fn main() {}
It complains that
error[E0277]: the trait bound `rand::Rng: std::marker::Sized` is not satisfied
--> src/main.rs:16:21
|
16 | let mut r = Box::new(*rng);
| ^^^^^^^^ `rand::Rng` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `rand::Rng`
= note: required by `<std::boxed::Box<T>>::new`
I don't understand why it requires Sized on Rng when Box<T> doesn't impose this on T.
More about the Sized trait and bound - it's a rather special trait, which is implicitly added to every function, which is why you don't see it listed in the prototype for Box::new:
fn new(x: T) -> Box<T>
Notice that it takes x by value (or move), so you need to know how big it is to even call the function.
In contrast, the Box type itself does not require Sized; it uses the (again special) trait bound ?Sized, which means "opt out of the default Sized bound":
pub struct Box<T> where T: ?Sized(_);
If you look through, there is one way to create a Box with an unsized type:
impl<T> Box<T> where T: ?Sized
....
unsafe fn from_raw(raw: *mut T) -> Box<T>
so from unsafe code, you can create one from a raw pointer. From then on, all the normal things work.
The problem is actually quite simple: you have a trait object, and the only two things you know about this trait object are:
its list of available methods
the pointer to its data
When you request to move this object to a different memory location (here on the heap), you are missing one crucial piece of information: its size.
How are you going to know how much memory should be reserved? How many bits to move?
When an object is Sized, this information is known at compile-time, so the compiler "injects" it for you. In the case of a trait-object, however, this information is unknown (unfortunately), and therefore this is not possible.
It would be quite useful to make this information available and to have a polymorphic move/clone available, but this does not exist yet and I do not remember any proposal for it so far and I have no idea what the cost would be (in terms of maintenance, runtime penalty, ...).
I also want to post the answer, that one way to deal with this situation is
fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
let r = Box::new(*rng);
Randomizer { rand: r }
}
Rust's monomorphism will create the necessary implementation of with_rng replacing TRand by a concrete sized type. In addition, you may add a trait bound requiring TRand to be Sized.

Type mismatch in trait bound - Why can't rustc infer the type on its own?

I just ran into an issue in some code of mine and managed to trim it down to the following minimal example :
use std::iter::IntoIterator;
use std::marker::PhantomData;
trait Bar<'a> {
fn compile_plz(&self) -> &'a str;
}
struct Foo<'a> {
ph: PhantomData<&'a str>
}
impl<'a> Bar<'a> for Foo<'a> {
fn compile_plz(&self) -> &'a str {
"thx"
}
}
fn do_something_with_bars<'a, I>(it: I) -> Result<(), ()> where I: IntoIterator<Item=&'a Bar<'a>> {
Ok(())
}
fn take_bar<'a, B>(b: &'a B) where B: Bar<'a> {
do_something_with_bars(vec![b]);
}
fn main() {
let f = Foo{ph: PhantomData};
take_bar(&f);
}
This fails with the following error :
23:5: 23:27 error: type mismatch resolving <collections::vec::Vec<&B> as core::iter::IntoIterator>::Item == &Bar<'_>:
expected type parameter,
found trait Bar [E0271]
However, changing vec![b] to vec![b as &Bar] works fine. But since b is of type &B, and B has a Bar<'a> bound, why can't the compiler figure out that b is indeed a &Bar?
It could theoretically attempt to 'fix' the type to &Bar. However, you're hitting the variance problem here - a vector of &B is not a vector of &Bar just because a &B is a &Bar, and neither is the inverse true.
Example: Say we treat a vector of &B as a vector of &Bar. Now we have a value of type &C where &C is a &Bar but not a &B - can we put a &C into our vector? Well, obviously not, because the implementation is a vector of &B. So we can only allow reads, not writes. On the other hand, we can attempt to treat a vector of &Bar as a vector of &B - this works fine as long as we only write &Bs into our vector (because it's allowed to accept &Bs of course) - but since it's still a vector of &Bar, it can contain things that aren't &Bs, so we aren't allowed to read from it.
Hence, a container that allows both read and write at the same time needs to be invariant in its generic argument. You'll always have this problems in languages that have both polymorphism and generics in this fashion.
So, back to the actual question: Since this problem exists, there can't be an automatic escape hatch that would turn your expression that is initially going to be inferred to be vector of &B to the type vector of &Bar. Be explicit about your types, and this won't happen.
Full type inference maybe could help here, but I'm not sure if full type inference is even possible in a language like rust.

Resources