复制/移动未实现复制的字段



Capitain Ferris有一张地图(seven_seas.png(,地图上他藏着多件珍宝(坐标为(5,7(和(7,9((。他想为每一件珍宝制作一张单独的财务图。原始地图不应更改。

他决定使用铁锈和图像板条箱。

extern crate image;
use crate::image::GenericImage;

struct Desk {
map_of_seven_seas: image::DynamicImage,
colored_ink: image::Rgba<u8>,
}
impl Desk {
fn create_treasure_map(mut self, x: u32, y:u32) -> image::DynamicImage {
self.map_of_seven_seas.put_pixel(x, y, self.colored_ink);
self.map_of_seven_seas
}
}
fn main() {
let desk = Desk {
map_of_seven_seas: image::open("seven_seas.png").unwrap(),
colored_ink: image::Rgba([255, 0, 0, 255]), // red             
};
println!("Marking my treasures!");
// works fine
let treasure_map_0 = desk.create_treasure_map(5, 7);
treasure_map_0.save("treasure_map_0.png").unwrap();
// breaks
let treasure_map_1 = desk.create_treasure_map(7, 9);
treasure_map_1.save("treasure_map_1.png").unwrap();
}

当费里斯船长意识到他的计划中有一个缺陷时,乌云遮住了天空:

error[E0382]: use of moved value: `desk`
--> src/main.rs:30:26
|
19 |     let desk = Desk {
|         ---- move occurs because `desk` has type `Desk`, which does not implement the `Copy` trait
...
26 |     let treasure_map_0 = desk.create_treasure_map(5, 7);
|                          ---- value moved here
...
30 |     let treasure_map_1 = desk.create_treasure_map(7, 9);
|                          ^^^^ value used here after move
error: aborting due to previous error

Capitain Ferris现在有麻烦了。他似乎只能用一次桌子。他评估了Mate Cargo的提示,并试图导出桌面的Copy特性(将#[derive(Clone, Copy)]添加到结构中(。但他又一次失败了:

error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:4:17
|
4 | #[derive(Clone, Copy)]
|                 ^^^^
5 | struct Desk {
6 |     map_of_seven_seas: image::DynamicImage,
|     -------------------------------------- this field does not implement `Copy`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

image::DynamicImage不能被复制,他也不能为它导出Copy,因为它是机箱的一部分。

Ferris上尉每次想要创建藏宝图(包括从文件系统加载map_of_the_seven_seas(时都必须创建一个新的Desk吗?或者有更好的方法可以重复使用办公桌吗?

这对我来说是一个学习练习,所以一个有解释的好解决方案比一个聪明的破解要好。

每次Ferris上尉想要创建藏宝图(包括从文件系统加载map_of_the_seven_seas(时,他都必须创建一个新的Desk吗?或者有更好的方法可以重复使用办公桌吗?

确实有更好的方法。这里的问题是

fn create_treasure_map(mut self, x: u32, y:u32) -> image::DynamicImage

CCD_ 5参数(无论是否为CCD_。虽然推导Clone是可能的(Copy肯定不是(,但它确实会每次复制整个桌子,这似乎是不必要的。

不过,这是可以避免的,方法是自行借用

fn create_treasure_map(&mut self, x: u32, y:u32) -> &image::DynamicImage {
self.map_of_seven_seas.put_pixel(x, y, self.colored_ink);
&self.map_of_seven_seas
}

但是请注意,这将更新map_of_seven_seas,这意味着treasure_map_1也将包含treasure_map_0中注明的宝藏。

我认为这是不可取的,因为这对第二张地图的持有者来说有点太容易了,而且宁愿击败通常的点宝地图。

因此,解决办法是不更新桌子:只可以永久借用它(我们不需要更新它或它的"参考"七海地图(,并在添加teasure位置之前只复制地图:

fn create_treasure_map(&self, x: u32, y:u32) -> image::DynamicImage {
let mut treasure_map = self.map_of_seven_seas.clone();
treasure_map.put_pixel(x, y, self.colored_ink);
treasure_map
}

这里有很多解决方案:

  • 克隆不复制(简单易读,允许同时进行两次映射,简单明了,我建议这样做,除非您有测试台问题(#[derive(Clone)]
  • 借用而不是消费,但这需要副作用,不允许同时拥有两个地图
  • 当你的文件再次读取(克隆更好(,允许同时映射两个

一个好的解决方案是使用保护模式:

use image::{GenericImage, GenericImageView};
struct Desk {
map_of_seven_seas: image::DynamicImage,
colored_ink: image::Rgba<u8>,
}
struct PixelTmpChange<'a> {
image: &'a mut image::DynamicImage,
pixel: image::Rgba<u8>,
x: u32,
y: u32,
}
impl PixelTmpChange<'_> {
fn new(
image: &mut image::DynamicImage,
x: u32,
y: u32,
tmp_pixel: image::Rgba<u8>,
) -> PixelTmpChange {
let pixel = image.get_pixel(x, y);
image.put_pixel(x, y, tmp_pixel);
PixelTmpChange { image, pixel, x, y }
}
fn get_image(&mut self) -> &mut image::DynamicImage {
self.image
}
}
impl Drop for PixelTmpChange<'_> {
fn drop(&mut self) {
self.image.put_pixel(self.x, self.y, self.pixel);
}
}
impl Desk {
fn create_treasure_map(&mut self, x: u32, y: u32, name: &str) -> image::error::ImageResult<()> {
let mut guard = PixelTmpChange::new(&mut self.map_of_seven_seas, x, y, self.colored_ink);
guard.get_image().save(name)
}
}
fn main() {
let mut desk = Desk {
map_of_seven_seas: image::open("seven_seas.png").unwrap(),
colored_ink: image::Rgba([255, 0, 0, 255]), // red
};
println!("Marking my treasures!");
desk.create_treasure_map(5, 7, "treasure_map_0.png")
.unwrap();
desk.create_treasure_map(7, 9, "treasure_map_1.png")
.unwrap();
}

这个想法是让编译器完成将像素置于原始状态的工作。这是可重复使用和清洁的。我们必须用一生的时间借用图像作为mut参考,所以写起来有点困难,我们必须要求警卫访问图像。但总的来说,如果你不想克隆,我认为这是一个不错的解决方案。

如果你愿意的话,你可以让create_treasure_map()返回保护,以允许更多的选择,但减少私人行为。

最新更新