如何在 Hyper 处理程序之间共享 HashMap?
How do I share a HashMap between Hyper handlers?
我正在尝试通过使用 Hyper 0.10 实现一个简单的内存中 URL 缩短器来学习 Rust。我 运行 遇到了一个问题,我认为这是由于试图关闭我的处理程序中的可变 HashMap
引起的:
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<&str, &str>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(&key.to_string(), &body.to_string());
*res.status_mut() = StatusCode::Created;
res.start().unwrap().write(&key.to_string().into_bytes());
},
Err(_) => *res.status_mut() = StatusCode::BadRequest
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<&str, &str>) {
match req.uri.clone() {
AbsolutePath(path) => {
match short_uris.get::<str>(&path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
},
None => *res.status_mut() = StatusCode::NotFound
}
},
_ => *res.status_mut() = StatusCode::BadRequest
}
}
fn main() {
let mut short_uris: HashMap<&str, &str> = HashMap::new();
short_uris.insert("/example", "http://www.example.com");
Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
match req.method {
hyper::Post => post(req, res, &mut short_uris),
hyper::Get => get(req, res, &short_uris),
_ => *res.status_mut() = StatusCode::MethodNotAllowed
}
}).unwrap();
}
src/main.rs:42:40: 42:46 error: the trait bound `for<'r, 'r, 'r> [closure@src/main.rs:42:47: 48:3 short_uris:std::collections::HashMap<&str, &str>]: std::ops::Fn<(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>)>` is not satisfied [E0277]
src/main.rs:42 Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
我是否需要使用 Arc
在线程之间共享 HashMap
?如果是这样,那会是什么样子?另外,我在这个问题上可能完全错了。错误消息对我来说非常神秘。
下次请包括所有必要的 use
声明,谢谢!
如果您使用的是 nightly Rust,则错误消息不那么神秘:
expected a closure that implements the Fn
trait, but this closure only implements FnMut
这意味着 Hyper 需要在线程之间共享闭包,因此闭包需要仅通过不可变或共享方法使用其环境——因此 &mut short_uris
的用法在这里是违规的。要在 Rust 中提供共享线程安全的可变性,您应该使用 Mutex
或 RwLock
.
请注意,您 不需要 这里需要 Arc
– Hyper 管理闭包本身的所有权(可能通过将闭包包装在 Arc
在引擎盖下,或使用诸如作用域线程之类的东西)。
您的代码还有第二个问题 – 您使用 HashMap<&str, &str>
。 &str
是借用的参考。每次当你从 Rust 中借用某些东西时,你应该问问自己——从哪里借来的?在这里,您尝试借用真正短暂的字符串 – key.to_string()
和 body.to_string()
。它就是行不通。只需让你的 hashmap 完全拥有——HashMap<String, String>
。这是编译的代码版本:
extern crate hyper;
use hyper::server::{Request, Response, Server};
use std::collections::HashMap;
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header::Location;
use std::io::prelude::*;
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<String, String>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(key.to_string(), body);
*res.status_mut() = StatusCode::Created;
res.start()
.unwrap()
.write(&key.to_string().into_bytes())
.unwrap();
}
Err(_) => *res.status_mut() = StatusCode::BadRequest,
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<String, String>) {
match req.uri {
AbsolutePath(ref path) => match short_uris.get(path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
}
None => *res.status_mut() = StatusCode::NotFound,
},
_ => *res.status_mut() = StatusCode::BadRequest,
}
}
fn main() {
let mut short_uris: HashMap<String, String> = HashMap::new();
short_uris.insert("/example".into(), "http://www.example.com".into());
let short_uris = std::sync::RwLock::new(short_uris);
Server::http("0.0.0.0:3001")
.unwrap()
.handle(move |req: Request, mut res: Response| match req.method {
hyper::Post => post(req, res, &mut short_uris.write().unwrap()),
hyper::Get => get(req, res, &short_uris.read().unwrap()),
_ => *res.status_mut() = StatusCode::MethodNotAllowed,
})
.unwrap();
}
我还删除了 get
函数中不必要的 .clone()
。
请注意,这段代码在编译时还不完美——RwLock
锁应该持续更短(get
和 post
应该把 &RwLock<HashMap<String,String>>
作为参数并自行执行锁定)。 .unwrap()
也可以用更好的方式处理。您还可以考虑使用一些无锁并发哈希图,应该有一些板条箱,但我不涉及这个主题,所以我不会推荐任何。
我正在尝试通过使用 Hyper 0.10 实现一个简单的内存中 URL 缩短器来学习 Rust。我 运行 遇到了一个问题,我认为这是由于试图关闭我的处理程序中的可变 HashMap
引起的:
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<&str, &str>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(&key.to_string(), &body.to_string());
*res.status_mut() = StatusCode::Created;
res.start().unwrap().write(&key.to_string().into_bytes());
},
Err(_) => *res.status_mut() = StatusCode::BadRequest
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<&str, &str>) {
match req.uri.clone() {
AbsolutePath(path) => {
match short_uris.get::<str>(&path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
},
None => *res.status_mut() = StatusCode::NotFound
}
},
_ => *res.status_mut() = StatusCode::BadRequest
}
}
fn main() {
let mut short_uris: HashMap<&str, &str> = HashMap::new();
short_uris.insert("/example", "http://www.example.com");
Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
match req.method {
hyper::Post => post(req, res, &mut short_uris),
hyper::Get => get(req, res, &short_uris),
_ => *res.status_mut() = StatusCode::MethodNotAllowed
}
}).unwrap();
}
src/main.rs:42:40: 42:46 error: the trait bound `for<'r, 'r, 'r> [closure@src/main.rs:42:47: 48:3 short_uris:std::collections::HashMap<&str, &str>]: std::ops::Fn<(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>)>` is not satisfied [E0277]
src/main.rs:42 Server::http("0.0.0.0:3001").unwrap().handle(move |req: Request, mut res: Response| {
我是否需要使用 Arc
在线程之间共享 HashMap
?如果是这样,那会是什么样子?另外,我在这个问题上可能完全错了。错误消息对我来说非常神秘。
下次请包括所有必要的 use
声明,谢谢!
如果您使用的是 nightly Rust,则错误消息不那么神秘:
expected a closure that implements the
Fn
trait, but this closure only implementsFnMut
这意味着 Hyper 需要在线程之间共享闭包,因此闭包需要仅通过不可变或共享方法使用其环境——因此 &mut short_uris
的用法在这里是违规的。要在 Rust 中提供共享线程安全的可变性,您应该使用 Mutex
或 RwLock
.
请注意,您 不需要 这里需要 Arc
– Hyper 管理闭包本身的所有权(可能通过将闭包包装在 Arc
在引擎盖下,或使用诸如作用域线程之类的东西)。
您的代码还有第二个问题 – 您使用 HashMap<&str, &str>
。 &str
是借用的参考。每次当你从 Rust 中借用某些东西时,你应该问问自己——从哪里借来的?在这里,您尝试借用真正短暂的字符串 – key.to_string()
和 body.to_string()
。它就是行不通。只需让你的 hashmap 完全拥有——HashMap<String, String>
。这是编译的代码版本:
extern crate hyper;
use hyper::server::{Request, Response, Server};
use std::collections::HashMap;
use hyper::status::StatusCode;
use hyper::uri::RequestUri::AbsolutePath;
use hyper::header::Location;
use std::io::prelude::*;
fn post(mut req: Request, mut res: Response, short_uris: &mut HashMap<String, String>) {
let mut body = String::new();
match req.read_to_string(&mut body) {
Ok(_) => {
let key = short_uris.len();
short_uris.insert(key.to_string(), body);
*res.status_mut() = StatusCode::Created;
res.start()
.unwrap()
.write(&key.to_string().into_bytes())
.unwrap();
}
Err(_) => *res.status_mut() = StatusCode::BadRequest,
}
}
fn get(req: Request, mut res: Response, short_uris: &HashMap<String, String>) {
match req.uri {
AbsolutePath(ref path) => match short_uris.get(path) {
Some(short_uri) => {
*res.status_mut() = StatusCode::MovedPermanently;
res.headers_mut().set(Location(short_uri.to_string()));
}
None => *res.status_mut() = StatusCode::NotFound,
},
_ => *res.status_mut() = StatusCode::BadRequest,
}
}
fn main() {
let mut short_uris: HashMap<String, String> = HashMap::new();
short_uris.insert("/example".into(), "http://www.example.com".into());
let short_uris = std::sync::RwLock::new(short_uris);
Server::http("0.0.0.0:3001")
.unwrap()
.handle(move |req: Request, mut res: Response| match req.method {
hyper::Post => post(req, res, &mut short_uris.write().unwrap()),
hyper::Get => get(req, res, &short_uris.read().unwrap()),
_ => *res.status_mut() = StatusCode::MethodNotAllowed,
})
.unwrap();
}
我还删除了 get
函数中不必要的 .clone()
。
请注意,这段代码在编译时还不完美——RwLock
锁应该持续更短(get
和 post
应该把 &RwLock<HashMap<String,String>>
作为参数并自行执行锁定)。 .unwrap()
也可以用更好的方式处理。您还可以考虑使用一些无锁并发哈希图,应该有一些板条箱,但我不涉及这个主题,所以我不会推荐任何。