【问题标题】:Specify that lifetime of argument lasts for function call指定参数的生命周期持续到函数调用
【发布时间】:2018-10-06 20:08:04
【问题描述】:

我有一个特征,它带有一个引用迭代器的函数:

#[derive(Clone)]
struct Dog {
    name: &'static str,
}

trait DogListAction<'a, I>
where
    I: Iterator<Item = &'a Dog>,
{
    fn on_dog_list(&mut self, dog_list: I);
}

struct DogListActionExample {}

impl<'a, I> DogListAction<'a, I> for DogListActionExample
where
    I: Iterator<Item = &'a Dog>,
{
    fn on_dog_list(&mut self, dog_list: I) {
        for dog in dog_list {
            println!("{}", dog.name);
        }
    }
}

fn main() {
    let dogs = vec![Dog { name: "Pluto" }, Dog { name: "Lilly" }];
    let mut action_example = DogListActionExample {};
    let mut dog_list_actions: Vec<Box<DogListAction<_>>> = vec![Box::new(action_example)];
    loop {
        let dog_clone = dogs.clone();
        for dog_list_action in &mut dog_list_actions {
            dog_list_action.on_dog_list(dog_clone.iter());
        }
    }
}

playground

它没有对元素的任何引用,所以它没有必要比函数调用持续更多的时间。

由于我对生命的理解有限,我还不知道如何表达这一点。调用此函数会导致编译错误:

error[E0597]: `dog_clone` does not live long enough
  --> src/main.rs:33:41
   |
33 |             dog_list_action.on_dog_list(dog_clone.iter());
   |                                         ^^^^^^^^^ borrowed value does not live long enough
34 |         }
35 |     }
   |     - `dog_clone` dropped here while still borrowed
36 | }
   | - borrowed value needs to live until here

我猜借用检查器认为dog_clone中的数据可能会在函数结束后被引用,但事实并非如此。

【问题讨论】:

  • 能否请您提供minimal example to reproduce the problem,最好带有操场链接?我看不出你显示的代码有什么问题。 (有关更多信息,另请参阅Rust tag wiki。)
  • 这里是操场上一个最小示例的链接:play.rust-lang.org/…
  • 我仍然认为问题在于编译器认为即使在调用函数之后 DogListAction 可能会引用来自迭代器的数据。我正在尝试添加明确的生命周期规范,但到目前为止没有运气
  • @Clynamen,这里的主要问题是代码可以潜在地将对dog_clone 元素的短期引用保存在寿命较长的dog_list_actions 元素中。
  • 如果将dog_clone.iter() 替换为dogs.iter(),代码将编译。您需要对函数on_dog_list() 进行签名,以表达您不保存迭代器产生的引用的意图。

标签: rust lifetime


【解决方案1】:

这里的问题是代码可能会保存对dog_clone 元素的短期引用到长期存在的dog_list_actions 中。我们需要告诉编译器我们不会保存迭代器产生的引用。可以这样做:

trait DogListAction {
    fn on_dog_list<'a, I>(&'a mut self, dog_list: I)
    where
        I: Iterator<Item = &'a Dog>;
}

现在Item 可以在on_dog_list 通话期间存活。在原始代码中,它们必须存在更长时间。

但是这段代码产生了另一个问题:我们不能再装箱特征DogListAction,因为它包含泛型函数。这里通常的方法是使用 trait 对象:

trait DogListAction {
    fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}

注意Box&lt;dyn Iterator&lt;Item = &amp;'a Dog&gt; + 'a&gt; 中的第二个'a。默认情况下,Rust 添加了一个 'static 特征绑定到盒装特征对象,我们不希望在此处使用 'static

#[derive(Clone)]
struct Dog {
    name: &'static str,
}

trait DogListAction {
    fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}

struct DogListActionExample {}

impl DogListAction for DogListActionExample {
    fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>) {
        for dog in dog_list {
            println!("{}", dog.name);
        }
    }
}

fn main() {
    let dogs = vec![Dog { name: "Pluto" }, Dog { name: "Lilly" }];
    let action_example = DogListActionExample {};
    let mut dog_list_actions: Vec<Box<DogListAction>> = vec![Box::new(action_example)];
    {
        let dogs_clone = dogs.clone();
        for dog_list_action in &mut dog_list_actions {
            dog_list_action.on_dog_list(Box::new(dogs_clone.iter()));
        }
    }
}

Playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 2022-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 2020-11-21
    相关资源
    最近更新 更多