< 返回版块

wukong 发表于 2023-03-04 22:28

Tags:lifetime

下面这段代码, 会报错,但是错的原因我不太能完全确认,是不是 drop check 原因导致的?

我的理解是:因为 Arg 只是满足 FromInput 的未知类型,所以可能会有手工drop实现? 不确定我理解的对不对,求指导~

use std::marker::PhantomData;

// 通过类型可以从 input 获取具体值
trait FromInput<'a> {
    fn from_input(input: &'a String) -> Self;
}

trait Handler {
    fn call(&self, input: String) -> String;
}

struct MyHandler<Args, F> {
    f: F,
    _phantom: PhantomData<Args>,
}

impl<'a, Args, F> Handler for MyHandler<Args, F>
where
    Args: FromInput<'a>,
    F: Fn(Args) -> String,
{
    fn call(&self, input: String) -> String {
        let args = Args::from_input(&input);
        (self.f)(args)
    }
}

报错信息:

error[E0597]: `input` does not live long enough
  --> src/lib.rs:23:37
   |
17 | impl<'a, Args, F> Handler for MyHandler<Args, F>
   |      -- lifetime `'a` defined here
...
23 |         let args = Args::from_input(&input);
   |                    -----------------^^^^^^-
   |                    |                |
   |                    |                borrowed value does not live long enough
   |                    argument requires that `input` is borrowed for `'a`
24 |         (self.f)(args)
25 |     }
   |     - `input` dropped here while still borrowed

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

Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=02dcb77ae6bfa008889ac70b07b5fcf5

评论区

写评论
作者 wukong 2023-03-05 13:23

谢谢 👍🏻

--
👇
苦瓜小仔: > 如你上面所说的要点,像这类知识,可以在哪里成体系获取?

要点是我自己思考总结的。而且仅仅是(与这里的问题相关的)要点总结,不是系统的总结(意味着有很多细节或者例外)。

最主要的还是多看多思考和观察,从小案例开始理解,变成自己的想法。否则生命周期在你眼里只是一些标记/符号/形式上的东西(以及最基本的省略规则),无法联想,也想不出各种案例。

如果你不从小的难点逐一击破,面对更复杂的东西,你都不知道如何拆解成熟悉的东西。大道至简:)

Rust 官方用户论坛 https://users.rust-lang.org/ 有很多帖子值得阅读,看别人的交流会启发自己。

我可以推荐 @danielhenrymantilla,他的 Github 主页列出的库大都与非常 niche/高深的 生命周期有关(同时他也是一个写宏的专家) —— 虽然我还没有仔细去研究过那些技巧。他正在写一本生命周期的书

书籍:Rust lifetimes: from 'static to ecstatic

仓库:https://github.com/danielhenrymantilla/lifetimes.rs

苦瓜小仔 2023-03-05 10:37

如你上面所说的要点,像这类知识,可以在哪里成体系获取?

要点是我自己思考总结的。而且仅仅是(与这里的问题相关的)要点总结,不是系统的总结(意味着有很多细节或者例外)。

最主要的还是多看多思考和观察,从小案例开始理解,变成自己的想法。否则生命周期在你眼里只是一些标记/符号/形式上的东西(以及最基本的省略规则),无法联想,也想不出各种案例。

如果你不从小的难点逐一击破,面对更复杂的东西,你都不知道如何拆解成熟悉的东西。大道至简:)

Rust 官方用户论坛 https://users.rust-lang.org/ 有很多帖子值得阅读,看别人的交流会启发自己。

我可以推荐 @danielhenrymantilla,他的 Github 主页列出的库大都与非常 niche/高深的 生命周期有关(同时他也是一个写宏的专家) —— 虽然我还没有仔细去研究过那些技巧。他正在写一本生命周期的书

书籍:Rust lifetimes: from 'static to ecstatic

仓库:https://github.com/danielhenrymantilla/lifetimes.rs

作者 wukong 2023-03-05 01:59

感谢苦瓜

要点是: 标注在函数上的生命周期(包括定义在 impl 上的生命周期),都表示这个生命周期来自函数外部,说明带有这个生命周期的引用必须能活到那么长(即超出函数还能存活)。这对于函数的输入参数,并不奇怪,因为外部引用通常可以存活到超出函数,但来自函数内部的引用,通常无法超出函数存活

这个要点特别重要,也增加我对 函数/impl 上生命周期标注的认识。

同时也想请教苦瓜,Rust 的 lifetime 虽然有各种资料,但很多都是比较杂没有成体系,这也是我学习 lifetime 基本概念后,想 deepin 的很大苦恼。比如,除了HRTB这个可以找到并学习;但如你上面所说的要点,像这类知识,可以在哪里成体系获取?不知是否相关较有体系一些的资料推荐,多谢~

--
👇
苦瓜小仔: 或许大多数人对发生在函数上的生命周期标注敏感,但其实 impl 上的泛型也会影响实现中的函数/方法的生命周期。

函数上的 HRTB 例子:https://users.rust-lang.org/t/lifetime-for-passing-in-references-to-methods/89703

和现在的这个一样情况的例子:https://users.rust-lang.org/t/lifetime-error/89437


要点是: 标注在函数上的生命周期(包括定义在 impl 上的生命周期),都表示这个生命周期来自函数外部,说明带有这个生命周期的引用必须能活到那么长(即超出函数还能存活)。这对于函数的输入参数,并不奇怪,因为外部引用通常可以存活到超出函数,但来自函数内部的引用,通常无法超出函数存活 —— 当然两种情况都有例外:

前一种情况的例外:常见于外部生命周期赋予了 &mut self

impl<'a> Layer<'a> {
    fn new_item(&'a mut self

对于 &mut T,通常你可以将使用它看作一次 consume(reborrow 也适用),所以上面的代码意味着获取 Layer<'a> 的生命周期为 'a 的独占引用,并移入 new_item 内,消耗掉它 —— 这个 Layer 活多长,这个独占引用就活多长,换句话说,一旦当你拿到 &'a mut self,你无法获得 &'temporary mut self&'temporary self。是的,当你写

impl<'a> Layer<'a> {
    fn new_item(&mut self

每次 new_item 消耗的是一个不长于 'a 的临时独占引用。这才是生命周期的正确用法。

后一种情况的例外:常见于 临时引用的静态生命周期提升

具体案例,见最近的这个帖子


现在这个例子

    fn call(&self, input: String) -> String {
        let args = Args::from_input(&input);
        (self.f)(args)
    }

虽然数据 input 来自函数外部,但所有权移入函数内,其任何引用都产生于函数内部,无法存活到函数之外。这就是报错信息告诉你的。(仔细阅读和理解报错!)

苦瓜小仔 2023-03-04 23:18

或许大多数人对发生在函数上的生命周期标注敏感,但其实 impl 上的泛型也会影响实现中的函数/方法的生命周期。

函数上的 HRTB 例子:https://users.rust-lang.org/t/lifetime-for-passing-in-references-to-methods/89703

和现在的这个一样情况的例子:https://users.rust-lang.org/t/lifetime-error/89437


要点是: 标注在函数上的生命周期(包括定义在 impl 上的生命周期),都表示这个生命周期来自函数外部,说明带有这个生命周期的引用必须能活到那么长(即超出函数还能存活)。这对于函数的输入参数,并不奇怪,因为外部引用通常可以存活到超出函数,但来自函数内部的引用,通常无法超出函数存活 —— 当然两种情况都有例外:

前一种情况的例外:常见于外部生命周期赋予了 &mut self

impl<'a> Layer<'a> {
    fn new_item(&'a mut self

对于 &mut T,通常你可以将使用它看作一次 consume(reborrow 也适用),所以上面的代码意味着获取 Layer<'a> 的生命周期为 'a 的独占引用,并移入 new_item 内,消耗掉它 —— 这个 Layer 活多长,这个独占引用就活多长,换句话说,一旦当你拿到 &'a mut self,你无法获得 &'temporary mut self&'temporary self。是的,当你写

impl<'a> Layer<'a> {
    fn new_item(&mut self

每次 new_item 消耗的是一个不长于 'a 的临时独占引用。这才是生命周期的正确用法。

后一种情况的例外:常见于 临时引用的静态生命周期提升

具体案例,见最近的这个帖子


现在这个例子

    fn call(&self, input: String) -> String {
        let args = Args::from_input(&input);
        (self.f)(args)
    }

虽然数据 input 来自函数外部,但所有权移入函数内,其任何引用都产生于函数内部,无法存活到函数之外。这就是报错信息告诉你的。(仔细阅读和理解报错!)

苦瓜小仔 2023-03-04 22:42

并不是,而是你需要 HRTB

impl<Args, F> Handler for MyHandler<Args, F>
where
    Args: for<'a> FromInput<'a>,

playground

1 共 5 条评论, 1 页