Cannot trigger Outcome::Failure in FromRequest implementation - rust

While trying to get started with development of an api using rocket, I was implementing a request guard which is supposed to check authorization headers. When the check fails it should result in a Failure, but that is where I cannot get it to work. Outcome::Success works perfectly fine and returns the correct object, but when triggering an Outcome::Failure I always run into the issue that I cannot get it to compile:
error[E0282]: type annotations needed
--> src/main.rs:43:21
|
43 | Outcome::Failure((Status::BadRequest, RequestError::ParseError));
| ^^^^^^^^^^^^^^^^ cannot infer type for type parameter S declared on the enum Outcome
To Reproduce
main.rs
#[macro_use] extern crate rocket;
use rocket::Request;
use rocket::request::{FromRequest, Outcome};
use rocket::http::Status;
use regex::Regex;
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[get("/test")]
fn test(device: Device) -> &'static str {
"Hello test"
}
#[derive(Debug)]
enum RequestError {
InvalidCredentials,
ParseError,
}
struct Device {
id: i32
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for Device {
type Error = RequestError;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
Outcome::Failure((Status::BadRequest, RequestError::ParseError));
// TEST
let d1 = Device {
id: 123
};
Outcome::Success(d1)
}
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index,test])
}
Cargo.toml
[package]
name = "api-sandbox"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "0.5.0-rc.1"
regex = "1"
Expected Behavior
The parameter S should not needed to be declared, as I am not using the parameter Success(S), but use Failure(E). According to the docs I can either return an error or an tuple with Status and Error, but instead the error message pops up. I have double checked with only available resources and blogs, but was not able to correctly trigger an outcome with status failure.
Environment:
VERSION="20.04.3 LTS (Focal Fossa)"
5.10.60.1-microsoft-standard-WSL2
Rocket 0.5.0-rc.1
I am quite new to this topic, so please let me know, if I need to supply more information. Thanks for your assistance in this topic

The type of Outcome::Failure(_) cannot be deduced. Even if not used in this particular construction, the type parameter S must be known for it to be a complete type. There is no default type available or any context that can help infer the type of S.
The same is true of Outcome::Success(_). By itself, the type of the template parameter E is not known. However, this compiles because it does have context that can help the compiler infer it. It is used as a return value, so it must match the return type, and therefore it can be deduced that E is Self::Error.
This will also work for Outcome::Failure(_) if it were used as a return value:
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
Outcome::Failure((Status::BadRequest, RequestError::ParseError))
}

Related

How to make OwningRef work with iterators?

I have a RwLock protected global WORLD, and I want to write a function that read locks it and returns an iterator (of type Neighbors) that iterates over edges in a petgraph::stable_graph::StableGraph that is stored inside the global. I'm using OwningRef to deal with keeping the read lock guard alive after the function exits, which has worked for me in the past when just returning a field of World directly. I've included a compilable example and the error I'm getting below -- it seems there is some sort of type problem but I haven't been able to figure it out. I think it might have to do with OwningRef wanting to deal with a reference rather than an object containing a reference (Neighbors) but I'm not sure how to work around that.
Cargo.toml:
[package]
name = "problem_demo"
version = "0.1.0"
authors = ["Joseph Garvin <joseph.h.garvin#gmail.com>"]
edition = "2018"
[dependencies]
owning_ref="0.4.1"
once_cell="1.4.0"
petgraph={version="0.5.1", features=["serde-1"]}
main.rs:
use std::{sync::RwLock};
use once_cell::sync::OnceCell;
use owning_ref::RwLockReadGuardRef;
use petgraph::stable_graph::{StableGraph, Neighbors};
struct Bar {
data: i32
}
struct World {
graph: StableGraph<(), Bar, petgraph::Directed, u32>
}
pub static WORLD: OnceCell<RwLock<World>> = OnceCell::new();
fn neighbors(id: u32) -> Result<RwLockReadGuardRef<'static, World, Neighbors<'static, Bar, u32>>, Box<dyn std::error::Error>> {
RwLockReadGuardRef::new(WORLD.get().unwrap().read().unwrap())
.try_map(
|world: &World| -> std::result::Result<Neighbors<'static, Bar, u32>, Box<dyn std::error::Error>>
{
let neighbors = world.graph.neighbors_directed(
petgraph::graph::NodeIndex::new(id as usize),
petgraph::Direction::Outgoing
);
Ok(neighbors)
}
)
}
Errors:
error[E0271]: type mismatch resolving `for<'r> <[closure#src/main.rs:21:13: 29:14 id:_] as std::ops::FnOnce<(&'r World,)>>::Output == std::result::Result<&'r _, _>`
--> src/main.rs:20:10
|
20 | .try_map(
| ^^^^^^^ expected struct `petgraph::stable_graph::Neighbors`, found reference
|
= note: expected enum `std::result::Result<petgraph::stable_graph::Neighbors<'static, Bar>, std::boxed::Box<dyn std::error::Error>>`
found enum `std::result::Result<&_, _>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.

How can I create hygienic identifiers in code generated by procedural macros?

When writing a declarative (macro_rules!) macro, we automatically get macro hygiene. In this example, I declare a variable named f in the macro and pass in an identifier f which becomes a local variable:
macro_rules! decl_example {
($tname:ident, $mname:ident, ($($fstr:tt),*)) => {
impl std::fmt::Display for $tname {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { $mname } = self;
write!(f, $($fstr),*)
}
}
}
}
struct Foo {
f: String,
}
decl_example!(Foo, f, ("I am a Foo: {}", f));
fn main() {
let f = Foo {
f: "with a member named `f`".into(),
};
println!("{}", f);
}
This code compiles, but if you look at the partially-expanded code, you can see that there's an apparent conflict:
impl std::fmt::Display for Foo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { f } = self;
write!(f, "I am a Foo: {}", f)
}
}
I am writing the equivalent of this declarative macro as a procedural macro, but do not know how to avoid potential name conflicts between the user-provided identifiers and identifiers created by my macro. As far as I can see, the generated code has no notion of hygiene and is just a string:
src/main.rs
use my_derive::MyDerive;
#[derive(MyDerive)]
#[my_derive(f)]
struct Foo {
f: String,
}
fn main() {
let f = Foo {
f: "with a member named `f`".into(),
};
println!("{}", f);
}
Cargo.toml
[package]
name = "example"
version = "0.1.0"
edition = "2018"
[dependencies]
my_derive = { path = "my_derive" }
my_derive/src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Meta, NestedMeta};
#[proc_macro_derive(MyDerive, attributes(my_derive))]
pub fn my_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let attr = input.attrs.into_iter().filter(|a| a.path.is_ident("my_derive")).next().expect("No name passed");
let meta = attr.parse_meta().expect("Unknown attribute format");
let meta = match meta {
Meta::List(ml) => ml,
_ => panic!("Invalid attribute format"),
};
let meta = meta.nested.first().expect("Must have one path");
let meta = match meta {
NestedMeta::Meta(Meta::Path(p)) => p,
_ => panic!("Invalid nested attribute format"),
};
let field_name = meta.get_ident().expect("Not an ident");
let expanded = quote! {
impl std::fmt::Display for #name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self { #field_name } = self;
write!(f, "I am a Foo: {}", #field_name)
}
}
};
TokenStream::from(expanded)
}
my_derive/Cargo.toml
[package]
name = "my_derive"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "1.0.13"
quote = "1.0.2"
proc-macro2 = "1.0.7"
With Rust 1.40, this produces the compiler error:
error[E0599]: no method named `write_fmt` found for type `&std::string::String` in the current scope
--> src/main.rs:3:10
|
3 | #[derive(MyDerive)]
| ^^^^^^^^ method not found in `&std::string::String`
|
= help: items from traits can only be used if the trait is in scope
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
1 | use std::fmt::Write;
|
What techniques exist to namespace my identifiers from identifiers outside of my control?
Summary: you can't yet use hygienic identifiers with proc macros on stable Rust. Your best bet is to use a particularly ugly name such as __your_crate_your_name.
You are creating identifiers (in particular, f) by using quote!. This is certainly convenient, but it's just a helper around the actual proc macro API the compiler offers. So let's take a look at that API to see how we can create identifiers! In the end we need a TokenStream, as that's what our proc macro returns. How can we construct such a token stream?
We can parse it from a string, e.g. "let f = 3;".parse::<TokenStream>(). But this was basically an early solution and is discouraged now. In any case, all identifiers created this way behave in a non-hygienic manner, so this won't solve your problem.
The second way (which quote! uses under the hood) is to create a TokenStream manually by creating a bunch of TokenTrees. One kind of TokenTree is an Ident (identifier). We can create an Ident via new:
fn new(string: &str, span: Span) -> Ident
The string parameter is self explanatory, but the span parameter is the interesting part! A Span stores the location of something in the source code and is usually used for error reporting (in order for rustc to point to the misspelled variable name, for example). But in the Rust compiler, spans carry more than location information: the kind of hygiene! We can see two constructor functions for Span:
fn call_site() -> Span: creates a span with call site hygiene. This is what you call "unhygienic" and is equivalent to "copy and pasting". If two identifiers have the same string, they will collide or shadow each other.
fn def_site() -> Span: this is what you are after. Technically called definition site hygiene, this is what you call "hygienic". The identifiers you define and the ones of your user live in different universes and won't ever collide. As you can see in the docs, this method is still unstable and thus only usable on a nightly compiler. Bummer!
There are no really great workarounds. The obvious one is to use a really ugly name like __your_crate_some_variable. To make it a bit easier for you, you can create that identifier once and use it within quote! (slightly better solution here):
let ugly_name = quote! { __your_crate_some_variable };
quote! {
let #ugly_name = 3;
println!("{}", #ugly_name);
}
Sometimes you can even search through all identifiers of the user that could collide with yours and then simply algorithmically chose an identifier that does not collide. This is actually what we did for auto_impl, with a fallback super ugly name. This was mainly to improve the generated documentation from having super ugly names in it.
Apart from that, I'm afraid you cannot really do anything.
You can thanks to a UUID:
fn generate_unique_ident(prefix: &str) -> Ident {
let uuid = uuid::Uuid::new_v4();
let ident = format!("{}_{}", prefix, uuid).replace('-', "_");
Ident::new(&ident, Span::call_site())
}

The example from the "chaining computations" section of the Tokio docs does not compile: "expected struct `std::io::Error`, found ()"

I am learning Tokio. I read Getting asynchronous from the official docs, but the source code from the Chaining computations section cannot be compiled under the latest Rust version (Rust 2018, v1.31):
extern crate tokio;
extern crate bytes;
#[macro_use]
extern crate futures;
use tokio::io::AsyncWrite;
use tokio::net::{TcpStream, tcp::ConnectFuture};
use bytes::{Bytes, Buf};
use futures::{Future, Async, Poll};
use std::io::{self, Cursor};
// HelloWorld has two states, namely waiting to connect to the socket
// and already connected to the socket
enum HelloWorld {
Connecting(ConnectFuture),
Connected(TcpStream, Cursor<Bytes>),
}
impl Future for HelloWorld {
type Item = ();
type Error = io::Error;
fn poll(&mut self) -> Poll<(), io::Error> {
use self::HelloWorld::*;
loop {
let socket = match *self {
Connecting(ref mut f) => {
try_ready!(f.poll())
}
Connected(ref mut socket, ref mut data) => {
// Keep trying to write the buffer to the socket as long as the
// buffer has more bytes it available for consumption
while data.has_remaining() {
try_ready!(socket.write_buf(data));
}
return Ok(Async::Ready(()));
}
};
let data = Cursor::new(Bytes::from_static(b"hello world"));
*self = Connected(socket, data);
}
}
}
fn main() {
let addr = "127.0.0.1:1234".parse().unwrap();
let connect_future = TcpStream::connect(&addr);
let hello_world = HelloWorld::Connecting(connect_future);
// Run it
tokio::run(hello_world)
}
The compiler outputs error messages:
error[E0271]: type mismatch resolving `<HelloWorld as futures::Future>::Error == ()`
--> src\main.rs:54:5
|
54 | tokio::run(hello_world)
| ^^^^^^^^^^ expected struct `std::io::Error`, found ()
|
= note: expected type `std::io::Error`
found type `()`
= note: required by `tokio::run`
Is the problem caused by the version of my Rust compiler? How do I fix it?
Tokio::run has the following signature:
pub fn run<F>(future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
which means that it accepts a Future which accepts a () as Item and has () as the error type.
On the other hand, your HelloWorld impl has
type Item = ();
type Error = io::Error;
which means they are not compatible, you have to convert the io::Error to () somehow.
I would suggest using map_err
tokio::run(hello_world.map_err(|e| Err(e).unwrap()))
to handle the error in case something bad happens. You could of course make better error handling, but this will work.
Interestingly enough, I have JavaScript disabled in my browser and therefore I see the comments in the Rustdoc on the webpage:
fn main() {
let addr = "127.0.0.1:1234".parse().unwrap();
let connect_future = TcpStream::connect(&addr);
let hello_world = HelloWorld::Connecting(connect_future);
# let hello_world = futures::future::ok::<(), ()>(());
// Run it
tokio::run(hello_world)
}
The # means that Rustdoc should not print that line, but should execute it while testing. I think this is a mistake/oversight and there is also an open issue and a fix pending. PR has been merged, webpage has been updated.

What does the Rust error code E0495 mean?

I'm using Rocket to make a web server and I'm trying to make a wrapper around the Responder trait so that my route methods can return any struct.
The code below does not compile because of an error about lifetimes that I don't fully understand. The error is not listed in the error index; it skips from E0492 to E0496.
Since this code uses Rocket, it requires the nightly compiler.
main.rs
#![feature(custom_attribute, proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
extern crate rocket_contrib;
use rocket::{http::Status, response::Responder, Request};
use rocket_contrib::templates::Template;
fn main() {
rocket::Rocket::ignite().mount("/", routes![route]).launch();
}
#[get("/")]
fn route<'a>() -> DynamicResponder<'a> {
DynamicResponder::from(Template::render("template", ()))
}
struct DynamicResponder<'a> {
inner: Box<dyn Responder<'a> + 'a>,
}
impl<'r> DynamicResponder<'r> {
pub fn from<T: 'r>(responder: T) -> DynamicResponder<'r>
where
T: Responder<'r>,
{
DynamicResponder {
inner: Box::new(responder),
}
}
}
impl<'r> Responder<'r> for DynamicResponder<'r> {
fn respond_to<'b>(
self,
request: &'b Request,
) -> Result<rocket::response::Response<'r>, Status> {
self.inner.respond_to(request)
}
}
Cargo.toml
[package]
name = "rocketing_around"
version = "0.1.0"
[dependencies]
rocket = "0.4.0"
[dependencies.rocket_contrib]
version = "0.4.0"
default_features = false
features = [ "handlebars_templates" ]
Compiler message:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'r` due to conflicting requirements
--> src/main.rs:15:5
|
15 | DynamicResponder::from(Template::render("template", ()))
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 14:10...
--> src/main.rs:14:10
|
14 | fn route<'a>() -> DynamicResponder<'a> {
| ^^
= note: ...so that the expression is assignable:
expected DynamicResponder<'a>
found DynamicResponder<'_>
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
expected rocket::response::Responder<'_>
found rocket::response::Responder<'static>
What does the Rust error code E0495 mean?
The error code E0495 seems to be a catch-all for a variety of different situations where lifetime requirements can't be reconciled. The message already says that, and there are huge number of ways that you could write code where lifetimes don't match up properly, which is perhaps why it isn't listed with examples in the error index.
Type parameters, including lifetimes, are always determined by the caller. Looking at your particular example, a function signature like this:
fn route<'a>() -> DynamicResponder<'a> { ... }
means that, for any lifetime 'a chosen by the caller, references inside the returned DynamicResponder<'a> must be valid. But what could the references inside DynamicResponder<'a> even be in this situation? They can't be references to variables in the function body because those only live as long as the function. There are no arguments, so the only things that could be referenced by the DynamicResponder<'a> are things that live outside the function, i.e. statics.
You can fix the error by removing the lifetime variable and setting the lifetime parameter to the only lifetime that makes sense:
fn route() -> DynamicResponder<'static> { ... }

Creating a hyper service with custom error type

I am trying to create a REST server using hyper. For robust error handling, I would prefer to have the service return a future with a custom error type that wraps hyper, Diesel, and other errors. Unfortunately, hyper::Response seems to hard-code a stream with error type hyper::error::Error, which conflicts with the error type I've defined for my service. I see a couple possible solutions:
Make my service return my custom error type by modifying hyper::Response, which seems hard.
Wrap non-hyper errors in a hyper::error::Error. This seems hacky.
Something else. It seems like I'm missing the "right" way to do this.
The following code shows what I think I want to do:
extern crate diesel;
extern crate futures;
extern crate hyper;
use futures::future::{ok, Future};
use hyper::StatusCode;
use hyper::server::{Request, Response, Service};
fn main() {
let address = "127.0.0.1:8080".parse().unwrap();
let server = hyper::server::Http::new()
.bind(&address, move || Ok(ApiService {}))
.unwrap();
server.run().unwrap();
}
pub struct ApiService;
impl Service for ApiService {
type Request = Request;
type Response = Response;
type Error = Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, request: Request) -> Self::Future {
Box::new(ok(Response::new().with_status(StatusCode::Ok)))
}
}
#[derive(Debug)]
pub enum Error {
Request(hyper::Error),
DatabaseResult(diesel::result::Error),
DatabaseConnection(diesel::ConnectionError),
Other(String),
}
// omitted impl of Display, std::error::Error for brevity
This code results in a compiler error which I believe is because the bind function requires that the response type have a body that is a stream with error type hyper::error::Error:
error[E0271]: type mismatch resolving `<ApiService as hyper::client::Service>::Error == hyper::Error`
--> src/main.rs:14:10
|
14 | .bind(&address, move || Ok(ApiService {}))
| ^^^^ expected enum `Error`, found enum `hyper::Error`
|
= note: expected type `Error`
found type `hyper::Error`
Because the ultimate goal of the server is to return a response to the user, I found an acceptable solution to be to create a finalize function that converts errors encountered while processing a request into correctly formed responses and treats those errors as non-errors from hyper's perspective. I will need to flesh this idea out some (e.g. By passing along hyper errors as errors), but I believe the basic idea is sound.
The following code modifies the code in the question to do this:
extern crate diesel;
extern crate futures;
extern crate hyper;
#[macro_use]
extern crate serde_derive;
use futures::future::{ok, Future};
use hyper::StatusCode;
use hyper::server::{Request, Response, Service};
fn main() {
let address = "127.0.0.1:8080".parse().unwrap();
let server = hyper::server::Http::new()
.bind(&address, move || Ok(ApiService {}))
.unwrap();
server.run().unwrap();
}
fn finalize(result: Result<Response, Error>) -> FutureResult<Response, hyper::Error> {
match result {
Ok(response) => ok(response),
Err(error) => {
let response_body =
json!({"status": 500, "description": error.description()}).to_string();
ok(Response::new()
.with_status(StatusCode::InternalServerError)
.with_header(ContentLength(response_body.len() as u64))
.with_body(response_body))
}
}
}
pub struct ApiService;
impl Service for ApiService {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
fn call(&self, request: Request) -> Self::Future {
let response = Ok(Response::new().with_status(StatusCode::Ok));
Box::new(finalize(response))
}
}
#[derive(Debug)]
pub enum Error {
Request(hyper::Error),
DatabaseResult(diesel::result::Error),
DatabaseConnection(diesel::ConnectionError),
Other(String),
}
// omitted impl of Display, std::error::Error for brevity
You can implement the std::convert::From trait for your Error type. E.g. for the hyper::Error case:
impl From<hyper::Error> for Error {
fn from(error: hyper::Error) -> Self {
Error::Request(error)
}
}

Resources