struct TheFoo(String);
impl Foo for TheFoo {
type Output = &String;
fn get(&self) -> impl Sized {
&self.0
}
}
But, it doesn't compile because the lifetime of &String in the type Output = &String is not named.
error: in the trait associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
--> src/lib.rs:21:19
|
21 | type Output = &String;
| ^ this lifetime must come from the implemented type
What is actually happening here?
To be honest, I thought the first code should not compile as well because we expect impl Sized that doesn't say anything about lifetime. But, I returned &self.0 which captures the lifetime of self. I thought it shouldn't compile, but it does.
As a note: I don't have any specific use case. I'm just trying to understand what's the correct mental model for associated type and the semantic of returned impl Trait.
trait Foo {
type Rpit<'this>: Sized where Self: 'this;
fn get(&self) -> Self::Rpit<'_>;
}
struct TheFoo(String);
impl Foo for TheFoo {
type Rpit<'this> = &'this String;
fn get(&self) -> Self::Rpit<'_> {
&self.0
}
}
Except the GAT is unnameable, and never normalizes (so its generic parameters are always invariant).
Though as I understand it's technically closer to something like this...
// vv generics from the method
trait FooGet<'a> {
type Rpit: Sized; // <-- bounds from the `-> impl ..`
}
// Made up syntax accouting for implied bounds in the method signature
// vvvvvvvvvvvvvvvvvvv
trait Foo: for <'a where Self: 'a> FooGet<'a> {
fn get<'this>(&'this self) -> <Self as FooGet<'this>>::Rpit;
}
struct TheFoo(String);
impl<'a> FooGet<'a> for TheFoo {
type Rpit = &'a String;
}
impl Foo for TheFoo {
fn get<'this>(&'this self) -> <Self as FooGet<'this>>::Rpit {
&self.0
}
}
I see.... I think I was mistaken because I thought returned impl captures only type parameter, not lifetime. I just realized the semantic changed just recently. But, isn't this breaking change?
It's all in the blog post, but anyway, -> impl Trait in traits, like async fn, has always captured everything, whereas outside of traits it captures everything but unmentioned lifetimes -- until edition 2024 where it will also capture everything. But you can now select which lifetimes get captured outside of traits with + use<'a, 'b> bounds (on any edition).