rust 尝试插入HashMap,但出现编译器错误:无法赋值给X,因为它是借用的

o2g1uqev  于 2023-02-23  发布在  其他
关注(0)|答案(2)|浏览(149)

我试着用Rust和Excel做一些简单的事情。有时候我想创建一个HashMap,键=工作表,值=行。我用office crate来处理Excel。
现在我创建了下面的代码:

let mut workSheets: HashMap<String, Rows> = HashMap::new();
let mut range: Range;

    for conf in config.supa{
        range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
            panic!("The Config-File contains worksheets, which are not present in your Excel-File. Notice that names must match exactly. Error: {}", err);
        });

        let _option = workSheets.insert(conf.worksheet, range.rows());
    }

这段代码在for循环中的“range”变量上给出了以下错误:

error[E0506]: cannot assign to `range` because it is borrowed
  --> src/main.rs:45:9
   |
45 |         range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
   |         ^^^^^ assignment to borrowed `range` occurs here
...
49 |         let _option = workSheets.insert(conf.worksheet, range.rows());
   |                       -----------------------------------------------
   |                       |                                 |
   |                       |                                 borrow of `range` occurs here
   |                       borrow later used here

For more information about this error, try `rustc --explain E0506`.
error: could not compile `supa_importer` due to previous error

即使一遍又一遍地阅读this,我还是不明白。我怎么能用一种生 rust 的方式来解决这个问题呢?

8yparm6h

8yparm6h1#

当你调用range.rows()的时候,它会引用range。请看这里的&self参数。把Rows存储在你的HashMap中是不可行的。因为它不拥有基础数据(您的range变量拥有您想要通过Rows迭代器访问的数据)。一个更好的解决方案是在HashMap中存储Range而不是Rows(这样HashMap的条目将拥有数据),并在需要时对某个值调用.rows()

use office::{Excel, Range, Rows};

use std::collections::HashMap;

fn main() {
    let mut workSheets: HashMap<String, Range> = HashMap::new();
    
    for conf in config.supa{
        let range = excel.worksheet_range(&conf.worksheet).unwrap_or_else(|err|{
            panic!("The Config-File contains worksheets, which are not present in your Excel-File. Notice that names must match exactly. Error: {}", err);
        });

        workSheets.insert(conf.worksheet, range).unwrap();
    }
}

然后,您可以像这样访问Rows,例如:

workSheet["foo"].rows()
km0tfn4u

km0tfn4u2#

首先,用calamine代替office。原因在注解中解释。让我们先把它弄清楚。
实际错误时打开:Jonas已经提供了一个解决方案,所以我将进一步解释“为什么”以及需要注意什么。
关键是始终密切关注一个类型是否拥有其所有数据的所有权(在Rust术语中,“被拥有的类型”),或者它是否拥有对其他数据的引用。判断一个类型是否是“被拥有的类型”的一个简单方法是检查其生存期的类型签名(以一个勾号开头的类型参数,通常为'a)。
拥有类型是最容易使用的,因为它们包含(也称为拥有)自己的数据副本,并且不与任何其他内容关联(也称为包含对任何其他内容的引用)。
然而,通常你不想总是复制内存(克隆)底层数据,就像大多数迭代器的情况一样,因为你在其他地方持有对某个数据片段的引用,所以当你拥有引用时,该数据片段一定不能被释放。
如果你看一下Rows struct的签名(通过Range::rows函数获得),您可以在其类型参数中看到前面提到的'a,这是有意义的,因为它是Range结构体所拥有的数据上的迭代器。如果要存储此结构体,则需要保存对它所引用的Range结构体的持久引用,这正是你犯的错误。
在循环的每次迭代中,都要将Range存储在range变量中,并存储对此Range的底层数据的引用(以Rows结构体的形式)。但是在下一次迭代中,range变量被重新赋值,这意味着它的旧底层数据被释放,使所有指向它的引用无效。悬空引用是非内存安全语言中一个非常常见的错误,并且在安全Rust中是不允许的,因此您的错误“cannot assign to range because it is borrown”。
如果您感兴趣,可以尝试在循环中移动let range = ...,观察错误如何变化,这样可能会更有意义。

相关问题