Copy/Move 未实现复制的字段
Copy/Move an field that doesn't implement Copy
Ferris 船长有一张地图 (seven_seas.png
),其中他隐藏了多个宝藏(在坐标 (5,7) 和 (7,9) 处)。他想为每个宝藏创建一个单独的藏宝图。原图不得改动
他决定为此使用 Rust 和 image crate。
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
费里斯船长现在有麻烦了。看来他的办公桌只能用一次。他评估 Mate Cargo 提示的内容并尝试导出 Desk 的 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
因为它是板条箱的一部分。
Captain Ferris 是否每次要创建藏宝图(包括从文件系统加载 map_of_the_seven_seas)时都必须创建一个新 Desk?或者有没有更好的方法来重用 Desk?
这对我来说是一个学习练习,所以一个有解释的好解决方案比一个聪明的 hack 更好。
Does Captain Ferris have to create a new Desk every time he wants to create a treasure map (including loading the map_of_the_seven_seas from the file system)? Or is there a better way to reuse the Desk?
确实有更好的方法。这里的问题是
fn create_treasure_map(mut self, x: u32, y:u32) -> image::DynamicImage
self
参数(无论是否mut
)表示调用此方法消耗桌面。虽然推导 Clone
是可能的(Copy
绝对不是),但它确实每次都会复制整个桌面,这似乎是不必要的。
这是可以避免的,方法是 borrow self 代替:
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
.
中记录的宝藏
我认为这是不可取的,因为这对第二张地图的持有者来说太容易了,而且宁愿击败通常的藏宝图点。
然后,解决方法是不更新桌子:只不变地借用它(我们不需要更新它或它的七大洋“参考”地图)并在添加戏弄之前只复制地图位置:
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();
}
想法是让编译器完成将像素置于其原始状态的工作。这是可重复使用且干净的。必须用一个lifetime,借用image作为mut reference,所以写起来有点难度,必须要找guard才能拿到image。但总的来说,如果你不想克隆,我认为这是一个不错的解决方案。
如果您愿意,可以让 create_treasure_map()
return 成为警卫,以允许更多选择但减少隐私行为。
Ferris 船长有一张地图 (seven_seas.png
),其中他隐藏了多个宝藏(在坐标 (5,7) 和 (7,9) 处)。他想为每个宝藏创建一个单独的藏宝图。原图不得改动
他决定为此使用 Rust 和 image crate。
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
费里斯船长现在有麻烦了。看来他的办公桌只能用一次。他评估 Mate Cargo 提示的内容并尝试导出 Desk 的 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
因为它是板条箱的一部分。
Captain Ferris 是否每次要创建藏宝图(包括从文件系统加载 map_of_the_seven_seas)时都必须创建一个新 Desk?或者有没有更好的方法来重用 Desk?
这对我来说是一个学习练习,所以一个有解释的好解决方案比一个聪明的 hack 更好。
Does Captain Ferris have to create a new Desk every time he wants to create a treasure map (including loading the map_of_the_seven_seas from the file system)? Or is there a better way to reuse the Desk?
确实有更好的方法。这里的问题是
fn create_treasure_map(mut self, x: u32, y:u32) -> image::DynamicImage
self
参数(无论是否mut
)表示调用此方法消耗桌面。虽然推导 Clone
是可能的(Copy
绝对不是),但它确实每次都会复制整个桌面,这似乎是不必要的。
这是可以避免的,方法是 borrow self 代替:
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
.
我认为这是不可取的,因为这对第二张地图的持有者来说太容易了,而且宁愿击败通常的藏宝图点。
然后,解决方法是不更新桌子:只不变地借用它(我们不需要更新它或它的七大洋“参考”地图)并在添加戏弄之前只复制地图位置:
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();
}
想法是让编译器完成将像素置于其原始状态的工作。这是可重复使用且干净的。必须用一个lifetime,借用image作为mut reference,所以写起来有点难度,必须要找guard才能拿到image。但总的来说,如果你不想克隆,我认为这是一个不错的解决方案。
如果您愿意,可以让 create_treasure_map()
return 成为警卫,以允许更多选择但减少隐私行为。