rust - "for<>"语法与常规生命周期界限有何不同?

标签 rust syntax

考虑以下代码:

trait Trait<T> {}

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

两个函数foobar似乎接受 Box<Trait<&'a usize>> , 尽管 foobar 更简洁吗.它们有什么区别?

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

最佳答案

for<>语法称为higher-ranked trait bound (HRTB),它的引入确实主要是因为闭包。

简而言之,foo之间的区别和 bar是在foo()内部的生命周期 usize引用由函数的调用者提供,而在 bar()由函数本身提供相同的生命周期。而这个区分对于foo的实现非常重要/bar .

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

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

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

// 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() 的推理相同)。

但是生命周期应该是多少&TFnOnce(&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 的另一种解释。 .

关于rust - "for<>"语法与常规生命周期界限有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35592750/

相关文章:

rust - 如何处理包装在Rc <RefCell <>>中的新型枚举的模式匹配?

function - Rust 函数如何修改数组索引的值?

rust - 不能借用 `*self` 作为可变的,因为 `self.history[..]` 也被借用为不可变的

rust - 如何在Rust中编写一个通用函数,该函数可以接受实现特定属性的任何结构?

google-sheets - Col BY 在 Google 表格查询中导致解析错误

python - 2种运行python程序的方式

rust - 将参数传递给函数n次

php - 在 Perl 中循环数组构建多维数组,从 PHP 转换,语法错误

java - 关于在 Java 中创建通用列表数组的错误

php - 如何使用 codeigniter 正确实现 PHPass 密码哈希?