2

I'm trying to build a simple API server in Rust in order to learn the language better. This should accept some datum on PUT /data and store that datum (currently in memory to simplify the problem).

I wrote a MemDb struct to handle inserting and retrieving data (which works):

trait HasRecords {
    fn insert(&mut self, record: &Datum) -> ();
    fn exists(&self, record: &Datum) -> bool;
    fn find(&self, opts: &SearchOpts) -> Vec<Datum>;
    fn records(&self) -> Vec<Datum>;
}

pub struct MemDb {
    store: Vec<Datum>, // also has a ::new() method on the struct
}

In order to keep this storage globally, I was going to create an App struct, which would hold the storage, and pass a route_handler function to the hyper server:

src/app.rs

extern crate hyper;

use db::MemDb;

use futures::future;
use hyper::rt::Future;
use hyper::{Body, Method, Request, Response, StatusCode};
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;

pub struct App {
    storage: MemDb,
}

impl App {
    pub fn new() -> App {
        return App {
            storage: MemDb::new(),
        };
    }
}

pub trait Router {
    fn route_handler(&self) -> Box<Fn(Request<Body>) -> BoxFut>;
}

impl Router for App {
    fn route_handler(&self) -> Box<Fn(Request<Body>) -> BoxFut> {
        return Box::new(|req: Request<Body>| {
            let mut response = Response::new(Body::empty());

            match (req.method(), req.uri().path()) {
                (&Method::GET, "/") => {
                    *response.body_mut() = Body::from("status: ok");
                }
                _ => {
                    *response.status_mut() = StatusCode::NOT_FOUND;
                }
            }

            return Box::new(future::ok(response));
        });
    }
}

I was then using App in my main function to handle the routing:

src/main.rs [extract]

fn main() {
    println!("Starting server...");
    let addr = ([127, 0, 0, 1], 8080).into();
    let app = App::new();
    let service = || service_fn(app.route_handler().as_ref());

    let server = Server::bind(&addr)
        .serve(service)
        .map_err(|e| eprintln!("server error: {}", e));

    hyper::rt::run(server);

    println!("Server listening on port 8080");
}

I'm now getting an error that says that the unboxed closure passed to service_fn cannot be safely shared between threads.

Before I directly pursue this error, is there a better way of approaching this problem? I come from more of a dynamically typed, single-threaded background (NodeJS, Python) and am struggling with the borrow checker.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
GTF
  • 8,031
  • 5
  • 36
  • 59
  • Asking for advice on how to structure your application doesn't seem on topic for Stack Overflow. – Shepmaster Jul 09 '18 at 13:03
  • I believe your question is answered by the answers of [How do I share a HashMap between Hyper handlers?](https://stackoverflow.com/q/38042592/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jul 09 '18 at 20:31
  • @Shepmaster that looks like it answers the question (effectively). – GTF Jul 09 '18 at 22:18

0 Answers0