简体   繁体   English

来自结构 Hashmap 的多个可变借用

[英]Multiple Mutable Borrows from Struct Hashmap

Running into an ownership issue when attempting to reference multiple values from a HashMap in a struct as parameters in a function call.尝试从结构中的 HashMap 引用多个值作为 function 调用中的参数时遇到所有权问题。 Here is a PoC of the issue.这是该问题的 PoC。

use std::collections::HashMap;

struct Resource {
    map: HashMap<String, String>,
}

impl Resource {
    pub fn new() -> Self {
        Resource {
            map: HashMap::new(),
        }
    }

    pub fn load(&mut self, key: String) -> &mut String {
        self.map.get_mut(&key).unwrap()
    }
}

fn main() {
    // Initialize struct containing a HashMap.
    let mut res = Resource {
        map: HashMap::new(),
    };
    res.map.insert("Item1".to_string(), "Value1".to_string());
    res.map.insert("Item2".to_string(), "Value2".to_string());

    // This compiles and runs.
    let mut value1 = res.load("Item1".to_string());
    single_parameter(value1);
    let mut value2 = res.load("Item2".to_string());
    single_parameter(value2);

    // This has ownership issues.
    // multi_parameter(value1, value2);
}

fn single_parameter(value: &String) {
    println!("{}", *value);
}

fn multi_parameter(value1: &mut String, value2: &mut String) {
    println!("{}", *value1);
    println!("{}", *value2);
}

Uncommenting multi_parameter results in the following error:取消注释multi_parameter会导致以下错误:

28 |     let mut value1 = res.load("Item1".to_string());
   |                      --- first mutable borrow occurs here
29 |     single_parameter(value1);
30 |     let mut value2 = res.load("Item2".to_string());
   |                      ^^^ second mutable borrow occurs here
...
34 |     multi_parameter(value1, value2);
   |                     ------ first borrow later used here

It would technically be possible for me to break up the function calls (using the single_parameter function approach), but it would be more convenient to pass the variables to a single function call.从技术上讲,我可以分解 function 调用(使用 single_parameter function 方法),但将变量传递给单个 ZC1C425268E68385D1AB50741C4 调用会更方便。

For additional context, the actual program where I'm encountering this issue is an SDL2 game where I'm attempting to pass multiple textures into a single function call to be drawn, where the texture data may be modified within the function.对于其他上下文,我遇到此问题的实际程序是一个 SDL2 游戏,我试图将多个纹理传递到一个要绘制的 function 调用中,其中可以在 function 中修改纹理数据。

This is currently not possible, without resorting to unsafe code or interior mutability at least.目前这是不可能的,至少不使用不安全的代码或内部可变性。 There is no way for the compiler to know if two calls to load will yield mutable references to different data as it cannot always infer the value of the key .编译器无法知道两次load调用是否会产生对不同数据的可变引用,因为它不能总是推断出key的值。 In theory, mutably borrowing both res.map["Item1"] and res.map["Item2"] would be fine as they would refer to different values in the map, but there is no way for the compiler to know this at compile time.理论上,可变地借用res.map["Item1"]res.map["Item2"]会很好,因为它们会引用 map 中的不同值,但编译器无法在编译时知道这一点时间。

The easiest way to do this, as already mentioned, is to use a structure that allows interior mutability, like RefCell , which typically enforces the memory safety rules at run-time before returning a borrow of the wrapped value.如前所述,最简单的方法是使用允许内部可变性的结构,例如RefCell ,它通常在运行时强制执行 memory 安全规则,然后返回借用的包装值。 You can also work around the borrow checker in this case by dealing with mut pointers in unsafe code:在这种情况下,您还可以通过处理不安全代码中的mut指针来绕过借用检查器:

pub fn load_many<'a, const N: usize>(&'a mut self, keys: [&str; N]) -> [&'a mut String; N] {
    // TODO: Assert that keys are distinct, so that we don't return
    // multiple references to the same value

    keys.map(|key| self.load(key) as *mut _)
        .map(|ptr| unsafe { &mut *ptr })
}

Rust Playground Rust游乐场

The TODO is important, as this assertion is the only way to ensure that the safety invariant of only having one mutable reference to any value at any time is upheld. TODO很重要,因为这个断言是确保在任何时候都只有一个对任何值的可变引用的安全不变量的唯一方法。

It is, however, almost always better (and easier) to use a known safe interior mutation abstraction like RefCell rather than writing your own unsafe code.然而,使用像RefCell这样已知的安全内部突变抽象几乎总是更好(也更容易),而不是编写自己的不安全代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM