我是Rust初学者,我无法获得以下代码进行编译。 我想要的是在一个向量中存储几个特征,每个特征也应该具有对借来的变量的只读访问权。
我猜我必须使用“Lifetime bounds” - 就像在thread中讨论的那样 - 因为如果我注释掉第60-68行,那么代码编译得很好。
有人可以解释一下如何使用“生命界限” - 如果这是解决问题的方法 - 或者这不是Rust解决问题的方法吗?如果有更好的方法来实现我想要做的事情,我很高兴改变我对问题的处理方法。
不编译的代码在此处rust-playground。
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();
}
}
我为变量“bitmapdata”获得了3个错误&#39;和&#39;位图&#39;两次。 正如我上面提到的:代码编译没有第60-68行,但导致编译器错误。
有趣的事情:编译器提示消息说明:
reference must be valid for the static lifetime...
那么编译器想要一个静态生命周期? (在代码中替换&#39;静态代码没有帮助)
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 };
...
作为替代方法,我尝试使用集合
的解决方案type CollectionOfImageOperations<'a> = Vec<&'a (ImageOperation<'a> + 'a)>;
但这给了我编译错误,这对我来说比上面的方法更不合理。 (看起来我只能将一个特征对象推送到向量 - 但为什么) - 请参阅rust-playground以获取代码和错误。
任何提示&amp;欢迎和赞赏提示。
答案 0 :(得分: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*/
}
'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>
但这不是正确的;你不能引用某些东西
超过被引用的内容。这意味着我们
要求 '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*/
}
它现在编译。
附录(由aatch建议):此外,
ImageOperation
本身的生命周期似乎是错误的。您没有使用任何内容,代码无需工作。在这种情况下,你最终会处理Box<ImageOperation + 'a>
s,这是为什么你需要特质对象生命周期边界的更好演示。
答案 1 :(得分:10)
我假设您遇到了XY-Problem(试图找出与您的真实问题无关的问题的解决方案)
Rust无法推断堆生命周期。 Box
和Vec
(通过vec![]
)分配是堆分配。它们也可以适用于整个程序,或仅适用于单个范围。 Rust对这些分配的生命周期一无所知,除了它们包含的任何引用需要比刚分配的堆对象更长。
您希望多个ImageOperation
对象具有对bitmap
对象的引用,并且您希望能够将这些ImageOperation
对象移动到堆上。最简单的解决方案是摆脱所有引用和生命周期,并使用移动和引用计数Rc
框的组合。
让我们从PixelImageSimple
类型开始。我们删除了生命周期和参考文献。
struct PixelImageSimple {
pixels: Vec<i32>,
width: i32,
height: i32,
}
现在你有一个拥有一个Vec
的对象。除了通过控制Vec
对象之外,没有其他对象可以控制PixelImageSimple
。
在ImageOperation
特质上。生命期'a
没有出现在特质的主体中。您可以将其删除而不会产生任何后果。
trait ImageOperation {
fn execute_op(&self);
}
现在它变得有趣了。您希望ImageOperationSharpen
类型了解PixelImageSimple
,但其他类型也应该有权访问相同的PixelImageSimple
对象。这是参考计数框发挥作用的地方。 Rc
允许对同一对象的多个不可变“引用”,这些对象都以某种方式拥有对象。您可以clone
Rc
创建更多指向同一对象的框。在内部,计数器会跟踪指向该对象的Rc
的数量。每当Rc
被删除(其范围结束或您明确调用drop
)时,计数器就会减少。当它达到零时,实际上会删除该对象并释放内存。
struct ImageOperationSharpen {
val: i32,
bitmapdata: Rc<PixelImageSimple>
}
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);
}
}
我们现在重复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);
}
}
在这里,我对你要做的事情感到有些困惑。您是否要在致电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);
}
}
这些更改需要进行一些微小的更改,主要是删除&
运算符并添加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();
}
}
如果你想在每个操作中修改PixelImageSimple
对象,我会以不同的方式构建所有内容。首先更改ImageOperation
特征的execute_op
函数,同时更改&mut PixelImageSimple
。
trait ImageOperation {
fn execute_op(&self, bitmap: &mut PixelImageSimple);
}
然后从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);
}
}
}