指定参数的生命周期仅限于函数调用

4

我有一个特性,其中包含一个接受迭代器引用的函数:

#[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());
        }
    }
}

游乐场

它不需要引用元素,因此无需在函数调用后保留。 由于我对生命期的理解有限,我还不知道如何表达这一点。 调用此函数会导致编译错误:

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 中的数据可能在函数结束后被引用,但实际上并非如此。


请提供一个最小化的可重现问题示例,最好附带一个playground链接。我看不出你展示的代码有什么问题。(更多信息请参见Rust标签wiki)。 - Sven Marnach
我仍然认为问题在于编译器认为DogListAction可能会在函数调用后引用迭代器中的数据。我正在尝试添加明确的生命周期规范,但目前还没有成功。 - Clynamen
1
@Clynamen,这里的主要问题是代码可能会将短寿命的dog_clone元素的引用保存到长寿命周期的dog_list_actions元素中。 - red75prime
如果你将 dog_clone.iter() 替换为 dogs.iter(),代码就可以编译。你需要让函数 on_dog_list() 的签名表达你不想保存迭代器产生的引用的意图。 - red75prime
谢谢,我正在考虑这个问题,但我还不知道如何编写。正确的签名是什么? - Clynamen
1个回答

3
这里的问题在于代码可能会在长期存在的dog_list_actions中保存短期引用dog_clone元素。我们需要告诉编译器,我们不会保存迭代器产生的引用。可以这样做:
trait DogListAction {
    fn on_dog_list<'a, I>(&'a mut self, dog_list: I)
    where
        I: Iterator<Item = &'a Dog>;
}

现在,Item 可以在 on_dog_list 调用的整个期间内存在。在原始代码中,它们必须存在更长的时间。
但这段代码又产生了另一个问题:我们不能再将 trait DogListAction 包装为它包含泛型函数。在这里通常的方法是使用 trait 对象:
trait DogListAction {
    fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}

请注意 Box<dyn Iterator<Item = &'a Dog> + 'a> 中的第二个 'a。 Rust 默认会为 trait 对象添加 'static 约束,而我们不希望在这里使用 'static。请保留 html 标签。
#[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()));
        }
    }
}

游乐场


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接