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:
Bar::BarError
in Foo
(there are actually an infinite number of types Bar<'_>::BarError
), so Foo
must have its own FooError
;foo
method to convert BarError
s to FooError
s.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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With