【问题标题】:Two mutable borrows happen on the same line?两个可变借用发生在同一行?
【发布时间】:2025-12-31 03:20:09
【问题描述】:

我正在尝试使用 Rust 中的 Snowball stemmer crate 来阻止单词向量。应该很简单,但是借用检查器一直拒绝我的代码:

// Read user input
let input = stdin();
let mut stemmer = Stemmer::new("english").unwrap();
for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace().collect();
    for t in &mut query {
        *t = stemmer.stem_str(t);
    }
    // …
}

借用检查器说我在*t = stemmer.stem_str(t); 行上有两个stemmer 的可变借用并拒绝我的代码。 (第 80 行是 for line in input.lock().lines() 块的结束位置。)

57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (first mutable borrow occurs here) (rust-cargo)
57  18 error    E0499  cannot borrow `stemmer` as mutable more than once at a time (second mutable borrow occurs here) (rust-cargo)
80   5 info     E0499  first borrow ends here (rust-cargo)

如果我直接调用stem() 方法,我会得到一个String,但是我不能只调用as_str() 并期望将获得的&amp;str 分配回*t,因为借用检查器抱怨“借来的价值不够长”。

57  18 error           borrowed value does not live long enough (temporary value created here) (rust-cargo)
57  18 info            consider using a `let` binding to increase its lifetime (rust-cargo)
57  42 info            temporary value only lives until here (rust-cargo)
80   5 info            temporary value needs to live until here (rust-cargo)

我不确定这是否与这个库的实现细节有关,但我真的觉得卡在这里。我从没想过阻止输入向量会如此困难。

【问题讨论】:

  • or_exit() 似乎不是宏,正如您在上面的评论中所说,因为缺少 ! (错字?)。另外:您可以添加完整的编译器错误吗? :)
  • @LukasKalbertodt 这是代码库中定义的东西,而不是标准库。我可能应该将这里的代码示例简单地更改为unwrap()。我现在包含了完整的错误。

标签: rust borrow-checker


【解决方案1】:

来自documentation of stem_str

它返回的 str 引用只有在你不再调用 stem 或 stem_str 时才有效;因此,如果您在范围内有这样的引用,Rust 的借用检查器不会让调用其中一个函数。

大概是因为词干分析器实现实际上有某种内部缓冲区,在词干提取时存储单词。

这就是为什么在保持对字符串的引用的同时不能调用stem_str 两次的原因;这样做会使第一个字符串无效!。

我不能只调用as_str() 并期望将获得的&amp;str 分配回*t

编译器再次绝对正确。您正在尝试创建一个值,对其进行引用,存储该引用,然后删除该值!这是一个内存漏洞,你不能这样做。

相反,收集Strings 的向量:

for line in input.lock().lines() {
    let line = line.unwrap();
    let mut query: Vec<_> = line.split_whitespace()
        .map(|t| stemmer.stem(t))
        .collect();
}

我强烈建议您阅读 The Rust Programming Language 并了解引用的工作原理以及它们阻止的内容。在之前处理任何与所有权相关的复杂事情之前,请执行此操作。这些章节具体:

【讨论】:

  • 我读过这本书,但显然这些概念仍然需要一些时间来消化。使用您的方法,我可以获得Strings 的向量,但是有一些函数需要Vec&lt;&amp;str&gt;。有没有一种方便的方法可以将Vec&lt;String&gt; 转换为Vec&lt;&amp;str&gt;,或者避免编写需要Vec&lt;&amp;str&gt; 的函数并将其输入类型更改为Vec&lt;String&gt; 会更惯用吗?
  • @JIXIang 我还建议在 SO 上搜索问题:Convert Vec<String> to Vec<&str>。如果有用,您可以更改函数类型以接受&amp;strString
  • 对。我先用谷歌搜索了它,但我似乎犯了写“Vector”而不是“Vec”的错误,并且没有遇到谷歌的相关结果。感谢您指出这个问题。
  • @JIXIang 还有,如果你写了那些方法,你可能想consider making them take a slice
最近更新 更多