无法将互斥量中的向量附加到另一个向量

Unable to append a vector in a mutex to another vector

我有一个 Vec<String> 对象,它通过 Arc<Mutex> 在线程之间共享。我想获取它的当前值并将其附加到其他地方的另一个向量,然后清除它:

use std::thread;
use std::sync::{Arc, Mutex};

struct Store {
    lines: Vec<String>
}

fn main() {
    let mut store = Store { lines: vec![] };

    let lines = Arc::new(Mutex::new(vec!["Initial value".to_string()]));
    let lines_clone = lines.clone();

    let t2 = thread::spawn(move || {
        // populate lines
    });

    let t1 = thread::spawn(move || {
        let mut lines_result = lines_clone.lock().unwrap();
        store.lines.extend(lines_result); // This will not work
        lines_result.clear();
    });

    let _ = t1.join();
    let _ = t2.join();
}

extend 将不起作用,因为 lines_result 实际上是一个 MutexGuard 对象。我可以遍历对象并追加每个元素,但考虑到我在它超出范围之前清除它的值,是否有更有效的方法?

您可以use Vec::drain()将其所有内容移出到一个迭代器中,这样您就可以在.extend()中使用它并同时清除原始向量。

move || {
    let mut lines_result = lines_clone.lock().unwrap();
    store.lines.extend(lines_result.drain(..));
}

您可以使用 Vec::append() 将所有元素从一个向量移动到另一个向量的末尾。

store.lines.append(&mut lines_clone.lock().unwrap());

查看您得到的错误:

error: the trait bound `std::sync::MutexGuard<'_, std::vec::Vec<std::string::String>>: std::iter::Iterator` is not satisfied [--explain E0277]
  --> <anon>:20:21
   |>
20 |>         store.lines.extend(lines_result); // This will not work
   |>                     ^^^^^^
note: `std::sync::MutexGuard<'_, std::vec::Vec<std::string::String>>` is not an iterator; maybe try calling `.iter()` or a similar method
note: required because of the requirements on the impl of `std::iter::IntoIterator` for `std::sync::MutexGuard<'_, std::vec::Vec<std::string::String>>`

所以正如你所说,MutexGuard 不是 Iterator。大多数时候,即当你在其上调用方法时,你可以使用 MutexGuard<T> 代替 T 因为它实现了 Deref<T> 并且编译器会在需要时自动取消引用.

因此,一种选择是显式取消引用:

store.lines.extend(&*lines_result);

这(部分)有效是因为 &*lines_result 是对实现 IntoIterator 的底层 Vec 的引用。但是,我认为调用 iter 方法更为惯用和清晰(这实际上是编译器将使用 IntoIterator:

store.lines.extend(lines_result.iter());

但是,这仍然无法编译,因为此迭代器 returns 引用了内容,而不是实际的 String。所以我们可以通过要求也克隆它们来解决这个问题:

store.lines.extend(lines_result.iter().cloned());

这可行,但效率低下 - 它会在 Vec 中分配每个 String 的新副本,然后无论如何都要丢弃原件。这(正如另一个答案所说)正是 Vec::drain() 的用途;它 Vec 的部分或全部项目移动到一个新的迭代器中,该迭代器产生值(而不是引用)。 v.drain(..)(使用..全范围)一次把它们全部拿走,给出最后的:

store.lines.extend(lines_result.drain(..));