返回捕获引用的更高级的闭包

2023-12-08

我正在尝试在返回闭包的结构上编写一个方法。此关闭应该需要&[u8]具有任意的生命周期'inner作为参数并返回相同的类型,&'inner [u8]。为了执行其功能,闭包还需要对结构成员的(共享)引用&self。这是我的代码:

#![warn(clippy::pedantic)]

// placeholder for a large type that I can't afford to clone
struct Opaque(usize);

struct MyStruct<'a>(Vec<&'a Opaque>);

impl<'a> MyStruct<'a> {
    pub fn get_func_for_index(
        &'a self,
        n: usize,
    ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
        // the reference to the struct member, captured by the closure
        let opaque: &'a Opaque = *self.0.get(n)?;

        Some(move |i: &[u8]| {
            // placeholder: do something with the input using the &Opaque
            &i[opaque.0..]
        })
    }
}

fn main() {
    let o1 = Opaque(1);
    let o5 = Opaque(5);
    let o7 = Opaque(7);

    let x = MyStruct(vec![&o1, &o5, &o7]);

    let drop_five = x.get_func_for_index(1 /*Opaque(5)*/).unwrap();

    let data: Vec<u8> = Vec::from(&b"testing"[..]);

    assert_eq!(drop_five(&data[..]), b"ng");
}

如果我尝试编译它rustc 1.54.0 (a178d0322 2021-07-26),我收到以下错误:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> /tmp/lifetimes.rs:16:14
   |
16 |             &i[opaque.0..]
   |              ^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 14:14...
  --> /tmp/lifetimes.rs:14:14
   |
14 |           Some(move |i: &[u8]| {
   |  ______________^
15 | |             // placeholder: do something with the input using the &Opaque
16 | |             &i[opaque.0..]
17 | |         })
   | |_________^
note: ...so that reference does not outlive borrowed content
  --> /tmp/lifetimes.rs:16:14
   |
16 |             &i[opaque.0..]
   |              ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
  --> /tmp/lifetimes.rs:6:6
   |
6  | impl<'a> MyStruct<'a> {
   |      ^^
note: ...so that return value is valid for the call
  --> /tmp/lifetimes.rs:10:17
   |
10 |     ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: higher-ranked subtype error
  --> /tmp/lifetimes.rs:7:5
   |
7  | /     pub fn get_func_for_index(
8  | |         &'a self,
9  | |         n: usize,
10 | |     ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
   | |_______________________________________________________________________^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0495`.

这实在是太拗口了,我真的不明白它想告诉我什么。第一部分 (first, the lifetime...)对我来说很有意义,返回的切片不能比闭包参数的寿命长。第二部分(but, the lifetime...)但是对我来说似乎很奇怪 -+ 'a方法签名中的注释指的是闭包本身(因为它捕获了&'a self.foo),而不是闭包返回的值。

是否可以更改代码以在 Rust 中正确建模,或者目前不支持此构造?


问题就在这里:

Some(move |i: &[u8]| {

Every &它有一个生命周期,无论是明确的还是不明确的。寿命是多少&[u8]?显然,它应该是“由闭包调用者选择的生命周期”(即,更高级别的生命周期)。但是当编译器遇到带有自由生命周期参数的引用类型时,即使在闭包的参数列表中,它也会not假设寿命排名较高。您收到的有关“匿名生命周期#1”的错误消息是编译器混淆地试图使非较高级别的生命周期工作。

理论上,编译器可以从impl Fn在返回类型中并认识到闭包的类型需要具有更高排名的生命周期。在这种情况下,这样做还不够聪明,但有一种方法可以说服它:使用带有有界类型参数的局部函数来将闭包的类型严格限制为您想要的边界。

pub fn get_func_for_index(
    &self, // note 1
    n: usize,
) -> Option<impl 'a + for<'inner> Fn(&'inner [u8]) -> &'inner [u8]> { // note 2
    // the reference to the struct member, captured by the closure
    let opaque: &'a Opaque = *self.0.get(n)?;

    // helper function to constrain the closure type
    fn check<F: Fn(&[u8]) -> &[u8]>(f: F) -> F { // note 3
        f
    }

    Some(check(move |i| {
        // placeholder: do something with the input using the &Opaque
        &i[opaque.0..]
    }))
}

操场

请注意以下事项:

  1. &'a self对于这个函数来说太保守了,因为'a是参考的生命周期参数self contains,而不是其生命周期self is borrowed。一般来说,你几乎不应该写&'a self or &'a mut self where 'a是来自外部作用域的命名生命周期。
  2. 我找到了+ 'a在长特征的末尾很容易被错过,尤其是Fn具有返回类型的特征。我建议在这种情况下将生命周期放在前面(将其放在第一位),以明确它与impl比到&'inner [u8]。这是一种风格选择。
  3. Fn(&[u8]) -> &[u8]实际上是完全一样的for<'inner> Fn(&'inner [u8]) -> &'inner [u8],因为省略规则为Fn特征与常规函数相同。无论哪种方式都可以;我发现较短的版本更容易阅读。

类似问题

  • 预期绑定寿命参数,发现具体寿命 [E0271]
  • 如何为闭包参数声明更高级别的生命周期?
  • 为什么这个闭包需要内联或“dyn”? `dyn` 在这里做什么?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

返回捕获引用的更高级的闭包 的相关文章

随机推荐