我有一个具有关联类型的特征,但我想用引用类型来实现它,但是我无法获得正确的生命周期。
例如,没有参考文献,假设我有:
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
}
}
然后一切正常。但假设我不想返回该值,而是只想返回对该对象所持有的值的引用。仅返回引用的明显尝试会导致对关联类型的生命周期的请求。
type Output = &f64;
向关联类型添加生命周期需要将其添加到 impl,然后它就不受约束,因此我尝试将其添加到结构中,然后需要一个 PhantomData,最终得到:
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
}
}
但即使这样也不好,因为编译器会抱怨:
method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
此时我陷入了困境。我如何告诉编译器返回类型应该与结构的生命周期匹配?
最初的做法是让trait按值取
self
,然后实现以供参考。
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
}
}
这在大多数情况下都有效,这就是几个标准库实现的工作原理,例如
impl<'a, T> IntoIterator for &'a [T]
。
新选项是使用通用关联类型(GAT)(来自eggyal的评论)。
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
}
}
我相信原始方法比 GAT 方法稍微灵活一些,因为您可以选择
do_thing
采用任何类型,而 GAT 方法必须采用 &mut
。但如果您有比示例中的简单函数更复杂的东西,您可能需要使用 GAT 方法。