如何告诉借用检查器一个已清空的Vec不包含任何借用?

8

我正在处理一个海量的TSV(制表符分隔值)文件,并希望尽可能高效地完成。为此,我想在循环之前预先分配Vec,以避免为每行分配新的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();
}

当然,借用检查器并不高兴,因为我们在 fields 可能仍然引用其内容的同时覆盖了 line

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 设置自定义分隔符?它具有摊销分配的配方。 - Masklinn
csv 还做了更多我不需要的工作,比如处理带引号的字符串,所以我觉得手动方式会更快。除了解析之外,我在这里真的做了很少的处理。 - Thomas
3
我记得曾经看过这样的情况,令我惊讶的是循环中的分配被完全优化掉了,因此也许不需要这个。我还想知道你在“对字段进行操作”时究竟在做什么 - 这是否真的需要一个 Vec ,或者你可以直接使用迭代器呢? - Sven Marnach
@SvenMarnach,我正在获取一些字段的值(其索引是从标题推导出来的),并对它们进行聚合。我确实可以直接使用迭代器,但这会很麻烦,因为我将依赖于特定的字段顺序。无论如何,我的优化可能过早了,但出于教育目的,我仍然对这个问题的答案感兴趣 :) - Thomas
1个回答

1

您的问题看起来与此帖子中描述的类似。

除了那里的答案(终身转换,refcells),根据您注释掉的复杂操作,您可能根本不需要存储对line的引用。例如,考虑以下修改您的游乐场代码:

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(())
}

嗯,旧的“存储索引而不是引用”的技巧。没错,那应该可以。 - Thomas
你愿意调整这个答案并将其移动到另一个问题吗?理想情况下,所有答案都应聚合在一个问题上,并将具有相同或类似答案的其他问题链接在一起。这有助于未来的提问者更轻松地找到他们的问题的答案,并减少维护负担,与将相关答案分散在多个问题之间相比。 - trent
当然。在这个上下文中,“移动”是什么意思?我对评论相对较新,但如果需要的话,我可以按照这个回答的精神提供一个新的答案。 - EriekeWeitenberg
这正是我想表达的(在另一个问题上新建一个答案)。如果将此问题标记为另一个问题的副本,那么删除答案也符合事物的精神(以帮助引导人们前往回答更多的问题); 但是,您并不是有义务这样做。 - trent
1
我在另一篇帖子中已经回答了。虽然如此,我还是犹豫是否要完全删除这个帖子,因为另一个人的代码 // do something with fields 很简单,存储索引似乎是不必要的复杂操作,所以我认为答案可能会被埋没在那里。建议让它们共存。 - EriekeWeitenberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接