gpt4 book ai didi

hashmap - 对包含引用的临时值强制执行生存期

转载 作者:行者123 更新时间:2023-11-29 08:18:54 26 4
gpt4 key购买 nike

我正在使用 HashMap ,但在如何“释放” HashMap的可变借项时遇到了绊脚石,却找不到如何执行此操作的良好解释。

这仅是示例,其目的不是“解决问题”,而是了解如何完成此任务和/或为什么不应该这样做。

该示例由一个HashMap组成,其中存储了一些简单的Record:

type Map = HashMap<String, Record>;

pub struct Record {
pub count: u32,
pub name: String,
}

impl Record {
fn new<S: Into<String>>(name: S) -> Record {
Record { name: name.into(), count: 0 }
}

pub fn add<'a>(&'a mut self, inc: u32) -> &'a mut Record {
self.count += inc;
self
}
}
add函数在记录中具有可变函数,但这里并不是真正的罪魁祸首。

现在,我们要实现一个函数,该函数返回 Record中对 HashMap的引用,以便我们可以就地对其进行修改。除此之外,我们希望能够控制返回的引用,以便我们可以做一些副作用(对于本示例,假设我们要打印出正在发生的事情就足够了,但是可能处理统计数据和/或访问其他存储或进行延迟评估的其他操作)。为了解决这个问题,我们引入了 Handle结构,该结构保留对 Record的引用以及对记录来自的 HashMap的引用。
pub struct Handle<'a> {
map: &'a Map,
record: &'a Record,
}

impl<'a> Handle<'a> {
fn new(record: &'a Record, map: &'a Map) -> Handle<'a> {
println!("Retrieving record");
Handle { record: record, map: map }
}

fn mut_record(&mut self) -> &mut Record {
println!("Modifying record");
self.record
}
}

假设出于某种原因我们都需要引用,并注意在存在句柄的情况下可以保留对 HashMap的不可变借用就可以了,因此不应该对 HashMap进行任何修改。
Handle只是临时的,我们希望可以大致使用它:
let mut map = HashMap::new();
let foo = get_or_insert(&mut map, "foo");
foo.mut_record().do_something(|record| record.add(3))
get_or_insert的第一个实现是这样的:
pub fn get_or_insert<'a, S>(map: &'a mut Map, name: S) -> Handle<'a>
where S: Into<String>
{
let key = name.into();
let record = map.entry(key.clone()).or_insert(Record::new(key));
Handle::new(record, map)
}

这给出了以下错误:

error[E0502]: cannot borrow `*map` as immutable because it is also borrowed as mutable
--> hashmap.rs:65:29
|
64 | let record = map.entry(key.clone()).or_insert(Record::new(key));
| --- mutable borrow occurs here
65 | Handle::new(record, map)
| ^^^ immutable borrow occurs here
66 | }
| - mutable borrow ends here
HashMap有两个引用,第一个是可变借项。我们需要“释放” map 的第一个可变借位,然后才能获取不变的借位。我试图以这种方式编写代码,并在第一个可变借位周围添加了一个作用域,期望当作用域结束时它会被“释放”:
pub fn get_or_insert<'a, S>(map: &'a mut Map, name: S) -> Handle<'a>
where S: Into<String>
{
let key = name.into();
let record = {
map.entry(key.clone()).or_insert(Record::new(key))
};
Handle::new(record, map)
}

但是错误仍然存​​在。

甚至在合并范围完成之后,仍然存在可变借项,这是很奇怪的。根据 References and Borrowing,借用应该在范围的末尾结束,并且根据 Scope and shadowing范围由块控制,这些块是用大括号括起来的语句的集合,因此从表面上看,似乎后面的函数定义应该以可变变量结束范围借用了对 map 的引用。

您如何以合理的方式实现这样的 Handle,以便 Handle的生存期不超过 HashMap的生存期,并在编译时捕获它?我正在寻找一种创建 Handle的好方法,
  • 通过使用临时Handle作为实现提供的抽象,抽象出对底层存储的访问。
  • 在编译时而不是在运行时捕获滥用情况,这会使RefCellRc丧失资格。
  • 在基础结构中执行一次查找。

  • 我查看了 RefCell,但这将检查从编译时移到了运行时,并且能够在编译时捕获 Handle的滥用将是有益的。

    Rust: Borrowing issues with attempted caching中的问题与此相似,但答案是使用 UnsafeCell,它可以解决检查而不是解决问题。

    对我来说,问题似乎是需要一种方法将可变引用转换为不可变引用并释放可变借用(在代码应允许的限制下),但仍然不确定我是否被误解了某物。

    更新:最初有3个项目符号要使结构更结构化,但已将其重写为仅提出一个问题,以明确目标是什么。

    最佳答案

    在这行上:

    let record = map.entry(key.clone()).or_insert(Record::new(key));
    record的类型为 &'a mut Record,因为 or_insert 返回对存储在 HashMap中的值的可变引用。这样可以保持对 map的借用处于 Activity 状态;这就是为什么您得到错误。

    一种解决方案是在插入后使用 get查找值,以获得不可变的借位。
    pub fn get_or_insert<'a, S>(map: &'a mut Map, name: S) -> Handle<'a>
    where S: Into<String>
    {
    let key = name.into();
    map.entry(key.clone()).or_insert(Record::new(key.clone()));
    let record = map.get(&key).unwrap();
    Handle::new(record, map)
    }

    注意,这仍然不能让您使用您提供的签名来实现 Handle::mut_recordHandle仅具有对 map 和记录的不变引用,并且您不能使用这些引用获得对记录的可变引用。

    关于hashmap - 对包含引用的临时值强制执行生存期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43649480/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com