指定参数的生存期持续到函数调用

时间:2018-10-05 19:36:18

标签: rust lifetime

我有一个特征,该特征带有对迭代器的引用:

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

1 个答案:

答案 0 :(得分:2)

这里的问题是代码可能会在寿命更长的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 DogListAction {
    fn on_dog_list<'a>(&'a mut self, dog_list: Box<dyn Iterator<Item = &'a Dog> + 'a>);
}

请注意'a中的第二个Box<dyn Iterator<Item = &'a Dog> + '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