如何使用生命周期来解决"引用必须对静态生命周期有效"

bum*_*ack 5 lifetime rust

我是Rust初学者,我无法获得以下代码进行编译.我想要的是在一个向量中存储几个特征,每个特征也应该具有对借来的变量的只读访问权限.

我猜我必须使用"Lifetime bounds" - 就像在这个帖子中讨论的那样- 因为如果我注释掉第60-68行,那么代码编译得很好.

有人可以解释如何使用"生命界限" - 如果这是解决问题的方法 - 或者这不是Rust解决问题的方法吗?如果有更好的方法来实现我想要做的事情,我很高兴改变我对问题的处理方法.

不编译的代码在这里和生锈的操场上.

struct PixelImageSimple<'a> {
    pixels: &'a Vec<i32>,
    width: i32,
    height: i32,
}

trait ImageOperation<'a> {
    fn execute_op(&self);
}

struct ImageOperationSharpen<'a> {
    val: i32,
    bitmapdata: &'a PixelImageSimple<'a>
}

impl<'a> ImageOperation<'a> for ImageOperationSharpen<'a> {
    fn execute_op(&self) {
        println!("ImageOperationSharpen - val = {}, width = {}, height = {}, pixels = {:?}",
            &self.val, &self.bitmapdata.width, &self.bitmapdata.height,&self.bitmapdata.pixels);
    }
}

struct ImageOperationRotate<'a> {
    angle: f64,
    bitmapdata: &'a PixelImageSimple<'a>
}

impl<'a> ImageOperation<'a> for ImageOperationRotate<'a> {
    fn execute_op(&self) {
        println!("ImageOperationRotate - angle = {}, width = {}, height = {}, pixels = {:?}",
            &self.angle, &self.bitmapdata.width, &self.bitmapdata.height,&self.bitmapdata.pixels);
    }
}

struct Image<'a> {
    image_operations: Vec<Box<ImageOperation<'a>>>
}

impl<'a> Image<'a> {
    fn new() -> Image<'a> {
        Image { image_operations: vec![] }
    }

    fn add_op(&mut self, image_ops: Box<ImageOperation<'a>>) {
           self.image_operations.push(image_ops);
    }
}

fn main () {
    let bitmapdata = vec![1,2,3];

    let bitmap = PixelImageSimple { pixels: &bitmapdata, width: 222, height:334 };

    let sharpen = ImageOperationSharpen { val: 34, bitmapdata: &bitmap };
    let rotate = ImageOperationRotate { angle: 13.32, bitmapdata: &bitmap };

    let box_sharpen = Box::new(sharpen);
    let box_rotate = Box::new(rotate);

    let mut image = Image::new();

    image.add_op(box_sharpen);
    image.add_op(box_rotate);

    println!("execute_op()");
    for imageops in image.image_operations.iter() {
        imageops.execute_op();
    }
}
Run Code Online (Sandbox Code Playgroud)

变量'bitmapdata'和'bitmap'两次出现3个错误.正如我上面提到的:代码编译没有第60-68行,但导致编译器错误.

有趣的事情:编译器暗示消息说明:

reference must be valid for the static lifetime...
Run Code Online (Sandbox Code Playgroud)

那么编译器想要一个静态生命周期?(在代码中替换'a with'static并没有帮助)

lifetime_bounds.rs:52:46: 52:56 error: `bitmapdata` does not live long enough
lifetime_bounds.rs:52     let bitmap = PixelImageSimple { pixels: &bitmapdata, width: 222, height:334 };
                                                                   ^~~~~~~~~~
note: reference must be valid for the static lifetime...
lifetime_bounds.rs:50:34: 69:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 50:33
lifetime_bounds.rs:50     let bitmapdata = vec![1,2,3];
lifetime_bounds.rs:51
lifetime_bounds.rs:52     let bitmap = PixelImageSimple { pixels: &bitmapdata, width: 222, height:334 };
lifetime_bounds.rs:53
lifetime_bounds.rs:54     let sharpen = ImageOperationSharpen { val: 34, bitmapdata: &bitmap };
lifetime_bounds.rs:55     let rotate = ImageOperationRotate { angle: 13.32, bitmapdata: &bitmap };
                      ...
Run Code Online (Sandbox Code Playgroud)

作为替代方法,我尝试使用集合的解决方案

type CollectionOfImageOperations<'a> = Vec<&'a (ImageOperation<'a> + 'a)>;
Run Code Online (Sandbox Code Playgroud)

但这给了我编译错误,这对我来说比上面的方法更不合理.(看起来我只能将一个特征对象推送到向量 - 但为什么) - 请参阅rust-playground以获取代码和错误.

任何提示和技巧都欢迎和赞赏.

DK.*_*DK. 13

你已经与生命的缺失相冲突了.如有疑问,请写出来!

注意:注释是因为我在这里添加的是无效的Rust语法.另请注意,以下内容并不完全准确,以避免陷入细节之中.

        fn main () {
            /* Lifetimes come from storage, so we'll annotate relevant
               variables with lifetimes.  We're not defining lifetimes,
               we're just assigning names to the lifetimes that the compiler
               will work out during borrow checking. */
/* 'a: */   let bitmapdata = vec![1,2,3];

            /* We'll also substitute the lifetimes into the types of the
               variables and expressions we talk about.  Again, you normally
               couldn't do this, because you can't name lifetimes *within*
               a function. */
/* 'b: */   let bitmap/*: PixelImageSimple<'a> */
            = PixelImageSimple {
                pixels: &/*'a*/bitmapdata,
                width: 222,
                height: 334
            };

            /* We have to pick *one* lifetime here, so we'll pick the
               "narrowest" lifetime.  We'll cheat here and "assume" that
               'a: 'b (read: "'a outlives 'b"); or, in other words, that
               'b < 'a (read: "'b is no longer than 'a"). */
            let sharpen/*: ImageOperationSharpen<'b> as 'b < 'a */
            = ImageOperationSharpen {
                val: 34,
                bitmapdata: &/*'b*/bitmap/*: PixelImageSimple<'a>*/
            };

            let box_sharpen/*: Box<ImageOperationSharpen<'b>>*/
            = Box::new(sharpen);

            /* We'll introduce `'x` here, because it's not immediately clear
               what this lifetime should be.  The compiler will infer it
               from whatever constraints it's been placed under for us. */
/* 'c: */   let mut image/*: Image<'x>*/
            = Image::new();

            /* Wait, where did `'y` come from?  Lifetime elision.  When
               you're dealing with trait objects, the compiler *must* know
               for how long said object is valid.  Normally, the compiler
               would just look at a type's lifetime parameters, but a trait
               object *throws that information away*.  As a result, it
               needs to preserve this information external to the trait.
               This is done using the `Trait + 'k` syntax, where `'k` is
               a lifetime that bounds *all* possible implementations of
               the trait *in this position*.

               This statement is implicit in the original code, but I'm
               adding it here to make things explicit.  I've also included
               the `impl` to denote how the lifetimes transfer around during
               the cast. */
            let box_sharpen/*: Box<ImageOperation<'b> + 'y>*/
            /*where impl<'l> ImageOperation<'l> for ImageOperationSharpen<'l>*/
            = box_sharpen/*: Box<ImageOperationRotate<'b>>*/
                as Box<ImageOperation/*<'b> + 'y*/>;

            /* The only change here is that I *explicitly* borrow `image`
               in order to make all the lifetimes involved explicit.  In
               addition, we now have all the information necessary to work
               out what the inferred lifetimes above should be. */
            (&/*'c */mut image).add_op(
                box_sharpen/* as Box<ImageOperation<'b> + 'y>*/
            );
            /*where impl<'l> Image::<'l>::add_op<'m>(&'m mut self,
                image_ops: Box<ImageOperation<'l> + 'z>)*/
            /*implies 'l = 'b, 'm = 'c, 'z = 'y, 'x = 'l = 'b*/
        }
Run Code Online (Sandbox Code Playgroud)

所有寿命退房...... 除了'z = 'y.什么是'z?无论它是什么,它决定了 所有实现的值的最小生命周期ImageOperation.

什么是合理的?你在谈论Box什么,所以什么才有意义?最短的寿命,还是最宽的?最窄的Box<Trait>几乎无法使用,所以它必须是最宽的.最宽的'static,因此'z = 'static.

但是等等...如果你有Box<ImageOperation<'q> + 'static>,实现类型ImageOperation<'q> 必须居住至少 只要'static一生......这意味着'q 还必须'static.

通过这种推理,'x = 'l = 'b = 'static.但这意味着当我们初始化时sharpen,我们使用以下表达式:

bitmapdata: &'static bitmap: PixelImageSimple<'a>
Run Code Online (Sandbox Code Playgroud)

但这不可能是正确的; 你不能引用比被引用的东西更长的东西.这意味着我们 需要 'a活得比'static...这意味着'a 'static.

但是'a是一个堆栈框架; 它不可能'static!

因此,该程序不健全.

......所以,如果我们只是明确地告诉什么,我们不编译器一个+ 'static我们的特质对象绑定?

struct Image<'a> {
    image_operations: Vec<Box<ImageOperation<'a> + 'a>>
}

impl<'a> Image<'a> {
    fn new() -> Image<'a> {
        Image { image_operations: vec![] }
    }

    fn add_op(&mut self, image_ops: Box<ImageOperation<'a> + 'a>) {
           self.image_operations.push(image_ops);
    }
}

// ...

fn main() {
    // ...
    (&/*'c */mut image).add_op(
        box_sharpen/* as Box<ImageOperation<'b> + 'y>*/
    );
    /*where impl<'l> Image::<'l>::add_op<'m>(&'m mut self,
        image_ops: Box<ImageOperation<'l> + 'z>)*/
    /*implies 'l = 'b, 'm = 'c, 'z = 'y, 'x = 'l = 'b*/
}
Run Code Online (Sandbox Code Playgroud)

它现在编译.

附录(aatch建议):此外,生命ImageOperation本身似乎是错误的.您没有使用任何内容,代码无需工作.在这种情况下,你最终会处理Box<ImageOperation + 'a>s,这更好地证明了为什么你需要特征对象的生命周期边界.


oli*_*obk 10

我假设您遇到了XY问题(试图找出与您的真实问题无关的问题的解决方案)

Rust无法推断堆生命周期.的BoxVec(通过vec![])分配是堆分配.它们也可以适用于整个程序,或仅适用于单个范围.Rust对这些分配的生命周期一无所知,除了它们包含的任何引用需要比刚刚分配的堆对象寿命更长.

您希望多个ImageOperation对象具有对该bitmap对象的引用,并且您希望能够将这些ImageOperation对象移动到堆上.最简单的解决方案是摆脱所有引用和生命周期,并使用移动和引用计数Rc框的组合.

让我们从PixelImageSimple类型开始.我们删除了生命周期和参考.

struct PixelImageSimple {
    pixels: Vec<i32>,
    width: i32,
    height: i32,
}
Run Code Online (Sandbox Code Playgroud)

现在你有一个拥有一个的对象Vec.Vec除了通过控制PixelImageSimple对象之外,没有其他对象可以控制它.

ImageOperation特质上.生命'a没有出现在特质的身体中.你可以删除它而没有后果.

trait ImageOperation {
    fn execute_op(&self);
}
Run Code Online (Sandbox Code Playgroud)

现在它变得有趣了.您希望ImageOperationSharpen类型知道该类型,PixelImageSimple但其他类型也应该具有对同一PixelImageSimple对象的访问权限.这是参考计数框发挥作用的地方.一个Rc允许多个不可变的"引用"到同一个对象,它们都以某种方式拥有对象.你可以cloneRc创造更多的箱子指向同一个对象.在内部,计数器会跟踪Rc指向该对象的数量.每当一个Rc被丢弃(其范围结束或你明确地调用drop)时,计数器就会减少.当它达到零时,实际上删除了对象并释放了内存.

struct ImageOperationSharpen {
    val: i32,
    bitmapdata: Rc<PixelImageSimple>
}
Run Code Online (Sandbox Code Playgroud)

ImageOperation实施是完全一样的,只是所有的生命周期中删除.

impl ImageOperation for ImageOperationSharpen {
    fn execute_op(&self) {
        println!("ImageOperationSharpen - val = {}, width = {}, height = {}, pixels = {:?}",
            &self.val, &self.bitmapdata.width, &self.bitmapdata.height,&self.bitmapdata.pixels);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们现在重复这个ImageOperationRotate:

struct ImageOperationRotate {
    angle: f64,
    bitmapdata: Rc<PixelImageSimple>
}

impl ImageOperation for ImageOperationRotate {
    fn execute_op(&self) {
        println!("ImageOperationRotate - angle = {}, width = {}, height = {}, pixels = {:?}",
            &self.angle, &self.bitmapdata.width, &self.bitmapdata.height,&self.bitmapdata.pixels);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我有点困惑你要做什么.你想PixelImageSimple在你打电话时修改execute_op吗?这是不可能的,因为您拥有的引用和Rc不允许修改指向对象的引用.请参阅本答案的底部以获得解决方案.

struct Image {
    image_operations: Vec<Box<ImageOperation>>
}

impl Image {
    fn new() -> Image {
        Image { image_operations: vec![] }
    }

    fn add_op(&mut self, image_ops: Box<ImageOperation>) {
        self.image_operations.push(image_ops);
    }
}
Run Code Online (Sandbox Code Playgroud)

这些更改需要进行一些最小的更改,主要是删除&操作员并添加Rc::new呼叫.

fn main () {
    let bitmapdata = vec![1,2,3];

    let bitmap = Rc::new(PixelImageSimple { pixels: bitmapdata, width: 222, height:334 });

    let sharpen = ImageOperationSharpen { val: 34, bitmapdata: bitmap.clone() };
    // since we don't create any more ImageOperations, we can move the
    // Rc directly into this object. otherwise we'd also clone it.
    let rotate = ImageOperationRotate { angle: 13.32, bitmapdata: bitmap };

    let box_sharpen = Box::new(sharpen);
    let box_rotate = Box::new(rotate);

    let mut image = Image::new();

    image.add_op(box_sharpen);
    image.add_op(box_rotate);

    println!("execute_op()");
    for imageops in image.image_operations.iter() {
        imageops.execute_op();
    }
}
Run Code Online (Sandbox Code Playgroud)

相关的解决方案

如果你想PixelImageSimple在每个操作中修改对象,我会以不同的方式构建所有内容.首先改变ImageOperation特征的execute_op功能也采取&mut PixelImageSimple.

trait ImageOperation {
    fn execute_op(&self, bitmap: &mut PixelImageSimple);
}
Run Code Online (Sandbox Code Playgroud)

然后Rc<PixelImageSimple>*ImageOperation类型中删除所有内容,而不是向该类型添加PixelImageSimple字段Image.

struct ImageOperationSharpen {
    val: i32,
}
impl ImageOperation for ImageOperationSharpen {
    fn execute_op(&self, bitmap: &mut PixelImageSimple) {
        // you could modify bitmap now.
        println!("ImageOperationSharpen - val = {}, width = {}, height = {}, pixels = {:?}",
            self.val, bitmap.width, bitmap.height, bitmap.pixels);
    }
}

struct ImageOperationRotate {
    angle: f64,
}

impl ImageOperation for ImageOperationRotate {
    fn execute_op(&self, bitmap: &mut PixelImageSimple) {
        println!("ImageOperationRotate - angle = {}, width = {}, height = {}, pixels = {:?}",
            self.angle, bitmap.width, bitmap.height, bitmap.pixels);
    }
}

struct Image {
    image_operations: Vec<Box<ImageOperation>>
    bitmap: PixelImageSimple,
}

impl Image {
    fn new(bitmap: PixelImageSimple) -> Image {
        Image {
            image_operations: vec![],
            bitmap: bitmap,
        }
    }

    fn add_op(&mut self, image_ops: Box<ImageOperation>) {
        self.image_operations.push(image_ops);
    }

    fn apply_ops(&mut self) {
        // iterate over the ops and automatically remove them
        for op in self.image_operations.drain() {
            op.execute_op(&mut self.bitmap);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)