Wea*_*ter 0 traits lifetime rust lifetime-scoping
对于一个类型
pub struct Child<'a> {
buf: &'a mut [u8],
}
Run Code Online (Sandbox Code Playgroud)
我可以定义一个特征并实现该类型的特征,但其生命周期绑定到调用函数的上下文(而不是本地循环上下文):
pub trait MakeMut<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut<'a> for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self {
Self { buf }
}
}
Run Code Online (Sandbox Code Playgroud)
首先展示一个有点有效的示例,因为x仅在循环上下文中借用,因为 Child::make_mut 是在函数中硬编码的map1:
pub fn map1<F>(mut func: F)
where
F: FnMut(&mut Child),
{
let mut vec = vec![0; 16];
let x = &mut vec;
for i in 0..2 {
let offset = i * 8;
let s = &mut x[offset..];
let mut w = Child::make_mut(s);
func(&mut w);
}
}
Run Code Online (Sandbox Code Playgroud)
但是在尝试制作map2(map1的通用版本)时,T绑定到MakeMut特征,但具有整个函数体的生命周期,这将无法编译,有充分的理由(T创建的T生命周期: MakeMut<'a> 具有 map2 的生命周期,而不是内部循环):
pub fn map2<'a, F, T>(mut func: F) // lifetime `'a` defined here
where
T: MakeMut<'a>,
F: FnMut(&mut T),
{
let mut vec = vec![0; 16];
let x = &mut vec;
for i in 0..2 {
let offset = i * 8;
let s = &mut x[offset..];
let mut w = T::make_mut(s); // error: argument requires that `*x` is borrowed for `'a`
func(&mut w);
}
}
Run Code Online (Sandbox Code Playgroud)
我想做类似这样的事情,但当然它也不能编译:
pub trait MakeMut {
fn make_mut<'a>(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self { // lifetime mismatch
Self{ buf }
}
}
Run Code Online (Sandbox Code Playgroud)
与编译器错误:
error[E0308]: method not compatible with trait
--> src/main.rs:45:5
|
45 | fn make_mut(buf: &'a mut [u8]) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected fn pointer `fn(&'a mut [u8]) -> Child<'_>`
found fn pointer `fn(&'a mut [u8]) -> Child<'_>`
note: the lifetime `'a` as defined here...
--> src/main.rs:45:5
|
45 | fn make_mut(buf: &'a mut [u8]) -> Self {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
--> src/main.rs:44:6
|
44 | impl<'a> MakeMut for Child<'a> {
| ^^
Run Code Online (Sandbox Code Playgroud)
是否有一种语法允许 a 的特征,Child<'a>其中 a'a由方法的输入参数定义make_mut?因此,可以为返回实例的特征定义一个通用函数,但实例生命周期不是整个函数,而只是由内部块定义的较短生命周期?
我知道生命周期是返回类型的一部分,但似乎更高级别的特征界限(HRTB)可以解决这个问题,除非我还没有找到一种方法来指定适合特征和方法签名的生命周期。
您的第一次尝试接近您想要的。以供参考:
pub trait MakeMut<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self;
}
impl<'a> MakeMut<'a> for Child<'a> {
fn make_mut(buf: &'a mut [u8]) -> Self {
Self { buf }
}
}
Run Code Online (Sandbox Code Playgroud)
T第一个问题是中的界限map2:
pub fn map2<'a, F, T>(mut func: F)
where
T: MakeMut<'a>,
F: FnMut(&mut T),
Run Code Online (Sandbox Code Playgroud)
这需要编译器推导出适用于整个函数的单个值。'a 由于生存期参数来自函数外部,因此生存期'a必然比函数调用长,这意味着任何具有生存期的东西都必须比'a函数生存期长。从T::make_mut()调用开始向后推导,编译器最终推断出这x意味着&'a mut Vec<_>必须vec比函数调用更长寿,但由于它是本地函数,因此不可能做到这一点。
这可以通过使用更高等级的特征界限来修复,指示必须在任何可能的生命周期内T实现,其表达如下:MakeMut<'a> 'a
pub fn map2<F, T>(mut func: F)
where
T: for<'a> MakeMut<'a>,
F: FnMut(&mut T),
Run Code Online (Sandbox Code Playgroud)
通过此更改,代码可以编译。
然后您会发现您实际上无法调用,map2因为T=Child<'_>您会在不同的地方遇到相同的问题。调用者必须为in指定一个特定的生命周期,但这与 HRTB 不一致——你已经有了,但 HRTB 想要,这又带来了该实现中的生命周期问题。'aChild<'a>impl<'a> MakeMut<'a> for Child<'a>impl<'a, 'b> MakeMut<'b> for Child<'a>make_mut
MakeMut解决这个问题的一种方法是解耦from的实现Child,提供使用关联类型的“工厂类型”。这样,调用者就不必提供任何可能在以后引起麻烦的讨厌的生命周期参数。
pub trait MakeMut<'a> {
type Item;
fn make_mut(buf: &'a mut [u8]) -> Self::Item;
}
struct ChildFactory;
impl<'a> MakeMut<'a> for ChildFactory {
type Item = Child<'a>;
fn make_mut(buf: &'a mut [u8]) -> Child<'a> {
Child { buf }
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们修改map2以了解关联类型:
pub fn map2<F, T>(mut func: F)
where
T: for<'a> MakeMut<'a>,
F: for<'a, 'b> FnMut(&'b mut <T as MakeMut<'a>>::Item),
Run Code Online (Sandbox Code Playgroud)
哇哦
现在,我们终于可以使用map2:
map2::<_, ChildFactory>(|v| {});
Run Code Online (Sandbox Code Playgroud)
(游乐场)