如何在 Hyper 处理程序之间共享 HashMap? [英] How do I share a HashMap between Hyper handlers?

查看:38
本文介绍了如何在 Hyper 处理程序之间共享 HashMap?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm attempting to learn Rust by implementing a simple in-memory URL shortener with Hyper 0.10. I'm running into an issue that I think is caused by trying to close over a mutable HashMap in my handler:

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?如果是这样,那会是什么样子?另外,我在这个问题上可能完全错了.错误信息对我来说很神秘.

Do I need to use an Arc to share the HashMap between threads? If so, what would that look like? Also, I could be totally wrong about the issue. The error message is very cryptic to me.

推荐答案

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

Please include all the necessary use declarations next time, thanks!

如果您使用的是 nightly Rust,则错误消息就不那么神秘了:

If you're using nightly Rust, the error message is a less cryptic:

期望一个实现 Fntrait 的闭包,但是这个闭包只实现了 FnMut

expected a closure that implements the Fntrait, but this closure only implements FnMut

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

That means that Hyper needs the closure to be shared between threads, so the closure needs to use its environment only via immutable or shared methods – so the usage of &mut short_uris is the offender here. To provide shared threadsafe mutability in Rust, you should use Mutex or RwLock.

请注意,这里不需要Arc——Hyper 管理闭包本身的所有权(可能通过将闭包包装在Arc> 在幕后,或使用类似作用域线程的东西).

Please note that you don't need Arc here – Hyper manages the ownership of the closure itself (probably by wrapping the closure in Arc under the hood, or using something like scoped-threads).

您的代码还有第二个问题——您使用 HashMap<&str, &str>.&str 是一个借用的参考.每次当你在 Rust 中借用了一些东西时,你应该问问自己——从哪里借来的?在这里,您尝试借用真正短暂的字符串——key.to_string()body.to_string().它只是行不通.只需让您的哈希图完全拥有 - HashMap.这是您编译的代码版本:

There's also second issue with your code – you use HashMap<&str, &str>. &str is a borrowed reference. Each time when you have something borrowed in Rust, you should ask yourself – from where? Here you try to borrow from really short-lived strings – key.to_string() and body.to_string(). It just can't work. Just make your hashmap fully owned – HashMap<String, String>. Here's the version of your code which compiles:

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().

I've also got rid of the unnecessary .clone() in the get function.

请注意,这段代码在编译时并不完美——RwLock 锁的持续时间应该更短(getpost 应该花费&RwLock> 作为参数并自行执行锁定)..unwrap() 也可以用更好的方式处理.你也可以考虑使用一些无锁的并发hashmap,应该有一些箱子,但我没有进入这个话题,所以我不会推荐任何.

Please note that this code, while compiles, is not perfect yet – the RwLock locks should last shorter (get and post should take &RwLock<HashMap<String,String>> as an argument and perform the locking by themselves). The .unwrap() also may be handled in a better way. You can also consider using some lockless concurrent hashmap, there should be some crates for that, but I'm not into the topic, so I won't recommend any.

这篇关于如何在 Hyper 处理程序之间共享 HashMap?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆