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的另一种解释。