Reputation: 8059
I have a trait with an associated type, but I want to implement it with a reference type, however I can't get the lifetimes right.
For example, without references, suppose I have:
trait DoesStuff {
type Output;
fn do_thing(&mut self, x: f64) -> Self::Output;
}
struct SummingWidget {
sum_so_far: f64
}
impl SummingWidget {
pub fn new() -> Self {
Self{
sum_so_far: 0.0
}
}
}
impl DoesStuff for SummingWidget {
type Output = f64;
fn do_thing(&mut self, x: f64) -> Self::Output {
self.sum_so_far += x;
self.sum_so_far
}
}
Then all works fine. But suppose instead of returning the value, I just want to return a reference to the value held by the object. The obvious attempt of just returning the reference, results in request for a lifetime on the associated type.
type Output = &f64;
Adding a lifetime to the associated type then requires it added to the impl, and it is then unconstrained, so I try adding it to the struct, which then needs a PhantomData and I end up with:
struct SummingWidgetRef<'a> {
sum_so_far: f64,
phantom: PhantomData<&'a ()>
}
impl<'a> SummingWidgetRef<'a> {
pub fn new() -> Self {
Self{
sum_so_far: 0.0,
phantom: PhantomData,
}
}
}
impl<'a> DoesStuff for SummingWidgetRef<'a> {
type Output = &'a f64;
fn do_thing(&mut self, x: f64) -> Self::Output {
self.sum_so_far += x;
&self.sum_so_far
}
}
But even that is no good, because the compiler complains that:
method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
At which point I'm stuck. How can I tell the compiler that the return type should match the lifetime of the struct?
Upvotes: 1
Views: 500
Reputation: 12812
The original way to do this is to make the trait take self
by value, and then implement it for references.
trait DoesStuff {
type Output;
fn do_thing(self, x: f64) -> Self::Output;
}
pub struct SummingWidget {
sum_so_far: f64
}
impl<'a> DoesStuff for &'a mut SummingWidget {
type Output = &'a f64;
fn do_thing(self, x: f64) -> Self::Output {
self.sum_so_far += x;
&self.sum_so_far
}
}
This works in most cases, and it's how several standard library implementations work, like impl<'a, T> IntoIterator for &'a [T]
.
The new option is to use generic associated types (GAT) (from eggyal's comment).
trait DoesStuff {
type Output<'a> where Self: 'a;
fn do_thing(&mut self, x: f64) -> Self::Output<'_>;
}
pub struct SummingWidget {
sum_so_far: f64
}
impl DoesStuff for SummingWidget {
type Output<'a> = &'a f64;
fn do_thing(&mut self, x: f64) -> Self::Output<'_> {
self.sum_so_far += x;
&self.sum_so_far
}
}
I believe the original method is slightly more flexible than the GAT method, since you can choose for do_thing
to take any type, whereas the GAT method must take &mut
. But if you have something more complex than the simple function from your example, you may need to use the GAT method.
Upvotes: 2