在actix-web路由功能中,我发现从受互斥锁保护的对象获取独立数据后,互斥锁必须处于锁定状态才能使用这些数据。如果互斥体仅在获取数据时加锁,则使用数据时会出现编译错误。这是可以直接编译的代码:
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Mutex;
use async_trait::async_trait;
use actix_web::{web, App, HttpServer, HttpResponse, Result as ActixResult};
#[async_trait]
pub trait Reader: Send + Debug { }
#[derive(Clone, Debug)]
pub struct RemoteReader;
#[async_trait]
impl Reader for RemoteReader { }
#[derive(Default)]
pub struct DataReaders(HashMap<String, Box<dyn Reader>>);
impl DataReaders {
pub fn get_readers(&self, ids: &str) -> ActixResult<Vec<&dyn Reader>> {
let mut readers = Vec::new();
for id in ids.split(',') {
readers.push(self.0.get(id).unwrap().as_ref());
}
Ok(readers)
}
}
async fn get(data: web::Data<Mutex<DataReaders>>) -> ActixResult<HttpResponse> {
let mut readers = data.lock().unwrap().get_readers("1,2")?;
if readers.is_empty() {
return Ok(HttpResponse::NotFound().finish())
}
// Below will call multiple asynchronous functions, so I do not want to
// hold the lock of DataReaders for a long time.
readers.clear();
Ok(HttpResponse::Ok().finish())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let mut readers = DataReaders{ 0: Default::default() };
readers.0.insert("1".to_string(), Box::new(RemoteReader{}));
readers.0.insert("2".to_string(), Box::new(RemoteReader{}));
let counter = web::Data::new(Mutex::new(readers));
HttpServer::new(move || {
App::new()
.app_data(counter.clone())
.route("/", web::get().to(get))
}).bind(("127.0.0.1", 8080))?.run().await
}
编译此代码并出现错误:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:30:23
|
30 | let mut readers = data.lock().unwrap().get_readers("1,2")?;
| ^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
31 | if readers.is_empty() {
| ------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
我只想使用互斥锁来保护 HashMap,因为它会在其他路由函数中被更改。我认为从“get_readers”返回的 Vec<&dyn Reader> 对象不应该受到保护,因为其中的所有项目都是只读的。 那么我该如何解决这个问题呢?谢谢!
如果在此期间有另一条路线插入到
HashMap
中,则可能需要重新分配,并且您持有的 &dyn Reader
引用将失效。事实上,Rust 不知道其他编写者是否可能更改或删除您正在使用的 HashMap
项。因此,除非您从 HashMap 克隆正在使用的内容,否则 Mutex
将需要保持锁定状态。
考虑将特征对象保存在重新计数的分配中,例如
Arc<dyn Reader>
,而不是 Box
。您可以廉价地克隆它们,然后放弃锁定:
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use actix_web::{web, App, HttpServer, HttpResponse, Result as ActixResult};
#[async_trait]
pub trait Reader: Send + Debug { }
#[derive(Clone, Debug)]
pub struct RemoteReader;
#[async_trait]
impl Reader for RemoteReader { }
#[derive(Default)]
pub struct DataReaders(HashMap<String, Arc<dyn Reader>>);
impl DataReaders {
pub fn get_readers(&self, ids: &str) -> ActixResult<Vec<Arc<dyn Reader>>> {
let mut readers = Vec::new();
for id in ids.split(',') {
readers.push(self.0.get(id).unwrap().clone());
}
Ok(readers)
}
}
async fn get(data: web::Data<Mutex<DataReaders>>) -> ActixResult<HttpResponse> {
let mut readers = data.lock().unwrap().get_readers("1,2")?;
if readers.is_empty() {
return Ok(HttpResponse::NotFound().finish())
}
// Below will call multiple asynchronous functions, so I do not want to
// hold the lock of DataReaders for a long time.
readers.clear();
Ok(HttpResponse::Ok().finish())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let mut readers = DataReaders{ 0: Default::default() };
readers.0.insert("1".to_string(), Arc::new(RemoteReader{}));
readers.0.insert("2".to_string(), Arc::new(RemoteReader{}));
let counter = web::Data::new(Mutex::new(readers));
HttpServer::new(move || {
App::new()
.app_data(counter.clone())
.route("/", web::get().to(get))
}).bind(("127.0.0.1", 8080))?.run().await
}