首页 > 解决方案 > 在 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
            }
        }
    }
}

正如您可以猜到的那样,这不起作用,因为我的 match 语句存在所有权问题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
            }
        }
    }
}

但是,现在我遇到了类型问题:第一个分支(如果图像已经加载)返回一个引用&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 有点陌生,并且有点不知所措。此外,我实际上并不确定我想做什么来实现我想要的目标。

任何帮助,无论是告诉我如何满足编译器,甚至只是告诉我这一切都错了,将不胜感激。

标签: rustlazy-loadingborrowing

解决方案


我建议get()直接在该LazyImage类型上实现一个方法,因为感觉加载图像是该类型的内部问题。

由于您很可能不想将图像移出数据结构,因此您应该返回一个&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案例,因为编译器不能轻易地看到另一个分支是不可访问的。

请注意,我们使用匹配人体工程学来避免代码中的一些噪音。


推荐阅读