“for<>”语法与常规生命周期界限有何不同?

2024-06-24

考虑以下代码:

trait Trait<T> {}

fn foo<'a>(_b: Box<dyn Trait<&'a usize>>) {}
fn bar(_b: Box<dyn for<'a> Trait<&'a usize>>) {}

两种功能foo and bar似乎接受一个Box<Trait<&'a usize>>, 虽然foo它比bar。它们之间有什么区别?

另外,在什么情况下我需要for<>像上面那样的语法?我知道 Rust 标准库在内部使用它(通常与闭包相关),但为什么我的代码需要它?


for<>语法称为较高等级的特质界限(HRTB),它的引入确实主要是因为关闭。

简而言之,两者之间的区别foo and bar是在foo()内部的寿命usize提供参考由呼叫者的函数,而在bar()提供相同的生命周期由函数本身。这种区别对于实施非常重要foo/bar.

然而,在这种特殊情况下,当Trait没有使用类型参数的方法,这种区别是没有意义的,所以让我们想象一下Trait看起来像这样:

trait Trait<T> {
    fn do_something(&self, value: T);
}

请记住,生命周期参数与泛型类型参数非常相似。当您使用泛型函数时,您始终指定其所有类型参数,提供具体类型,并且编译器会单态化该函数。生命周期参数也是如此:当您调用具有生命周期参数的函数时,you指定生命周期,尽管是隐式的:

// imaginary explicit syntax
// also assume that there is TraitImpl::new::<T>() -> TraitImpl<T>,
// and TraitImpl<T>: Trait<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 in FnOnce(&T) -> bool有?请记住,我们在函数签名中指定生命周期不仅仅是因为存在生命周期省略;而是因为存在生命周期省略。实际上,编译器会为函数签名内的每个引用插入一个生命周期参数。那里should be some与相关的生命周期&T in FnOnce(&T) -> bool。因此,扩展上面签名的最“明显”的方法是:

fn filter<'a, F>(self, f: F) -> Option<T> where F: FnOnce(&'a T) -> bool

然而,这是行不通的。正如示例中所示Trait以上,一生'a is 严格更长比该函数中任何局部变量的生命周期长,包括value在 match 语句中。因此无法申请f to &value因为一生的不匹配。使用这样的签名编写的上述函数将无法编译。

另一方面,如果我们扩展签名filter()像这样(这实际上是 Rust 中闭包生命周期省略的工作原理):

fn filter<F>(self, f: F) -> Option<T> where F: for<'a> FnOnce(&'a T) -> bool

然后打电话f with &value作为一个论点是完全有效的:we现在可以选择生命周期,因此使用局部变量的生命周期绝对没问题。这就是 HRTB 如此重要的原因:没有它们,您将无法表达许多有用的模式。

您还可以阅读 HRTB 的另一个解释:Nomicon https://doc.rust-lang.org/nightly/nomicon/hrtb.html.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“for<>”语法与常规生命周期界限有何不同? 的相关文章

随机推荐