这是如何在 Rust 中的闭包内重用外部作用域中的值的延续?,为了更好的呈现,开通了新的Q。
// main.rs
// The value will be modified eventually inside `main`
// and a http request should respond with whatever "current" value it holds.
let mut test_for_closure :Arc<RefCell<String>> = Arc::new(RefCell::from("Foo".to_string()));
// ...
// Handler for HTTP requests
// From https://docs.rs/hyper/0.14.8/hyper/service/fn.service_fn.html
let make_svc = make_service_fn(|_conn| async {
Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
if req.version() == Version::HTTP_11 {
let foo:String = *test_for_closure.borrow();
Ok(Response::new(Body::from(foo.as_str())))
} else {
Err("not HTTP/1.1, abort connection")
}
}))
});
Run Code Online (Sandbox Code Playgroud)
不幸的是,我得到RefCell<std::string::String> cannot be shared between threads safely:
RefCell 仅适用于单线程。您将需要使用类似但适用于多个线程的互斥体。您可以在此处阅读有关互斥体的更多信息:https ://doc.rust-lang.org/std/sync/struct.Mutex.html 。
下面是将 Arc<Mutex<>> 移动到闭包中的示例:
use std::sync::{Arc, Mutex};
fn main() {
let mut test: Arc<Mutex<String>> = Arc::new(Mutex::from("Foo".to_string()));
let mut test_for_closure = Arc::clone(&test);
let closure = || async move {
// lock it so it cant be used in other threads
let foo = test_for_closure.lock().unwrap();
println!("{}", foo);
};
}
Run Code Online (Sandbox Code Playgroud)
您的错误消息中的第一个错误是Sync未针对RefCell<String>. 这是设计使然Sync,如rustdoc所述:
\n\n非同步类型是那些以非线程安全形式具有 \xe2\x80\x9cinterior mutability\xe2\x80\x9d 的类型,例如 Cell 和 RefCell。即使通过不可变的共享引用,这些类型也允许\n改变其内容。例如,Cell 上的 set 方法采用 &self,因此\n只需要共享引用 &Cell。该方法不执行同步,因此 Cell 无法同步。
\n
因此,在线程之间共享 RefCell 是不安全的,因为您可能会通过常规共享引用引起数据争用。
\n但如果你把它包裹起来呢Arc?好吧,rustdoc又说得很清楚了:
\n\n只要 T 实现发送\n和同步,Arc 就会实现发送和同步。为什么\xe2\x80\x99 不能将非线程安全类型 T 放入 Arc 中以\n使其成为线程安全的?乍一看这可能有点违反直觉:\n毕竟,\xe2\x80\x99 不是 Arc 线程安全的重点吗?关键在于:\nArc 使拥有相同数据的多个所有权成为线程安全,但它并没有\xe2\x80\x99 为其数据添加线程安全性。考虑\nArc<RefCell>。RefCell 是\xe2\x80\x99t 同步的,如果 Arc 始终是发送的,\nArc<RefCell> 也会是同步的。但是我们\xe2\x80\x99d 有一个问题:\nRefCell 不是线程安全的;它使用非原子操作来跟踪借用计数\n。
\n最后,这意味着您可能需要将 Arc 与某种 std::sync 类型配对,通常是互斥体。
\n
Arc<T>不会的,Sync除非T是Sync因为同样的原因。鉴于此,您可能应该使用 std/tokioMutex而不是RefCell