如何在Hyper处理程序之间共享HashMap?

for*_*uce 7 rust hyper

我试图通过使用Hyper 0.10实现一个简单的内存URL缩短器来学习Rust.我遇到了一个问题,我认为这是因为我试图HashMap在我的处理程序中关闭一个mutable :

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();
}
Run Code Online (Sandbox Code Playgroud)
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| {
Run Code Online (Sandbox Code Playgroud)

我是否需要使用a Arc来共享HashMap线程之间?如果是这样,那会是什么样子?此外,我对这个问题完全错了.错误信息对我来说非常神秘.

krd*_*dln 10

请在use下次包含所有必要的声明,谢谢!

如果您每晚使用Rust,则错误消息不那么神秘:

期望一个实现Fn特征的闭包,但这个闭包只能实现FnMut

这意味着Hyper需要在线程之间共享闭包,因此闭包只需要通过不可变或共享方法来使用它的环境 - 所以&mut short_uris这里的使用者就是罪犯.要在Rust中提供共享线程安全可变性,您应该使用MutexRwLock.

请注意,你不会需要Arc在这里-超(可能通过包装在封闭管理封闭本身的所有权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();
}
Run Code Online (Sandbox Code Playgroud)

我也摆脱.clone()get函数中不必要的东西.

请注意,此代码虽然编译,但还不完美 - RwLock锁应该持续更短(get并且post应该&RwLock<HashMap<String,String>>作为参数并自己执行锁定).该.unwrap()还可以以更好的方式来处理.你也可以考虑使用一些无锁的并发hashmap,应该有一些crate,但我没有进入主题,所以我不会推荐任何.