无法将互斥量中的向量附加到另一个向量
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(..));
我有一个 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(..));