2016-06-26 1 views
3

私はHyper 0.10で単純なメモリ内URL短縮語を実装することでRustを学習しようとしています。私は私が私のハンドラ内で変更可能なHashMapを超える閉鎖しようとすることで引き起こされていると思う問題に実行しているよ:ハイパーハンドラー間で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> [[email protected]/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を共有するために私が使用する必要がありますか?もしそうなら、どのように見えますか?また、私はこの問題について完全に間違っている可能性があります。エラーメッセージは私には非常にわかりにくいです。

+0

なぜグローバルとしてHashMapを宣言していないのですか? –

+1

@ Dr.Sahib私は一般的にグローバルな状態を避けようとしています。また、グローバル状態の使用を最小限に抑えることを推奨した次のSOの記事を読んでいます:http://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singletonだから私はグローバルな状態なしでそれをやろうとしていました。 – forTruce

+0

夜間のRustに注意するだけで、ここではより良いエラーメッセージが表示されます:「Fn'特性を実装するクロージャが期待されますが、このクロージャは 'FnMut'しか実装しません」 – krdln

答えて

4

次回、必要なすべてのuse宣言を含めてください!

あなたが毎晩ラストを使用している場合は、エラーメッセージが少ない不可解です:

Fn形質を実装閉鎖を期待が、この閉鎖は唯一のことを意味しFnMut

を実装Hyperではスレッド間でクロージャを共有する必要があるため、クロージャは不変メソッドまたは共有メソッドを介してのみその環境を使用する必要があるため、&mut short_urisの使用がここでは犯行者です。 Rustで共有スレッドセーフの可変性を提供するには、MutexまたはRwLockを使用する必要があります。

あなたがここにいない必要Arcを行うことに注意してください - ハイパーは、(おそらく、ボンネットの下にArcに閉鎖を包む、またはスコープ・スレッドのようなものを使用することによって)閉鎖自体の所有権を管理します。

コードに2番目の問題があります。HashMap<&str, &str>を使用しています。 &strは借用参照です。あなたがRustで借りたことがあるたびに、あなたはどこから?ここでは、実際に短い文字列(key.to_string()body.to_string())から借りようとします。それは単に働くことができません。あなたのハッシュマップを完全に所有させるだけです - 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()もより良い方法で処理できます。また、ロックレス同時ハッシュマップの使用を検討することもできますが、そこにはいくつかの枠があるはずですが、私はトピックには入っていないので、私は何もお勧めしません。

+0

ありがとう!あなたの説明は本当に助けになりました。 – forTruce

関連する問題