for<>
语法称为高阶特征绑定(HRTB),实际上引入它的主要原因是闭包。
总之,之间的差foo
和bar
是,在foo()
寿命为内部usize
参考设置由呼叫者的功能,而在bar()
相同的寿命,提供由函数本身。这种区别对于foo
/的实现非常重要bar
。
但是,在这种特殊情况下,当Trait
没有使用类型参数的方法时,这种区别是没有意义的,因此让我们想象一下,Trait
看起来像这样:
trait Trait<T> {
fn do_something(&self, value: T);
}
请记住,生存期参数与通用类型参数非常相似。使用泛型函数时,始终指定其所有类型参数,并提供具体类型,然后编译器使函数单一化。同样的事情会终身参数:当你调用哪个有一生的时间参数的函数,你指定的一生,虽然含蓄:
'a: {
foo::<'a>(Box::new(TraitImpl::new::<&'a usize>()));
}
现在foo()
,该值有什么限制,即它可以调用哪些参数do_something()
。例如,它将不会编译:
fn foo<'a>(b: Box<Trait<&'a usize>>) {
let x: usize = 10;
b.do_something(&x);
}
这不会编译,因为局部变量的生命周期严格小于生命周期参数指定的生命周期(我认为很清楚为什么如此),因此您无法调用,b.do_something(&x)
因为它要求其参数具有生命周期'a
,即严格大于x
。
但是,您可以执行以下操作bar
:
fn bar(b: Box<for<'a> Trait<&'a usize>>) {
let x: usize = 10;
b.do_something(&x);
}
之所以可行,是因为现在bar
可以选择所需的生存期,而不是的调用者bar
。
当您使用接受引用的闭包时,这确实很重要。例如,假设您要在上编写一个filter()
方法Option<T>
:
impl<T> Option<T> {
fn filter<F>(self, f: F) -> Option<T> where F: FnOnce(&T) -> bool {
match self {
Some(value) => if f(&value) { Some(value) } else { None }
None => None
}
}
}
这里的闭包必须接受对 T
因为否则将无法返回该选项中包含的值(这与filter()
迭代器上的推理相同)。
但是,生存时间应该&T
在FnOnce(&T) -> bool
有?请记住,我们不仅仅在函数签名中指定生存期,还因为存在生存期省略。实际上,编译器会在函数签名内为每个引用插入生命周期参数。这里应该是有些关联的寿命&T
在FnOnce(&T) -> bool
。因此,扩展上述签名的最“明显”的方式是:
fn filter<'a, F>(self, f: F) -> Option<T> where F: FnOnce(&'a T) -> bool
但是,这行不通。如结合的例子Trait
以上,寿命'a
是严格长比任何本地变量的生存期在这个函数中,包括value
匹配语句内。因此,不可能适用f
于&value
,因为一辈子不匹配。用这种签名编写的上述函数将无法编译。
另一方面,如果我们filter()
像这样扩展签名(实际上,这就是现在在Rust中对闭包进行生存期删除的方式):
fn filter<F>(self, f: F) -> Option<T> where F: for<'a> FnOnce(&'a T) -> bool
然后调用f
与&value
作为参数是完全有效的:我们现在可以选择生存期,因此使用局部变量的生存期绝对可以。这就是HRTB非常重要的原因:没有它们,您将无法表达很多有用的模式。
您还可以在Nomicon中阅读有关HRTB的另一种解释。