Rust中如何定义可创建与自身引用同生命周期对象的Trait
核心需求
想要定义一个Trait,确保类型能通过自身引用(&'a self)创建另一个类型Item,且Item的生命周期与引用的'a一致,同时避免用无意义的生命周期污染Error这类关联类型。
偏好的Trait实现
我偏好的写法如下,它把生命周期放在关键位置,不会冗余标记其他类型:
trait T { type Item<'a> where Self: 'a; type Error; fn f<'a>(&'a mut self) -> Result<Self::Item<'a>, Self::Error>; }
用示例类型S可以轻松实现这个Trait:
use std::convert::Infallible; struct S(i32); impl T for S { type Item<'a> = &'a i32; type Error = Infallible; fn f<'a>(&'a mut self) -> Result<Self::Item<'a>, Self::Error> { Ok(&self.0) } }
泛型函数中的问题
下面这个不指定Item类型的泛型函数可以正常运行:
fn g<X: T>(x: X) -> Option<()> { let item = x.f().ok()?; Some(()) }
但一旦指定Item类型,代码就无法编译:
fn f<'a, X: T<Item<'a> = &'a i32> + 'a>(x: X) -> Option<()> { let item = x.f().ok()?; let y = item + 1; Some(()) }
报错信息:
argument requires that
xis borrowed for'axdropped here [the end of the fn] while still borrowed [no, it's not?]
其他尝试的Trait方案
方案U:未约束生命周期的Trait
这种写法在泛型中能工作,但无法实现,因为描述Item需要的生命周期'a未被Trait或impl约束:
trait U { type Item; type Error; fn f(&'a mut self) -> Result<Self::Item, Self::Error>; }
方案V:将生命周期/Item移至Trait参数
无论是否在f签名中包含'a,都会遇到和之前方案一样的问题:
// 和方案T问题相同 trait V1<'a, Item> { type Error; fn f(&'a self) -> Result<Item, Self::Error>; } // 和方案U问题相同 trait V2<'a, Item> { type Error; fn f(&self) -> Result<Item, Self::Error>; }
方案W:基于TryFrom的变体
尝试用TryFrom来表达Trait,虽然可以实现,但调用时会出现奇怪错误:
trait W<'a> where Self: 'a { type Item: TryFrom<&'a Self>; fn f(&'a self) -> Result<Self::Item, <Self::Item as TryFrom<&'a Self>>::Error> {<Self::Item as TryFrom<&'a Self>>::try_from(self)} }
对应的泛型函数:
fn f<'a, X: W<'a, Item = &'a i32> + 'a>(x: X) -> Option<()> { let item = x.f().ok()?; let y = item + 1; Some(()) }
报错信息:
the trait bound
&'a i32: From<&'a X>is not satisfied
[which bound is both unnecessary and guaranteed to be implemented.]
the traitFrom<&'a X>is not implemented for&'a i32, which is required by&'a i32: TryFrom<&'a X>[no it's not and can't be?]
即使显式添加该约束,错误依然存在。
总结
我完全搞不懂为什么结构最合理的方案T和W会报错,其他方案只是临时替代。这个Trait本质是我之前定义的迭代器Trait"I"的开放式版本,I<Item = T>等价于T<'a, Item = I::Iter<'a>>,而那个迭代器Trait没有问题,因为它的泛型形式不引用生命周期'a。
内容的提问来源于stack exchange,提问作者Ben

