【问题标题】:How to tell the borrow checker that a cleared Vec contains no borrows? [duplicate]如何告诉借用检查器清除的 Vec 不包含借用? [复制]
【发布时间】:2020-04-21 08:22:50
【问题描述】:

我正在处理一个庞大的 TSV(制表符分隔值)文件,并希望尽可能高效地执行此操作。为此,我想我会通过在循环之前预先分配它来防止为每一行分配一个新的Vec

let mut line = String::new();
let mut fields = Vec::with_capacity(headers.len());
while reader.read_line(&mut line)? > 0 {
    fields.extend(line.split('\t'));
    // do something with fields
    fields.clear();
}

当然,借用检查器并不好笑,因为我们正在覆盖line,而fields 可能仍然有对它的引用:

error[E0502]: cannot borrow `line` as mutable because it is also borrowed as immutable
  --> src/main.rs:66:28
   |
66 |     while reader.read_line(&mut line)? > 0 {
   |                            ^^^^^^^^^ mutable borrow occurs here
67 |         fields.extend(line.split('\t'));
   |         ------        ---- immutable borrow occurs here
   |         |
   |         immutable borrow later used here

(Playground)

这实际上不是问题,因为fields.clear(); 删除了所有引用,因此在循环开始时调用read_line(&mut line) 时,fields 实际上并没有从line 借用任何东西。

但是我该如何通知借阅检查器呢?

【问题讨论】:

  • 也许看看它在csv 中是如何工作的?还是直接使用csv 设置自定义分隔符?它有摊销分配的配方。
  • csv 还做了更多我不需要的工作,比如处理带引号的字符串,所以我认为手动方式会更快。除了解析之外,我在这里做的处理真的很少。
  • 我记得我看到了令我惊讶的情况,循环中的分配被完全优化掉了,所以也许不需要这样做。我还想知道您在“使用字段做某事”时到底在做什么——这真的需要Vec,还是您可以直接使用迭代器?
  • @SvenMarnach 我正在获取一些字段的值(我从标题中推断出它们的索引)并聚合它们。我确实可以直接使用迭代器,但这会很麻烦,因为我将依赖特定的字段顺序。无论如何,我的优化可能还为时过早,但出于教育目的,我仍然对这个问题的答案感兴趣:)

标签: rust borrow-checker


【解决方案1】:

您的问题与this post 中描述的问题相似。

除了那里的答案(生命周期转换、参考单元)之外,根据您注释掉的复杂操作,您可能根本不需要存储对 line 的引用。例如,考虑以下对 Playground 代码的修改:

use std::io::BufRead;

fn main() -> Result<(), std::io::Error> {
    let headers = vec![1,2,3,4];
    let mut reader = std::io::BufReader::new(std::fs::File::open("foo.txt")?);
    let mut fields = Vec::with_capacity(headers.len());
    loop {
        let mut line = String::new();
        if reader.read_line(&mut line)? == 0 {
            break;
        }
        fields.push(0);
        fields.extend(line.match_indices('\t').map(|x| x.0 + 1));
        // do something with fields
        // each element of fields starts a field; you can use the next
        // element of fields to find the end of the field.
        // (make sure to account for the \t, and the last field having no
        // 'next' element in fields.
        fields.clear();
    }
    Ok(())
}

【讨论】:

  • 嗯,旧的“存储索引而不是引用”技巧。是的,应该可以。
  • 您是否愿意调整此答案并将其移至另一个问题?理想情况下,所有答案都将汇总在一个问题上,而具有相同或相似答案的其他问题则链接在一起。与将相关答案分散在多个问题之间相比,这有助于未来的提问者更轻松地找到他们问题的答案,并减少未来发生变化时的维护负担。
  • 当然。在这种情况下,您所说的“移动”是什么意思?我对评论还很陌生,但如果需要的话,我可以本着这个精神做出一个新的答案。
  • 这正是我的意思(对另一个问题做出新的回答)。如果这个问题被标记为另一个问题的重复,那么也可以删除 this 答案(以帮助人们找到更多答案的问题);但是,您没有义务这样做。
  • 我已经在另一篇文章中给出了答案。不过,我犹豫是否要完全删除这个,因为其他人的// do something with fields 代码很简单,以至于存储索引似乎不必要地复杂,所以我希望答案被埋在那里。建议让它们共存。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-25
  • 1970-01-01
相关资源
最近更新 更多