The issue in this code is that you’re returning a reference &Hello but you’ve only implemented ISay for Hello. If you implement ISay for references, the code compiles:
8 | async fn people_say<'a>(&'a self, name: &str) -> &'a impl ISay;
| ^^^^^^^^^^^^^^^^^^^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | the associated type `impl ISay` must be valid for the lifetime `'a` as defined here...
| ...so that the reference type `&'a impl ISay` does not outlive the data it points at
in my understanding, constraint the impl ISay the same lifetime with self. this should be okay.
#![feature(trait_alias)]
use std::collections::HashMap;
use anyhow::Result;
use std::hash::Hash;
pub trait GID =
Eq + Clone + Default + Hash + 'static;
trait ISay {
async fn say(&self);
}
trait IPeopleQueue<ID> where ID:GID {
type Output: ISay;
async fn people_say(&self, name: &ID) -> Result<&Self::Output>;
}
struct Hello {}
impl ISay for Hello {
async fn say(&self){}
}
struct PeopleQueue<ID> {
peoples: HashMap<ID, Hello>
}
impl<ID> IPeopleQueue<ID> for PeopleQueue<ID> where ID:GID {
type Output = Hello;
async fn people_say(&self, name: &ID) -> Result<&Self::Output>{
Ok(self.peoples.get(name).unwrap())
}
}
#[tokio::main]
async fn main() {
let mut pq = PeopleQueue{
peoples: HashMap::new()
};
pq.peoples.insert("123".to_string(), Hello{});
let data = pq.people_say(&"123".to_string()).await.unwrap();
data.say().await;
}
so whats the difference between
trait IPeopleQueue<ID> where ID:GID {
type Output: ISay;
async fn people_say(&self, name: &ID) -> Result<&Self::Output>; //work
}
and
trait IPeopleQueue2<ID> where ID:GID {
async fn people_say(&self, name: &ID) -> Result<&impl ISay>; //cannot work
}
it seem that they do the same things. i have a idea that, impl ISay just constrain return type impl ISay but the type maybe different, but for associate type constrain return type impl ISay and also constrain the return is the same type, right?
You are not adding the lifetime everywhere it matters. Since your (async) function body uses the &str, now the resulting future needs to store that too. So the &str has to have (at least) the same lifetime.
Ps.: don't prefix traits with "I". Rust is not Java or C#. We have a real and sound type system and great auto-generated documentation. Hungarian notation is totally useless and unwarranted.
Impl trait in a trait method's return type is translated internally to an anonymous associated type. There's no fundamental difference between the two, they are the same high-level idea/mechanism.
There may however be minor differences in the exact desugaring of the two syntaxes that affect the behavior observed here. I'm not 100% sure, but it may be that the impl Trait syntax, following the usual desugaring rules, only captures the lifetime of &self by default, whereas the lifetime(s) of the explicit associated type are determined in a different way.
but in general, opaque return types capture all generic inputs to the function whereas associated types only capture generic inputs to the implementation and (in the case of GATs) to the associated type itself. Normalization can also let more things compile (opaque types don't normalize to their actual type).
In this case the associated type can only capture generics that are part of the implementing type (because the trait and associated type have no generics of their own). The pertinent part as far as fixing the error goes, though, is that the associated type can't capture the lifetime from name: &ID.
That is to say, in this particular case, I think the problem was just that the opaque impl ISaycould contain the lifetime from name: &ID, which would not be valid when it's shorter than the lifetime on &self. Therefore, another fix is to just assert that the opaque is valid for the lifetime on &self.
Technically at this point I believe impl ISay is still "capturing" the lifetime from &ID, but in a weaker sense that doesn't inhibit the outlives bound (which I don't fully understand myself and may even be subject to change).[1]
I can go find the hackmd that describes it if anyone is really interested. ↩︎