Reputation: 2094
I have a trait with an associated type:
pub trait Speak {
type Error;
fn speak(&self) -> Result<String, Self::Error>;
}
An implementation of that trait:
#[derive(Default)]
pub struct Dog;
impl Speak for Dog {
type Error = ();
fn speak(&self) -> Result<String, Self::Error> {
Ok("woof".to_string())
}
}
And a function returning an instance of that implementation:
pub fn speaker() -> impl Speak {
Dog::default()
}
I know that in this example I could just use Dog
as the return type, but in my actual code I have to use impl Speak
instead (the above function is in fact generated by a macro).
As I understand it, the impl Trait
notation lets the compiler figure out which concrete type is actually returned, so I would expect the following function to compile correctly because speaker()
returns a Dog
and that Dog::Error
is the type ()
:
fn test() -> Result<String, ()> {
speaker().speak()
}
Instead, I get the following error:
error[E0308]: mismatched types
--> src/lib.rs:21:5
|
20 | fn test() -> Result<String, ()> {
| ------------------ expected `std::result::Result<std::string::String, ()>` because of return type
21 | speaker().speak()
| ^^^^^^^^^^^^^^^^^ expected (), found associated type
|
= note: expected type `std::result::Result<_, ()>`
found type `std::result::Result<_, <impl Speak as Speak>::Error>`
It is as if the compiler could not (at this point) infer the return type of the speaker
function.
Who is missing something something, the compiler or myself?
Upvotes: 2
Views: 586
Reputation: 71899
Use -> impl Speak<Error = ()>
as the return type of speaker()
.
The problem is that the compiler needs, from the signature alone, enough information that the caller can actually use the function. If you just return impl Speak
, then the compiler knows that speak()
returns a Result<String, ???>
- the error type isn't known, and thus the compiler issues an error.
The compiler cannot infer anything here. It cannot infer the error type from the call site, because impl Trait
in return position doesn't allow inference from the call site. It cannot infer the error type from the implementation, because that would mean whether the caller type-checks depends on the implementation, and that's not how impl Trait
works. The caller must always type-check in presence of only the information of the signature; the concrete type is only plugged in after that.
Upvotes: 10
Reputation: 58975
You are.
You never specified the associated Error
type, so you're not allowed to assume anything about it. Even if it really is ()
, the compiler won't allow you to use that knowledge. To solve this, just specify what Error
is:
pub fn speaker() -> impl Speak<Error = ()> {
Dog::default()
}
Upvotes: 2