Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass State in as a reference #3

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/handlers/simple_async_handlers_await/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ edition = "2018"
gotham = { path = "../../../gotham" }
gotham_derive = { path = "../../../gotham_derive" }

mime = "0.3.16"
mime = "0.3"
futures = "0.3.1"
serde = "1.0.104"
serde_derive = "1.0.104"
serde = "1.0"
serde_derive = "1.0"
tokio = "0.2.9"
29 changes: 8 additions & 21 deletions examples/handlers/simple_async_handlers_await/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use futures::prelude::*;
use std::pin::Pin;
use std::time::{Duration, Instant};

use gotham::hyper::{Body, StatusCode};
use gotham::hyper::{Body, Response, StatusCode};

use gotham::handler::HandlerResult;
use gotham::handler::{HandlerError, HandlerResult};
use gotham::helpers::http::response::create_response;
use gotham::router::builder::DefineSingleRoute;
use gotham::router::builder::{build_simple_router, DrawRoutes};
Expand Down Expand Up @@ -60,34 +60,21 @@ fn sleep(seconds: u64) -> SleepFuture {

/// This handler sleeps for the requested number of seconds, using the `sleep()`
/// helper method, above.
async fn sleep_handler(mut state: State) -> HandlerResult {
let seconds = QueryStringExtractor::take_from(&mut state).seconds;
async fn sleep_handler(state: &mut State) -> HandlerResult {
let seconds = QueryStringExtractor::take_from(state).seconds;
println!("sleep for {} seconds once: starting", seconds);
// Here, we call the sleep function and turn its old-style future into
// a new-style future. Note that this step doesn't block: it just sets
// up the timer so that we can use it later.
let sleep_future = sleep(seconds);

// Here is where the serious sleeping happens. We yield execution of
// this block until sleep_future is resolved.
// The Ok("slept for x seconds") value is stored in result.
let data = sleep_future.await;

// Here, we convert the result from `sleep()` into the form that Gotham
// expects: `state` is owned by this block so we need to return it.
// We also convert any errors that we have into the form that Hyper
// expects, using the helper from IntoHandlerError.
let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, data);
println!("sleep for {} seconds once: finished", seconds);
Ok((state, res))
Ok(res)
}

/// It calls sleep(1) as many times as needed to make the requested duration.
///
/// Notice how much easier it is to read than the version in
/// `simple_async_handlers`.
async fn loop_handler(mut state: State) -> HandlerResult {
let seconds = QueryStringExtractor::take_from(&mut state).seconds;
async fn loop_handler(state: &mut State) -> HandlerResult {
let seconds = QueryStringExtractor::take_from(state).seconds;
println!("sleep for one second {} times: starting", seconds);

// The code within this block reads exactly like syncronous code.
Expand All @@ -106,7 +93,7 @@ async fn loop_handler(mut state: State) -> HandlerResult {
Body::from(accumulator),
);
println!("sleep for one second {} times: finished", seconds);
Ok((state, res))
Ok(res)
}

/// Create a `Router`.
Expand Down
5 changes: 3 additions & 2 deletions gotham/src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ pub mod assets;
pub use self::error::{HandlerError, IntoHandlerError};

/// A type alias for the results returned by async fns that can be passed to to_async.
pub type HandlerResult = std::result::Result<(State, Response<Body>), (State, HandlerError)>;
pub type HandlerResult = std::result::Result<Response<Body>, HandlerError>;

/// A type alias for the trait objects returned by `HandlerService`.
///
/// When the `Future` resolves to an error, the `(State, HandlerError)` value is used to generate
/// an appropriate HTTP error response.
pub type HandlerFuture = dyn Future<Output = HandlerResult> + Send;
pub type HandlerFuture =
dyn Future<Output = std::result::Result<(State, Response<Body>), (State, HandlerError)>> + Send;

/// A `Handler` is an asynchronous function, taking a `State` value which represents the request
/// and related runtime state, and returns a future which resolves to a response.
Expand Down
25 changes: 18 additions & 7 deletions gotham/src/router/builder/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::panic::RefUnwindSafe;

use crate::extractor::{PathExtractor, QueryStringExtractor};
use crate::handler::assets::{DirHandler, FileHandler, FileOptions, FilePathExtractor};
use crate::handler::{Handler, HandlerResult, NewHandler};
use crate::handler::{Handler, HandlerFuture, HandlerResult, NewHandler};
use crate::pipeline::chain::PipelineHandleChain;
use crate::router::builder::{
ExtendRouteMatcher, ReplacePathExtractor, ReplaceQueryStringExtractor, SingleRouteBuilder,
Expand Down Expand Up @@ -127,9 +127,9 @@ pub trait DefineSingleRoute {
/// # use gotham::middleware::session::NewSessionMiddleware;
/// # use gotham::test::TestServer;
/// #
/// async fn my_handler(state: State) -> HandlerResult {
/// async fn my_handler(state: &mut State) -> HandlerResult {
/// // Handler implementation elided.
/// # Ok((state, Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap()))
/// # Ok(Response::builder().status(StatusCode::ACCEPTED).body(Body::empty()).unwrap())
/// }
/// #
/// # fn router() -> Router {
Expand All @@ -152,13 +152,24 @@ pub trait DefineSingleRoute {
/// # assert_eq!(response.status(), StatusCode::ACCEPTED);
/// # }
/// ```
fn to_async<H, Fut>(self, handler: H)
fn to_async<'s, H, Fut>(self, handler: H)
where
Self: Sized,
H: (FnOnce(State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static,
Fut: Future<Output = HandlerResult> + Send + 'static,
H: (FnOnce(&'_ mut State) -> Fut) + RefUnwindSafe + Copy + Send + Sync + 'static,
Fut: Future<Output = HandlerResult> + Send,
Copy link
Owner Author

@alsuren alsuren May 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that you can't tie the lifetime of State to that of Fut, because it has no lifetime parameters, and therefore no way to specify the required higher-ranked lifetime bounds ( https://doc.rust-lang.org/reference/trait-bounds.html#higher-ranked-trait-bounds ). If I manage to work-around this issue then it should fix gotham-rs#431 . I am going to chase this up in the upstream rust repo here:
rust-lang/rust#45994

If we manage to fix this, I will also have a good solution to the ergonomics problems in tag1consulting/goose#22

There is a slightly more self-contained exploration of this issue here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=63830174a65ea4adf45eb5b095f0ae9b

{
self.to_new_handler(move || Ok(move |s: State| handler(s).boxed()))
self.to_new_handler(move || {
Ok(move |mut state: State| {
async move {
let res = { handler(&mut state).await };
match res {
Ok(r) => Ok((state, r)),
Err(e) => Err((state, e)),
}
}
.boxed()
})
})
}
/// Directs the route to the given `NewHandler`. This gives more control over how `Handler`
/// values are constructed.
Expand Down