如何在 Rust 中延迟创建其构造使用 self 的映射条目

2023-11-23

我正在尝试在 Rust 中实现惰性构造/记忆化评估/缓存习惯用法。

有一个外部类型,它有一堆数据和一个访问器方法。访问器方法需要返回缓存的计算(如果有)或计算它并将返回值存储在映射中以供以后使用。缓存的值不需要引用外部值,因此不存在循环引用问题;但它确实需要访问外部值的数据才能构建自身。

这是一个未通过 Rust 借用检查器的完整示例:

use std::collections::HashMap;

pub struct ContainedThing {
    count: usize,
}

impl ContainedThing {
    fn create(thing: &Thing) -> ContainedThing {
        // create uses an arbitrary number of attributes from Thing
        // it doesn't keep any references after returning though
        let count = thing.map.len();
        ContainedThing { count: count }
    }
}

struct Thing {
    map: HashMap<i32, ContainedThing>,
}

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        self.map
            .entry(key)
            .or_insert_with(|| ContainedThing::create(&self))
    }
}

fn main() {}

具体错误是:

error[E0502]: cannot borrow `self` as immutable because `self.map` is also borrowed as mutable
  --> src/main.rs:24:29
   |
22 |         self.map
   |         -------- mutable borrow occurs here
23 |             .entry(key)
24 |             .or_insert_with(|| ContainedThing::create(&self))
   |                             ^^                         ---- borrow occurs due to use of `self` in closure
   |                             |
   |                             immutable borrow occurs here
25 |     }
   |     - mutable borrow ends here

我很难找到实现这个习惯用法的好方法。我尝试了模式匹配的返回值get()而不是entry()API,但仍然与借用检查器发生冲突:match表达式最终也借用了self.

我可以重写get像这样:

pub fn get(&mut self, key: i32) -> &ContainedThing {
    if !self.map.contains_key(&key) {
        let thing = ContainedThing::create(&self);
        self.map.insert(key, thing);
    }
    self.map.get(&key).unwrap()
}

但这非常丑陋(看看那个unwrap)并且似乎需要比应该需要的更多的查找和复制。理想情况下,我想要

  1. 只支付查找一次哈希条目的成本。entry()如果做得正确,应该在找不到时跟踪插入位置。
  2. 减少新构建的值的副本数量。这可能是不可行的,理想情况下我应该有一个就地构造。
  3. 以避免使用unwrap;也就是说,没有毫无意义的模式匹配。

我的笨拙代码是可以实现的最好的吗?


答案是,这具体取决于您需要访问的状态or_insert_with关闭。问题是or_insert_with绝对无法访问地图本身,因为入口 api 采用了地图的可变借用。

如果你需要的一切ContainedThing::create只是地图的大小,那么你只需要提前计算出地图的大小即可。

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        let map_size = self.map.len();
        self.map.entry(&key).or_insert_with(|| {
            // The call to entry takes a mutable reference to the map,
            // so you cannot borrow map again in here
            ContainedThing::create(map_size)
        })
    }
}

不过,我认为这个问题的精神更多的是关于一般策略,所以让我们假设内部还有一些其他状态Thing这也需要创建ContainedThing.

struct Thing {
    map: HashMap<i32, ContainedThing>,
    some_other_stuff: AnotherType, //assume that this other state is also required in order to create ContainedThing
}

impl Thing {
    pub fn get(&mut self, key: i32) -> &ContainedThing {
        //this is the borrow of self
        let Thing {
            ref mut map,
            ref mut some_other_stuff,
        } = *self;

        let map_size = map.len();
        map.entry(key).or_insert_with(|| {
            // map.entry now only borrows map instead of self
            // Here you're free to borrow any other members of Thing apart from map
            ContainedThing::create(map_size, some_other_stuff)
        })
    }
}

这是否真的比其他手动检查解决方案更干净if self.map.contains_key(&key)有待辩论。不过,解构往往是我所追求的策略,允许借用特定的成员self而不是整个结构。

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

如何在 Rust 中延迟创建其构造使用 self 的映射条目 的相关文章

随机推荐

  • 关于 Rails 中的演示者模式。有更好的方法吗?

    我的模型中有 def presenter presenter ProfilePresenter new self presenter end ProfilePresenter 是一个类 它具有 get link get img url si
  • Mysql - “选择类似”不使用索引

    我一直在研究MySQL 5 5 24 WinXP 上的索引 但我找不到服务器在运行时不使用一个索引的原因 LIKE用来 例子是这样的 我创建了一个测试表 create table testTable id varchar 50 primar
  • POST 响应的 TIdHTTP 字符编码

    采取以下情况 procedure Test var Response String begin Response IdHttp Post MyUrL AStream DoSomethingWith Response end 现在网络服务器以
  • 线程池中的活动线程数

    当我编写下面的代码时 为什么我得到的可用线程号是 1022 1020 因为我正在使用线程池 所以我必须获得最多 25 个线程 我猜输出线程数是系统上的可用线程 我需要在 win 表单应用程序中获取线程池中的可用线程号 private voi
  • 将特殊字符转换为 HTML 字符代码

    我正在为客户开发 CMS 他需要编辑内容并使用特殊字符 例如 and 但是 我不希望他必须输入字符代码 例如 reg 有谁知道使用 PHP 自动转换这些字符的好方法吗 您可以使用 htmlentities 来做到这一点 php r echo
  • 将 Angular 应用程序部署在与根文件夹不同的文件夹中

    刚接触 Angular 如果部署在应用程序工作正常nginx var www mydomain com html 但我想将其部署在 var www mydomain com html myapp文件夹 我设置nginx available
  • 是否有 JavaScript / jQuery DOM 更改侦听器?

    本质上我想让一个脚本在 a 的内容被执行时执行DIV改变 由于脚本是分开的 Chrome 扩展程序中的内容脚本和网页脚本 我需要一种简单观察 DOM 状态变化的方法 我可以设置投票 但这看起来很草率 长期以来 DOM3 突变事件是最佳可用解
  • 如何使用 Xcode 4.2 区分多个目标

    我开发了一个应用程序的精简版 现在我想创建一个付费版本 所以我复制了目标 更改了它的名称 因此更改了 plist 和其他具有该名称的内容 现在我必须在代码中进行区分 我正在使用 Xcode 4 2 我在网上看到我必须创建一个预处理器标志 我
  • 如何在 Flutter 中进行交互式通知 [关闭]

    Closed 这个问题需要多问focused 目前不接受答案 I am making a music player app in Flutter and I am not able to find any way to make a wid
  • 使用 jQuery 在 Dom Load 上自动播放 HTML5 视频

    我正在尝试使用 jQuery 在 dom 加载后立即播放视频 这是我的代码 HTML
  • 悬停时旋转或旋转图像

    我想了解如何制作旋转或旋转图像当它悬停时 我想知道如何模拟该功能CSS关于以下代码 img border radius 50 img src http i imgur com 3DWAbmN jpg 您可以使用 CSS3 过渡rotate
  • Sed/Awk - 在模式 x 和 y 之间拉线

    我有一些大型 CSV 文件 我想在其中提取包含模式 x 的 X 行和包含模式 y 的 Y 行之间的所有数据 例如 other data Header data data data Footer other data 我希望能够将页眉 gt
  • Sling 资源与节点

    我无法理解为什么要在吊索中使用资源而不是节点 假设我有一些简单的访问节点 如下所示 NodeIterator headerNode currentNode getNodes loop through and do something wit
  • Div 高度 100%(不包括标题)

    好吧 我知道这个主题有很多问题 但我仍然无法确切地弄清楚如何完成这项工作 This接近问题 但它对我不起作用 我希望我的页面具有 100 的高度 此页面内有一个高度为 40 像素的静态标题 然后是占据剩余高度 100 40 像素 的内容 H
  • 如何使用 Greasemonkey/Tampermonkey 脚本更改类 CSS?

    我正在尝试设置主体的背景图像 但仅限于使用该类的地方banner url HTML如下 基本上 我想强制页面使用以下 CSS banner url background url http www pxleyes com images con
  • Android Studio:无法加载 JVM DLL

    我已经彻底研究了这个问题 并且发布的解决方案对我不起作用 我运行的是 Windows 8 以及最新的 JAVA JDK 64 位 和 Android Studio 版本 系统规格 新 XPS 13 酷睿 i5 8GB RAM SSD 确切的
  • 如何在 F# 中等待异步方法

    如何在 F 中等待异步方法 我有以下代码 type LegoExample let brick Brick BluetoothCommunication COM3 let result brick ConnectAsync Error 成员
  • 在 iOS 15 之前,我只能发送声音本地通知,现在我必须显示横幅

    这在 iOS 15 之前运行良好 let center UNUserNotificationCenter current let content UNMutableNotificationContent content sound UNNo
  • 如何获取JUnit中src/test/resources目录的路径?

    我知道我可以使用以下命令从 src test resources 加载文件 getClass getResource somefile getFile 但是我怎样才能获得 src test resources 的完整路径目录 即我不想加载文
  • 如何在 Rust 中延迟创建其构造使用 self 的映射条目

    我正在尝试在 Rust 中实现惰性构造 记忆化评估 缓存习惯用法 有一个外部类型 它有一堆数据和一个访问器方法 访问器方法需要返回缓存的计算 如果有 或计算它并将返回值存储在映射中以供以后使用 缓存的值不需要引用外部值 因此不存在循环引用问