内部可变性如何影响缓存行为?

2023-11-24

我正在尝试创建一个struct这需要一个Path并根据需要从指定路径加载图像。这是我到目前为止所拥有的:

extern crate image;

use std::cell::{RefCell};
use std::path::{Path};
use image::{DynamicImage};

pub struct ImageCell<'a> {
    image: RefCell<Option<DynamicImage>>,
    image_path: &'a Path, 
}

impl<'a> ImageCell<'a> {
    pub fn new<P: AsRef<Path>>(image_path: &'a P) -> ImageCell<'a>{
        ImageCell { image: RefCell::new(None), image_path: image_path.as_ref() }
    }

    //copied from https://doc.rust-lang.org/nightly/std/cell/index.html#implementation-details-of-logically-immutable-methods
    pub fn get_image(&self) -> &DynamicImage {
        {
            let mut cache = self.image.borrow_mut();
            if cache.is_some() {
                return cache.as_ref().unwrap(); //Error here
            }

            let image = image::open(self.image_path).unwrap();
            *cache = Some(image);
        }

        self.get_image()
    } 
}

这无法编译:

src/image_generation.rs:34:24: 34:29 error: `cache` does not live long enough
src/image_generation.rs:34                 return cache.as_ref().unwrap();
                                                  ^~~~~
src/image_generation.rs:30:46: 42:6 note: reference must be valid for the anonymous lifetime #1 defined on the block at 30:45...
src/image_generation.rs:30     pub fn get_image(&self) -> &DynamicImage {
src/image_generation.rs:31         {
src/image_generation.rs:32             let mut cache = self.image.borrow_mut();
src/image_generation.rs:33             if cache.is_some() {
src/image_generation.rs:34                 return cache.as_ref().unwrap();
src/image_generation.rs:35             }
                           ...
src/image_generation.rs:32:53: 39:10 note: ...but borrowed value is only valid for the block suffix following statement 0 at 32:52
src/image_generation.rs:32             let mut cache = self.image.borrow_mut();
src/image_generation.rs:33             if cache.is_some() {
src/image_generation.rs:34                 return cache.as_ref().unwrap();
src/image_generation.rs:35             }
src/image_generation.rs:36 
src/image_generation.rs:37             let image = image::open(self.image_path).unwrap();
                           ...

我想我明白为什么,因为一生cacheborrow_mut().

有没有办法构建代码以使其有效?


我不完全相信你需要内部可变性。但是,我确实认为您提出的解决方案是一般来说很有用,所以我将详细说明实现它的一种方法。

您当前代码的问题是RefCell提供dynamic借用语义。换句话说,借用 a 的内容RefCell对于 Rust 的借用检查器来说是不透明的。问题是,当你尝试返回一个&DynamicImage当它仍然存在于RefCell,这是不可能的RefCell跟踪其借贷状态。如果一个RefCell允许这种情况发生,那么其他代码可能会覆盖该内容RefCell虽然有一笔贷款&DynamicImage。哎呀!内存安全违规。

因此,从 a 中借用一个值RefCell与你调用时返回的警卫的生命周期相关borrow_mut()。在这种情况下,守卫的生命周期是堆栈帧get_image,在函数返回后不再存在。因此,您不能借用RefCell就像你正在做的那样。

另一种方法(同时保持内部可变性的要求)是move内和外的值RefCell。这使您能够保留缓存语义。

基本思想是返回一个guard它包含动态图像以及返回其来源单元格的指针。处理完动态图像后,防护措施将被删除,我们可以将图像添加回单元的缓存中。

为了保持人体工程学,我们意味着Deref保持警惕,这样你就可以假装它是一个DynamicImage。这是带有一些注释和其他一些内容的代码:

use std::cell::RefCell;
use std::io;
use std::mem;
use std::ops::Deref;
use std::path::{Path, PathBuf};

struct ImageCell {
    image: RefCell<Option<DynamicImage>>,
    // Suffer the one time allocation into a `PathBuf` to avoid dealing
    // with the lifetime.
    image_path: PathBuf,
}

impl ImageCell {
    fn new<P: Into<PathBuf>>(image_path: P) -> ImageCell {
        ImageCell {
            image: RefCell::new(None),
            image_path: image_path.into(),
        }
    }

    fn get_image(&self) -> io::Result<DynamicImageGuard> {
        // `take` transfers ownership out from the `Option` inside the
        // `RefCell`. If there was no value there, then generate an image
        // and return it. Otherwise, move the value out of the `RefCell`
        // and return it.
        let image = match self.image.borrow_mut().take() {
            None => {
                println!("Opening new image: {:?}", self.image_path);
                try!(DynamicImage::open(&self.image_path))
            }
            Some(img) => {
                println!("Retrieving image from cache: {:?}", self.image_path);
                img
            }
        };
        // The guard provides the `DynamicImage` and a pointer back to
        // `ImageCell`. When it's dropped, the `DynamicImage` is added
        // back to the cache automatically.
        Ok(DynamicImageGuard { image_cell: self, image: image })
    }
}

struct DynamicImageGuard<'a> {
    image_cell: &'a ImageCell,
    image: DynamicImage,
}

impl<'a> Drop for DynamicImageGuard<'a> {
    fn drop(&mut self) {
        // When a `DynamicImageGuard` goes out of scope, this method is
        // called. We move the `DynamicImage` out of its current location
        // and put it back into the `RefCell` cache.
        println!("Adding image to cache: {:?}", self.image_cell.image_path);
        let image = mem::replace(&mut self.image, DynamicImage::empty());
        *self.image_cell.image.borrow_mut() = Some(image);
    }
}

impl<'a> Deref for DynamicImageGuard<'a> {
    type Target = DynamicImage;

    fn deref(&self) -> &DynamicImage {
        // This increases the ergnomics of a `DynamicImageGuard`. Because
        // of this impl, most uses of `DynamicImageGuard` can be as if
        // it were just a `&DynamicImage`.
        &self.image
    }
}

// A dummy image type.
struct DynamicImage {
    data: Vec<u8>,
}

// Dummy image methods.
impl DynamicImage {
    fn open<P: AsRef<Path>>(_p: P) -> io::Result<DynamicImage> {
        // Open image on file system here.
        Ok(DynamicImage { data: vec![] })
    }

    fn empty() -> DynamicImage {
        DynamicImage { data: vec![] }
    }
}

fn main() {
    let cell = ImageCell::new("foo");
    {
        let img = cell.get_image().unwrap(); // opens new image
        println!("image data: {:?}", img.data);
    } // adds image to cache (on drop of `img`)
    let img = cell.get_image().unwrap(); // retrieves image from cache
    println!("image data: {:?}", img.data);
} // adds image back to cache (on drop of `img`)

这里有一个非常重要的警告需要注意:这只有一个缓存位置,这意味着如果您调用get_image在第一个守卫被放下之前第二次,由于单元格为空,因此将从头开始生成新图像。这种语义很难改变(在安全代码中),因为您已经致力于使用内部可变性的解决方案。一般来说,内部可变性的全部要点是在调用者无法观察到的情况下改变某些东西。确实,那should这里的情况就是这样,假设打开图像总是返回完全相同的数据。

这种方法可以概括为线程安全的(通过使用Mutex对于内部可变性而不是RefCell),并且根据您的用例选择不同的缓存策略可能会提高性能。例如,regexcrate 使用一个简单的内存池来缓存已编译的正则表达式状态。由于此缓存对调用者来说应该是不透明的,因此它是使用与此处概述的完全相同的机制通过内部可变性来实现的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内部可变性如何影响缓存行为? 的相关文章

随机推荐

  • 是否有经过充分研究的优化来找到穿过图形中每个加权边的最短路径?

    我一直在四处寻找 但似乎每个人最喜欢的问题都有一个稍微不同的情况 TSP 哈密顿量 欧拉等 我有一个图 由 V 顶点 和 E 边 表示 其中每条边是无向的 并且有一定的遍历成本 我想以最小的成本遍历每一条边 并可能重复 直观上 这个问题感觉
  • 在android中显示TextView而不是ImageView

    我想在图像视图上显示文本 我按照 Alesqui 的建议这样做 Android 文字覆盖图像 The preview in Android studio looks fine 但我的实际结果看起来像这样 上面有不必要的文本 我必须在执行期间
  • 在 Fortran 95 中将任意浮点字符串转换为实数

    在 fortran 中是否有任何简单的方法可以将任意浮点字符串转换为实数 想想类似的事情strtod 问题在于READ声明是所有浮点格式编辑描述符都需要显式宽度 到目前为止 我做出的最好的解决方法是这样的 pure function str
  • 有没有办法确定用户加入了哪些多用户会议 (MUCH)?

    我想知道是否有办法查询 XMPP 服务器 传递用户 JID 以找出该用户当前所在的聊天室 如果没有 我们可以查询 jabber 服务器来获取所有活动聊天室的列表吗 顺便说一句 我们正在运行支持多用户聊天的 ejabber 使用 java 库
  • 为什么我应该使用 Deque 而不是 Stack?

    我需要一个Stack我的用例的数据结构 我应该能够将项目推送到数据结构中 并且我只想从堆栈中检索最后一个项目 这堆栈的 JavaDoc says 一组更完整且一致的 LIFO 堆栈操作是 由 Deque 接口及其实现提供 应该 优先使用此类
  • 2x2 按钮网格布局

    我正在尝试创建一个包含 2x2 按钮网格 总共 4 个 的布局 我有以下内容 但它只在左上角创建按钮网格 我希望按钮网格填满整个屏幕
  • 设置输入框背景

    我必须创建一个具有以下背景的输入框 到目前为止 在我的代码中 我有下面的代码 但它没有显示 gt 执行此操作的正确程序是什么 我还必须制作此按钮的几个变体 如下所示 按钮黑色区域上的十字 我只是要使用 span 标记一个类并将图形设置为该类
  • 在 C++ 中从向量复制到向量

    我创建一个向量 A 并想使用以下方法复制到另一个类中的向量 B 这是正确的方法吗 向量A可能会被破坏 我在谷歌搜索 但没有找到好的解决方案和有意义的解释 感谢大家 void StateInit vector
  • Android 上的 OpenId

    我想让我的用户能够使用他们的 OpenId 登录我的 Android 应用程序 有一个像 StackOverflow 一样的文本字段 并要求用户在那里输入他的 OpenId 我环顾四周 但找不到任何允许我在 Android 上实现客户端 O
  • Laravel 5 Flysystem - 从远程磁盘下载文件

    我正在使用 Flysystem 在 Rackspace 上存储站点的文件 上传没问题 但无法弄清楚如何开始下载文件 这是我尝试过的 Storage disk rackspace return response gt download fil
  • 为什么追加documentFragment时需要使用cloneNode?

    我一直在考虑在 Backbone js 应用程序中使用 documentFragments 并且想知道为什么我看到在将 documentFragment 附加到父 DOM 元素时使用 cloneNode 的示例 可以看一个例子here 如果
  • jsr 关键字是什么意思?

    我正在查看一些 Java 代码 并且注意到以下内容 if foo bar baz qux i 0 jsr 433 javac 被它噎住了 说这不是一个语句 并且关键字后面应该有一个分号jsr 我做了一些谷歌搜索 我发现一些包含相同内容的代码
  • Heroku - 无法通过 Play Framework 应用程序设置 postgres 数据库?

    我正在尝试通过 Play Framework 应用程序在 heroku 上设置 postgres 数据库 但我不断收到有关 DATABASE URL 的错误 堆栈跟踪 gt Heroku receiving push gt Play 2 0
  • Flask 蓝图中的 render_template 使用其他蓝图的模板

    我有一个带有蓝图的 Flask 应用程序 每个蓝图都提供了一些模板 当我尝试渲染时index html第二个蓝图的模板 将渲染第一个蓝图的模板 为什么 blueprint2 会覆盖 blueprint1 的模板 如何渲染每个蓝图的模板 ap
  • Cordova Phonegap IOS 应用程序设置。捆绑可能吗?

    所以我是移动开发的新手 但我即将使用 HTML CSS JS 和 Cordova PhoneGap 3 完成我的第一个 IOS 应用程序 我试图允许用户通过 iPhone 的本机 设置 应用程序提供文本输入 灰色齿轮图标 我的应用程序将在
  • 如何更改闪存的起始地址?

    我正在使用 STM32F746ZG 和 FreeRTOS Flash的起始地址是0x08000000 但我想把它改成0x08040000 我通过谷歌搜索了这个问题 但没有找到解决方案 我更改了链接器脚本 如下所示 MEMORY RAM xr
  • 物化视图和表同名

    我有点理解物化视图并且以前曾与它们合作过 最近出现了一个问题 为什么特定报告没有显示最新数据 我调查了这个问题 显然 他们之前有一个加载了 crontab 的临时表 后来切换到了物化视图 当我使用以下查询查看数据库时 表名称已更改 SELE
  • 如何在wp7中设置可点击的文本块中的链接

    我有一个包含链接的文本框 文本中的内容是在运行时生成的 我的问题是文本内的链接不可单击 如何使文本块内的所有链接可单击 以便当我点击链接时它应该打开网络浏览器 在android中 我们可以使用自动链接来设置它 这样的选项在wp7或wp7 1
  • 如何使用动态 OR 语句构建 Linq 查询?

    以下代码 var dynamicQuery from a in context Users select a string args new string aa bb cc foreach string word in args dynam
  • 内部可变性如何影响缓存行为?

    我正在尝试创建一个struct这需要一个Path并根据需要从指定路径加载图像 这是我到目前为止所拥有的 extern crate image use std cell RefCell use std path Path use image