Please consider implementing Responder for Box<T> where T: Responder.
This would allow a function to easily return multiple types of Response.
I've tried doing this with a function that returns impl Responder, but that gives me an error if I try to return multiple types that each implement Responder.
I'm thinking of a function that would potentially return e.g. a Template in one case, and maybe a redirect or a flash in another case.
I've kind of managed to hack around this in one case by returning Result<Flash<Redirect>, Template>, but that feels wrong because there are 2 error conditions - one error case redirects (as does the success case), while the other error case returns a template.
You may argue that this is the wrong way of doing it, but having the ability to do this would really help me reuse more of the code that I've already written.
I've not found any way to implement this or something similar (e.g. a wrapper type) that doesn't have too much code overhead, although if someone has an alternative approach here I'd be willing to look at that.
Below is the error message that I currently get when I define a function to return Box<Response>:
error[E0277]: the trait bound `std::boxed::Box<rocket::response::Responder<'static>>: rocket::response::Responder<'_>` is not satisfied
--> src/controllers/period.rs:67:1
|
67 | #[post("/<id>/edit", data = "<period_form>")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `rocket::response::Responder<'_>` is not implemented for `std::boxed::Box<rocket::response::Responder<'static>>`
|
= note: required by `rocket::handler::<impl rocket::Outcome<rocket::Response<'r>, rocket::http::Status, rocket::Data>>::from`
You can already use a Result for this, and we might imagine Rocket having an Either type (or using the existing one) to alleviate the implications of returning a Result.
For more than two possible result types, Rocket advocates for using a custom enum. In 0.4, you'll be able to derive Responder, making this even easier:
#[derive(Responder)]
enum MyResponse {
A(Template),
B(Redirect),
C(Error)
}
I am unsure if we should implement this (hence my accept/not accept). A Box<Responder> has no meaning whatsoever; it can be anything. Rocket advocates for types having meaning. This is why returning a Response is frowned upon and may, in fact, be made impossible in future versions of Rocket. I fear that making this possible will simultaneously make Rocket code harder to follow and lead to users Boxing things unnecessarily.
You can already use a Result for this, and we might imagine Rocket having an Either type (or using the existing one) to alleviate the implications of returning a Result.
Could you demonstrate that how we can return multiple error types with Result?
For more than two possible result types, Rocket advocates for using a custom enum. In 0.4, you'll be able to derive Responder, making this even easier:
This is really cool. Looking forward to 0.4 release.
@weiwei-lin Nest them: Result<T, Result<E1, E2>>. You can do this as many times as you want: Result<T, Result<E1, Result<E2, Result<E3, E4>>>>.
[15:04:16] occultus imo using a custom enum makes it clearer what the different response conditions are and what kind of response goes with each
[15:04:49] jkcclemens it also adds a lot of unnecessary cruft
[15:06:09] jkcclemens if I want to change the status code, I need to wrap my return type in Custom. my return type is JSON, so wrap it in Json. now, if I want to return multiple items, wrap it in an enum
[15:07:01] jkcclemens or, I could `return box` something that is concise and not wrapped several times, most likely hidden behind other methods for ease of use
[15:14:20] jkcclemens basically, when I'm already returning `Result<Custom<Json<CustomWrappers<T>>>>`, telling me to add more shit to my return type (which adds more cruft, which requires me to go and wrap all my returns in MORE crap, even if I just want to have one custom return code) is a big turn-off.
In the end, this makes it more difficult to use Rocket and makes code ugly and harder to maintain.
```rust
fn example() -> Result
let my_struct = unimplemented!();
// great!
Ok(Custom(Status::BadRequest, Json(my_struct)))
// but what if I also need to return just plain text sometimes?
// oh, I guess I have to wrap this already-cumbersome expression AND return type in
// yet another wrapper, in addition to create a new enum to represent this
// or...
let my_string = unimplemented!();
box my_string
// versus
Ok(Custom(Status::Ok, TextOrJson::Json(Json(my_string))))
// sure you could do some magic to get rid of that last Json, but come on
// and I have to do this for every unique return type in my application?
// 馃槩
}
Closing in favor of #590.
This is not a fix.
There is nothing to fix here; there is no functionality that Rocket is preventing here. The argument to make is one of convenience, which you've stated. My counter-argument is that if we approach #590 the correct way, then solutions are just as convenient while simultaneously not having the same readability, performance, and safety drawbacks I mention in my previous comment.
What is your real-world use-case in which you feel a custom enum won't suffice? All of the previous cases you've mentioned can be contained in a single enum, though it's unclear what you're actually trying to achieve here. But as an example, if you want to be able to return a Template or Redirect on failure, as well as Template, String, or Json on success, you can have the following enum:
#[derive(Responder)]
enum Response {
#[response(header = "<1>")]
Page(Template, Status),
Static(String),
Schema(Json),
Unknown(Redirect),
}
#[get("/")]
fn example() -> Response { }
A better approach would be to split the success and error cases:
#[derive(Responder)]
enum Success {
Page(Template),
Static(String),
Schema(Json),
}
#[derive(Responder)]
enum Error {
Page(Template),
Other(Redirect),
}
#[get("/")]
fn example() -> Result<Success, Error> { }
This is probably an extreme example; this much variance is pretty unlikely in practice. At most, I've had three different variants that I've needed to capture in a single handler.