2016-06-26 5 views
3

Ich versuche Rust zu lernen, indem ich einen einfachen In-Memory URL-Shortener mit Hyper 0.10 implementiere. Ich laufe in ein Problem, das ich denke, wird verursacht, indem man versucht, eine veränderbare HashMap in meinem Handler zu schließen über:Wie teile ich eine HashMap zwischen Hyper-Handlern?

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| { 

Muss ich ein Arc verwenden, um die HashMap zwischen Threads zu teilen? Wenn ja, wie würde das aussehen? Außerdem könnte ich mich in Bezug auf das Problem völlig irren. Die Fehlermeldung ist für mich sehr kryptisch.

+0

Warum deklarieren Sie die HashMap nicht als global? –

+1

@ Dr.Sahib Ich versuche generell den globalen Zustand zu vermeiden. Außerdem habe ich den folgenden SO-Beitrag gelesen, der empfohlen hat, die Verwendung des globalen Status zu minimieren: http://stackoverflow.com/questions/27791532/how-do-i-create-a-global-mutable-singleton. Also habe ich versucht, es ohne globalen Staat zu machen. – forTruce

+0

Nur, dass allnächtlich Rust gibt viel bessere Fehlermeldung hier: "erwartet eine Schließung, die das' Fn' Merkmal implementiert, aber diese Schließung implementiert nur 'FnMut'" – krdln

Antwort

4

Bitte alle notwendigen use Deklarationen das nächste Mal, danke!

Wenn Sie jede Nacht Rust verwenden, ist die Fehlermeldung ein weniger kryptisch:

einen Verschluss erwartet, dass die Fn Merkmal implementiert, aber dieser Verschluss implementiert nur FnMut

Das bedeutet, dass Hyper benötigt die Schließung, die zwischen Threads geteilt wird, also muss der Verschluss seine Umgebung nur über unveränderliche oder geteilte Methoden benutzen - so ist die Verwendung von &mut short_uris hier der Täter. Um eine gemeinsame Thread-Veränderbarkeit in Rust zu ermöglichen, sollten Sie Mutex oder RwLock verwenden.

Bitte beachten Sie, dass Sie Arc hier nicht Notwendigkeit tun - Hyper verwaltet das Eigentum an der Schließung selbst (wahrscheinlich durch den Verschluss in Arc unter der Haube Einwickeln oder wie scoped-Threads etwas).

Es gibt auch ein zweites Problem mit Ihrem Code - Sie verwenden HashMap<&str, &str>. &str ist eine geliehene Referenz. Jedes Mal, wenn du in Rust etwas geliehen hast, solltest du dich fragen - woher? Hier versuchen Sie, aus wirklich kurzlebigen Strings - key.to_string() und body.to_string() zu leihen. Es kann einfach nicht funktionieren. Machen Sie Ihre hashmap vollständig in Besitz - HashMap<String, String>. Hier ist die Version des Codes, die kompiliert:

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(); 
} 

Ich habe auch loswerden der unnötigen .clone() in der get Funktion.

Bitte beachten Sie, dass dieser Code, während compiliert, nicht perfekt ist noch - die RwLock Schleusen sollten kürzer dauern (get und post soll &RwLock<HashMap<String,String>> als Argument nehmen und die Verriegelung durch selbst durchführen). Die .unwrap() kann auch auf eine bessere Weise behandelt werden. Sie können auch in Betracht ziehen, einige lockless gleichzeitige hashmap zu verwenden, es sollte einige Kisten dafür geben, aber ich bin nicht in das Thema, also werde ich keine empfehlen.

+0

Vielen Dank! Ihre Erklärungen haben mir sehr geholfen. – forTruce

Verwandte Themen