在 Rust 中通过枚举类型实现延迟加载
Implementing a lazy load in by way of enum type in Rust
在我的用例中,我有一个很大的 Layer
类型列表,其中包含图像数据和一些关于图像的元数据:
extern crate image;
type Img = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
#[derive(Debug, Clone)]
struct Layer {
// some metadata fields
lazy_image: Img,
}
到目前为止一切顺利。但是,我在实际处理图像数据之前执行了一些图层剔除,因此我想以惰性方式执行图像加载,以避免加载我实际上不需要的所有图像。
这是我的第一次尝试:
extern crate image;
type Img = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
#[derive(Debug, Clone)]
enum LazyImage {
Path(String),
Image(Img)
}
#[derive(Debug, Clone)]
struct Layer {
// some metadata fields
lazy_image: LazyImage,
}
impl Layer {
fn get_image(&mut self) -> Img {
match self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
img
}
}
}
}
如您所料,这行不通,因为我在 get_image
中的匹配语句存在所有权问题。编译器建议我借用 self.lazy_image
,实际上是在做:
impl Layer {
fn get_image(&mut self) -> Img {
match &self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
img
}
}
}
}
但是,现在我遇到了类型问题:第一个分支(如果图像已加载)returns 引用 &Img
而不是实际的 Img
。好的,没问题,让我们去完整的参考。唯一要注意的是,由于我正在对这些图像进行处理,因此它们需要是可变的:
impl Layer {
fn get_image(&mut self) -> &mut Img {
match &self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
&mut img
}
}
}
}
现在它们是同一类型,但可变性不同:在第一个分支中(如果图像已经加载)我得到一个不可变的引用。我尝试了更多的尝试,但未能成功实现我想要的效果。
正如您可能会说的那样,我对 Rust 有点陌生,并且有点不知所措。此外,我实际上并不确定我想要做什么来实现我想要的目标。
任何帮助,无论是告诉我如何满足编译器的要求,还是只是告诉我这一切都错了,我们将不胜感激。
我建议直接在LazyImage
类型上实现一个get()
方法,因为它感觉加载图像是该类型的内部事务。
由于您很可能不想将图像移出数据结构,因此您应该 return &mut Img
。该引用又需要指向存储在 Image
枚举变体中的值。一个枚举变量字段只能通过解构来提取,所以我们需要在加载图像后再次解构。
一种可能的解决方案是使用两个 if let
语句进行解构:
impl LazyImage {
fn get(&mut self) -> &mut Img {
if let LazyImage::Path(path) = self {
let img = image::open(path).expect("Could not open image").to_rgba();
*self = LazyImage::Image(img);
}
if let LazyImage::Image(img) = self {
img
} else {
unreachable!()
}
}
}
在第一个 if let
之后,枚举保证是一个 Image
。第二次解构也可以使用 match
表达式来完成。编译器会坚持我们包括 else
的情况,因为编译器不能轻易地看到另一个分支是不可达的。
请注意,我们使用 match ergonomics 来避免代码中出现一些噪音。
在我的用例中,我有一个很大的 Layer
类型列表,其中包含图像数据和一些关于图像的元数据:
extern crate image;
type Img = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
#[derive(Debug, Clone)]
struct Layer {
// some metadata fields
lazy_image: Img,
}
到目前为止一切顺利。但是,我在实际处理图像数据之前执行了一些图层剔除,因此我想以惰性方式执行图像加载,以避免加载我实际上不需要的所有图像。
这是我的第一次尝试:
extern crate image;
type Img = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
#[derive(Debug, Clone)]
enum LazyImage {
Path(String),
Image(Img)
}
#[derive(Debug, Clone)]
struct Layer {
// some metadata fields
lazy_image: LazyImage,
}
impl Layer {
fn get_image(&mut self) -> Img {
match self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
img
}
}
}
}
如您所料,这行不通,因为我在 get_image
中的匹配语句存在所有权问题。编译器建议我借用 self.lazy_image
,实际上是在做:
impl Layer {
fn get_image(&mut self) -> Img {
match &self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
img
}
}
}
}
但是,现在我遇到了类型问题:第一个分支(如果图像已加载)returns 引用 &Img
而不是实际的 Img
。好的,没问题,让我们去完整的参考。唯一要注意的是,由于我正在对这些图像进行处理,因此它们需要是可变的:
impl Layer {
fn get_image(&mut self) -> &mut Img {
match &self.lazy_image {
LazyImage::Image(img) => img,
LazyImage::Path(path) => {
let img = image::open(path).expect("Could not open image").to_rgba();
self.lazy_image = LazyImage::Image(img);
&mut img
}
}
}
}
现在它们是同一类型,但可变性不同:在第一个分支中(如果图像已经加载)我得到一个不可变的引用。我尝试了更多的尝试,但未能成功实现我想要的效果。
正如您可能会说的那样,我对 Rust 有点陌生,并且有点不知所措。此外,我实际上并不确定我想要做什么来实现我想要的目标。
任何帮助,无论是告诉我如何满足编译器的要求,还是只是告诉我这一切都错了,我们将不胜感激。
我建议直接在LazyImage
类型上实现一个get()
方法,因为它感觉加载图像是该类型的内部事务。
由于您很可能不想将图像移出数据结构,因此您应该 return &mut Img
。该引用又需要指向存储在 Image
枚举变体中的值。一个枚举变量字段只能通过解构来提取,所以我们需要在加载图像后再次解构。
一种可能的解决方案是使用两个 if let
语句进行解构:
impl LazyImage {
fn get(&mut self) -> &mut Img {
if let LazyImage::Path(path) = self {
let img = image::open(path).expect("Could not open image").to_rgba();
*self = LazyImage::Image(img);
}
if let LazyImage::Image(img) = self {
img
} else {
unreachable!()
}
}
}
在第一个 if let
之后,枚举保证是一个 Image
。第二次解构也可以使用 match
表达式来完成。编译器会坚持我们包括 else
的情况,因为编译器不能轻易地看到另一个分支是不可达的。
请注意,我们使用 match ergonomics 来避免代码中出现一些噪音。