在下面的代码中,我向stdout
写入了一些文本:
use std::{
io::Write,
sync::{Arc, Mutex},
};
pub struct CmdRunner {}
impl CmdRunner {
pub fn run<W: Write + Send + 'static>(&mut self, stdout_mutex: Arc<Mutex<Option<W>>>) {
// Code that runs the command
let stdout_clone = Arc::clone(&stdout_mutex);
std::thread::spawn(move || {
// This is wrapped in code that listens to Ctrl + C (in raw mode)
let stdout_clone = Arc::clone(&stdout_mutex);
let mut stdout_lock = stdout_clone.lock().unwrap();
stdout_lock.take();
});
let mut stdout_lock = stdout_clone.lock().unwrap();
let stdout = stdout_lock.as_mut().unwrap();
write!(stdout, "Command executed successfully!\r\n").unwrap();
}
}
fn handle_cmd_input<T: Write>(stdout: &mut T) {
write!(stdout, "Input handled!\r\n").unwrap();
}
fn handle_cmd_run<T: Write + std::marker::Send + 'static>(mut stdout: T) {
handle_cmd_input(&mut stdout);
let mut cmd_runner = CmdRunner {};
let stdout_mutex = Arc::new(Mutex::new(Some(stdout)));
cmd_runner.run(stdout_mutex);
}
fn main() {
let stdout = Vec::new();
handle_cmd_run(stdout);
let stdout_string = String::from_utf8_lossy(&stdout);
println!("stdout: {}", stdout_string);
}
正如你所看到的,我尝试打印stdout
,但是产生了一个错误:
44 | let stdout = Vec::new();
| ------ move occurs because `stdout` has type `Vec<u8>`, which does not implement the `Copy` trait
45 |
46 | handle_cmd_run(stdout);
| ------ value moved here
47 |
48 | let stdout_string = String::from_utf8_lossy(&stdout);
| ^^^^^^^ value borrowed here after move
编译器建议我这样写:
46 | handle_cmd_run(stdout.clone());
| ++++++++
但是如果我这样做,write!
将写入克隆的stdout
,而不是原始的stdout
。
Rust Playground
1条答案
按热度按时间rkkpypqq1#
你的问题是所有权编译器抱怨你的代码是正确的。
在编程过程中,你必须知道变量的位置。Rust
Arc<Mutex>
与C/C++的工作方式不同;它们并不存在于变量之外,而是围绕着它。这意味着,一旦你将一个变量移到Arc<Mutex>
中,你将永远无法将它取出来。有了这些知识,编译器为什么抱怨
String::from_utf8_lossy(&stdout);
就有意义了--stdout
变量现在存在于互斥体中,而原始的stdout
变量不再存在。有几种方法可以解决这个问题:
Arc<Mutex>
并将其用于String::from_utf8_lossy
。Arc<Mutex>
,而是从函数返回stdout
数据&mut
引用而不是整个Arc<Mutex>
-这将需要scoped threads。当然,你选择哪一个取决于你的使用情况。
从你提供的小例子中,我个人会选择scoped threads:
之所以这样做是因为线程的
Arc<Mutex>
是不需要的,因为多线程的本质;这是必需的,因为由std::thread::spawn
产生的线程具有开放式生命周期,该生命周期与其产生范围断开。通过std::thread::scope
生成它,线程的生存期连接到父作用域,并且可以从内部使用普通引用。还要注意,move
从闭包中删除,以防止将stdout
变量移动到线程中。**免责声明:**我假设你忘记了
join
线程,否则你的整个代码将没有多大意义。请注意,std::thread::scope
会在最后自动加入,因此父线程将被阻止,直到线程完成,并且"Command executed successfully!"
只有在线程退出后才会被添加到stdout
。如果这不是你想要的行为,那么你必须走另一条路。