“在借来的时候掉在这里”,当做出终身明确时

"dropped here while still borrowed" when making lifetime explicits

提问人:Xavier Detant 提问时间:7/7/2023 更新时间:7/7/2023 访问量:92

问:

我试图通过显式隐式生存期来提高我对 rust borrow 检查器的理解。它实际上来自一个更大的工作问题,但我把它归结为这个(到目前为止)。

我们以这段代码为例:

    struct StringWrapper<'a>(&'a str);
    struct StringWrapperWrapper<'a>(&'a StringWrapper<'a>);

    struct ContainingAValue {
        value: String,
    }

    impl ContainingAValue {
        fn do_something_with_wrapper<F>(&self, f: F)
        where
            F: FnOnce(StringWrapper) -> (),
        {
            let wrapper = StringWrapper(&self.value);
            f(wrapper)
        }

        fn do_something_with_wrapper_wrapper<F>(&self, f: F)
        where
            F: FnOnce(StringWrapperWrapper) -> (),
        {
            let wrapper = StringWrapper(&self.value);
            let tmp = StringWrapperWrapper(&wrapper);
            f(tmp)
        }
    }

此代码编译正常。现在,我想在实现中明确生命周期。

    impl ContainingAValue {
        fn do_something_with_wrapper<'a, F>(&'a self, f: F)
        where
            F: FnOnce(StringWrapper<'a>) -> (),
        {
            let wrapper = StringWrapper(&self.value);
            f(wrapper)
        }

        fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
        where
            F: FnOnce(StringWrapperWrapper<'_>) -> (),
        {
            let wrapper = StringWrapper(&self.value);
            let tmp = StringWrapperWrapper(&wrapper);
            f(tmp)
        }
    }

在那之前,它也可以正常编译。

现在,最大的问题是:我应该把什么寿命而不是放在 的 ?'_StringWrapperWrapper<'_>do_something_with_wrapper_wrapper

我认为这会起作用(在错误中添加了行号以供参考):

  17   │     fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
  18   │     where
  19 ~ │         F: FnOnce(StringWrapperWrapper<'a>) -> (),
  20   │     {
  21   │         let wrapper = StringWrapper(&self.value);
  22   │         let tmp = StringWrapperWrapper(&wrapper);
  23   │         f(tmp)
  24   │     }

但我得到:

   |
17 |     fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
   |                                          -- lifetime `'a` defined here
...
21 |         let wrapper = StringWrapper(&self.value);
   |             ------- binding `wrapper` declared here
22 |         let tmp = StringWrapperWrapper(&wrapper);
   |                                        ^^^^^^^^
   |                                        |
   |                                        borrowed value does not live long enough
   |                                        this usage requires that `wrapper` is borrowed for `'a`
23 |         f(tmp)
24 |     }
   |     - `wrapper` dropped here while still borrowed

所以,我试图添加一个不同的生命周期:

  17 ~ │     fn do_something_with_wrapper_wrapper<'a: 'b, 'b, F>(&'a self, f: F)
  18   │     where
  19 ~ │         F: FnOnce(StringWrapperWrapper<'b>) -> (),
  20   │     {
  21   │         let wrapper = StringWrapper(&self.value);
  22   │         let tmp = StringWrapperWrapper(&wrapper);
  23   │         f(tmp)
  24   │     }

但得到完全相同的错误(被替换为 )。'a'b

我使用 a 的事实对于我的用例错误很重要,因为这会编译:FnOnce

    fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
    where
        F: FnOnce(StringWrapperWrapper<'a>) -> (),
    {
        let wrapper = StringWrapper(&self.value);
        let tmp = StringWrapperWrapper(&wrapper);
        // f(tmp)
    }
Rust Closures Lifetime 借用检查器

评论


答:

1赞 jthulhu 7/7/2023 #1

这是更高等级特征边界的完美用例。正确的代码应该是

impl ContainingAValue {
   fn do_something_with_wrapper<'a, F>(&'a self, f: F)
    where
        F: for<'b> FnOnce(StringWrapper<'b>) -> (),
    {
        let wrapper = StringWrapper(&self.value);
        f(wrapper)
    }
    fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
    where
        F: for<'b> FnOnce(StringWrapperWrapper<'b>) -> (),
    {
        let wrapper = StringWrapper(&self.value);
        let tmp = StringWrapperWrapper(&wrapper);
        f(tmp)
    }
}

这个想法是,无论参数的生存期如何,您都希望工作,这意味着它特别适用于受 作用域限制的生存期,该作用域不能在 之外命名。fdo_something_with_wrapper_wrapperdo_something_with_wrapper_wrapper

你在表达什么

fn do_something_with_wrapper_wrapper<'a, F>(&'a self, f: F)
where
    F: FnOnce(StringWrapperWrapper<'a>) -> (),
{
    let wrapper = StringWrapper(&self.value);
    let tmp = StringWrapperWrapper(&wrapper);
    f(tmp)
}

是调用方选择其参数所需的生存期,该生存期可能比 的范围长。do_something_with_wrapper_wrapperfdo_something_with_wrapper_wrapper

评论

0赞 Xavier Detant 7/7/2023
你是救命恩人。这完全有道理。我不知道那个符号。多谢!
0赞 Xavier Detant 7/8/2023
这是一个关于如何概括(和使用)的后续问题:stackoverflow.com/questions/76638405/......