Rust ? operator does not work with Into<T> trait - rust

I was writting a function to flat a Result. This is what I got.
type BoxDyn = Box<dyn Error + Send + Sync>;
fn flat_result<U, T, G>(r: Result<Result<U, G>, T>) -> Result<U, BoxDyn>
where
T: Into<BoxDyn>,
G: Into<BoxDyn>,
{
match r {
Err(x) => Err(x.into()),
Ok(x) => match x {
Err(x) => Err(x.into()),
Ok(x) => Ok(x),
},
}
}
It works fine. But I should be able to write it using two ? like this.
type BoxDyn = Box<dyn Error + Send + Sync>;
fn flat_result<U, T, G>(r: Result<Result<U, G>, T>) -> Result<U, BoxDyn>
where
T: Into<BoxDyn>,
G: Into<BoxDyn>,
{
Ok(r??)
}
When I try this, I get this error:
`G` cannot be shared between threads safely
required because of the requirements on the impl of `From<G>` for `Box<dyn std::error::Error + Send + Sync>`
required because of the requirements on the impl of `FromResidual<Result<Infallible, G>>` for `Result<U, Box<dyn std::error::Error + Send + Sync>>`
But it seems to me that it should not happen because the documentation says that the ? is equivalent to a match expression, where the Err(err) branch expands to an early return Err(From::from(err)). And also From<T> for U implies Into<U> for T. So it would not matter What G or T are, if they implement Into it should work. What is the problem?

You are right that From implies Into but not that into implies From. And remember that ? uses From, not Into.
As it happens there is this impl in the standard library that gets in the way:
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a>
Since your BoxDyn alias is exactly that with 'a = 'static, you have an implementation for From<E>. But for that to work your error types T and G has to satisfy Error + Send + Sync + 'a. But your only constraints are that they are Into<Error> that is actually unrelated to what is needed. Remember that From implies Into but Into does not imply From.
It works if you write instead:
fn flat_result<U, T, G>(r: Result<Result<U, G>, T>) -> Result<U, BoxDyn>
where
T: Error + Send + Sync + 'static,
G: Error + Send + Sync + 'static,
{
Ok(r??)
}
But I think that the proper way to write that would be to require the From trait:
fn flat_result<U, T, G>(r: Result<Result<U, G>, T>) -> Result<U, BoxDyn>
where
BoxDyn: From<T> + From<G>,
{
Ok(r??)
}
Why does it work with the manual implementation? Well, because you are using Into instead of From of course. Change it to BoxDyn::from(x) and it will fail just the same.

Related

How to use an unnamed type implementing a trait in a closure's return type?

Closures are unnamed types in rust, so you have to use impl Fn to return one. Is it possible to also use an unnamed type for the closure's return type?
This is what I'm trying to do:
use actix_web::{services, web, Resource, dev::HttpServiceFactory};
pub fn service_factory() -> impl Fn() -> (impl HttpServiceFactory + 'static) + Clone + Send + 'static {
|| services![web::resource("/a"), web::resource("/b")]
}
But the compiler disallows (impl HttpServiceFactory + 'static) and tells me that impl Trait is only usable in function return values, so I'm instead forced to do this:
use actix_web::{services, web, Resource};
pub fn service_factory() -> impl Fn() -> (Resource, Resource) + Clone + Send + 'static {
|| services![web::resource("/a"), web::resource("/b")]
}
which works, but is a pain to use; also, if the type I'm returning from the closure was truly unnamed (such as another closure), this workaround wouldn't be possible at all.
What's the idiomatic way to write this code?
I should also mention that Box<dyn HttpServiceFactory + 'static> doesn't work for me because some function somewhere doesn't expect boxed trait objects, so the type has to be known.
You can make a helper trait "alias" with an associated type:
trait HttpContextFactoryFn: Fn() -> Self::Factory {
type Factory: HttpContextFactory + 'static;
}
impl<F, T> HttpContextFactoryFn for F
where
F: ?Sized + Fn() -> T,
T: HttpContextFactory + 'static,
{
type Factory = T;
}
This trait is equvialent to Fn() -> (impl HttpContextFactory + 'static), except it's one that can actually be expressed in Rust today. That means that you can use this in your function signature:
pub fn service_factory() -> impl HttpContextFactoryFn + Clone + Send + 'static {
|| services![web::resource("/a"), web::resource("/b")]
}

One type (function pointer) is more general than the other

I reached this error in this example, and I'm trying to understand what's happening.
use futures::future::{BoxFuture, FutureExt};
use std::sync::Arc;
use futures::lock::Mutex;
struct Message {}
struct Client {}
enum Error {
ConnectionClosed,
}
#[derive(Default)]
struct MyType{}
impl Client {
fn send_and_expect<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> BoxFuture<'a, Result<R, Error>> {
async move { Ok(R::default()) }.boxed()
}
pub fn connection_retrier<'a, R: 'a + Default + Send>(
client: Arc<Mutex<Self>>,
f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,
f_m: &'a Message,
) -> BoxFuture<'a, Result<R, Error>>
{
async move {Ok(R::default())}.boxed()
}
async fn send_with_retry<'a>(
client: Arc<Mutex<Self>>,
message: &'a Message,
) -> Result<MyType, Error> {
let client = Arc::new(Mutex::new(Client{}));
Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
.await
}
}
Error:
error[E0308]: mismatched types
--> src/lib.rs:36:52
|
36 | Self::connection_retrier::<MyType>(client, Self::send_and_expect, message)
| ^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'b> fn(Arc<_>, &'b Message) -> Pin<Box<(dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send + 'b)>>`
found fn pointer `fn(Arc<_>, &Message) -> Pin<Box<dyn futures::Future<Output = std::result::Result<MyType, Error>> + std::marker::Send>>`
Playground
Well, connection_retrier requres a function f: for<'b> fn(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<R, Error>>,. The one I passed seems to fit the signature.
The way I view for<'b> fn... is: I accept a function f for any lifetime 'b presented. So, for the particular lifetime 'a, send_and_expect seems to fit everything.
The only suspicion I have is for the lifetime of R being fixed at 'a in connection_retrier so the f with its for<'b> is not free to give any lifetime for its R. I think the lifetime of the R in f should be 'b. Is it possible to force f to have R with lifetime 'b?
Something like this:
f: for<'b> fn<RR: 'b>(Arc<Mutex<Self>>, &'b Message) -> BoxFuture<'b, Result<RR, Error>>,
This error looks to be caused by limitation of the compiler's ability to automatically convert generic functions which include a type bounded by a lifetime into a function pointer that is fully generic over that lifetime. I'm not quite sure why this limitation exists, but it can be worked around by wrapping the generic call in a non-generic closure. All you need to do to make your example compile is to change:
Self::send_and_expect
to
|c, m| Self::send_and_expect(c, m)
Thus hiding the generic from the compiler.

Strange behavior when adding the Send trait to a boxed trait object

Here's an error struct:
#[derive(Debug)]
pub struct Error {
msg: &'static str,
//source: Option<Box<dyn std::error::Error>>, // old
source: Option<Box<dyn std::error::Error + Send>>, // new
}
impl Error {
fn new_caused<E>(msg: &'static str, err: E) -> Self
where
E: 'static + std::error::Error + Send,
{
Self {
msg: msg,
source: Some(Box::from(err)),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", self.msg) // HACK
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|err| err.as_ref())
}
}
fn main() {
let err = "this will fail".parse::<i32>().unwrap_err();
let err = Error::new_caused("some msg", err);
}
I decided to make it Sendable so I changed source: Option<Box<dyn std::error::Error>> to source: Option<Box<dyn std::error::Error + Send>> and strange things happened.
Magic #1
new_caused refused to compile any more:
error[E0277]: the trait bound `std::boxed::Box<dyn std::error::Error + std::marker::Send>: std::convert::From<E>` is not satisfied
--> src/main.rs:14:26
|
14 | source: Some(Box::from(err)),
| ^^^^^^^^^^^^^^ the trait `std::convert::From<E>` is not implemented for `std::boxed::Box<dyn std::error::Error + std::marker::Send>`
|
= note: required by `std::convert::From::from`
Changing Box::from to Box::new helped, even though their signatures seem identical and Box::from's implementation just calls Box::new.
Magic #2
source also became incorrect:
error[E0308]: mismatched types
--> src/main.rs:27:9
|
27 | self.source.as_ref().map(|err| err.as_ref())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `std::error::Error`, found trait `std::error::Error + std::marker::Send`
|
= note: expected enum `std::option::Option<&(dyn std::error::Error + 'static)>`
found enum `std::option::Option<&dyn std::error::Error + std::marker::Send>`
Why isn't the unused Send trait ignored like other ones?
Replacing that combinator logic with its manual version worked fine:
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.source {
Some(source) => Some(source.as_ref()),
None => None
}
}
Summary
What are the explanations for this "magic" and what are better methods of dealing with it?
For magic #1, it is because the standard library has these implementations:
impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a>
impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<dyn Error + Send + Sync + 'a>
There is no implementation for E: Error + Send without the Sync.
Easy solution #1: add Sync whereever there is a Send or use Box::new.
Magic #2 is more complicated: you have an std::option::Option<&dyn std::error::Error + Sync> where you need an Option<&dyn std::error::Error>. You know that a &(dyn std::error::Error + Send) is convertible to a &dyn std::error::Error so you expect that the Option<_> will be too, but those conversions are not transitive1, so it fails.
The difference between the map and the match is in the order of type deduction:
In the map case, the type of the closure is deduced as taking a Box<dyn std::error::Error + Sync>. Since it returns err.as_ref(), of type &dyn std::error::Error + Sync, that is the type that the closure returns. Then Option::map returns an Option<_> with the same type of the closure return type so you get a final Option<&dyn std::error::Error + Sync> and an error.
In the match code, when you write Some(source) => Some(source.as_ref()), source is deduced as type Box<dyn std::error::Error + Sync>, but the right side is deduced from the returned type Option<&dyn std::error::Error>, so the argument to Some is cast to that type: source.as_ref() is converted to the right type and it compiles.
I think the easiest way to write this example is to add a cast as _ inside the map, to instruct the compiler to deduce the type of the closure from the usage instead of from the inner code:
self.source.as_ref().map(|err| err.as_ref() as _)
If the code is more complex, as _ may not be feasible. Then a match is perfectly adequate.
Playground with the fixed code.
1: I think I read about making these conversions automatic (covariant on auto traits?) but I cannot find it anywhere...

How do I pass an async callback containing an argument that is a reference?

I have a function that requires an asynchronous callback (a request handler); I'm currently trying to accept things that look like this:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
It had been working up until the addition of the second parameter body, which is causing me grief. The function that accepts the parameter looks like this:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Part way through this function, we call a helper function:
handle_request(&mut connection, request_handler, request)
which has a very similar signature; in particular, the signature for request_handler is identical. It does some minor pre-processing before invoking request_handler. When I attempt to compile this, I get:
error[E0310]: the parameter type `H` may not live long enough
|
106 | pub async fn process_requests<H, F>(
| - help: consider adding an explicit lifetime bound `H: 'static`...
...
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
|
note: ...so that the type `H` will meet its required lifetime bounds
|
142 | handle_request(&mut connection, request_handler, request)
| ^^^^^^^^^^^^^^
Why do I need this / what do I do about this? Indeed adding 'static to H: in the where does seem to silence the error, but is that the right thing to do? Couldn't the type implementing H carry a reference, and 'static would forbid that? I don't necessarily want to do that, but any amount of trying to annotate a lifetime that isn't 'static onto H has not worked; e.g., 'a + Fn(...) -> F + 'a does not work, nor does adding a new generic 'b lifetime. I'd rather not 'static something that doesn't need it, but I don't see how to do that.
(I'm also a bit perplexed by the wording of the message — that the parameter type — not some argument or variable — doesn't live long enough. How does a type not live long enough?)
I've played with things a bit more, but I still can't get anything that actually compiles. This playground example shows another one of the more perplexing error messages that I'm running into. I've tried dropping some of the lifetime annotations, and moved the for<'a> bit (I'm not sure what the difference is?).
A callback argument passed as reference does not work with HRTB constraints when the callback is marked with the async keyword.
The signature using async/await:
async fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> HandlerResponse
Is equivalent to:
fn handle_request<'a>(request: Request, body: &'a mut (dyn AsyncRead + 'a)) -> Future<Output=HandlerResponse> + 'a
This implies that input lifetimes of an async function are captured in the
future returned by the async function.
See the paragraph "Lifetime capture in the anonymous future" of RFC 2394.
Declaring the function that accepts the parameter as:
pub async fn process_requests<H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
Give a compilation error because the HRTB requirement:
for<'a> H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a
"unlink" the lifetime bound from the caller and produce the compilation error
expected bound lifetime parameter 'a, found concrete lifetime
for more details about HRTB see here.
To make it works you have to write:
pub async fn process_requests<'a, H, F>(
mut connection: Box<dyn AsyncConnection>,
request_handler: &H,
) -> Result<(), DecodeError>
where
H: Fn(crate::Request, &'a mut (dyn AsyncRead + 'a)) -> F + 'a,
F: Future<Output = HandlerResponse>,
{
But this get you to another problem:
`body` does not live long enough
because the local body struct does not outlive request_handler:
async fn handle_request<'a, H, F>(
request_handler: &H,
request: Request,
) -> io::Result<()>
where
H: Fn(Request, &'a mut (dyn AsyncRead + 'a)) -> F,
F: Future<Output = String>,
{
let mut body = Body {};
request_handler(request, &mut body);
unimplemented!();
}
If feasible
one possible solution could be to use Boxed trait objects and get rid off HTRB constraints.

Failed to infer type when using Result::map and Box

Why won't this compile?
trait T {}
fn f<U: 'static + T, V, E>(f2: V) -> impl Fn() -> Result<Box<dyn T>, E>
where
V: Fn() -> Result<U, E>,
{
move || -> Result<Box<dyn T>, E> { f2().map(Box::new) }
}
The error message is:
error[E0308]: mismatched types
--> src/lib.rs:7:40
|
7 | move || -> Result<Box<dyn T>, E> { f2().map(Box::new) }
| ^^^^^^^^^^^^^^^^^^ expected trait T, found type parameter
|
= note: expected type `std::result::Result<std::boxed::Box<(dyn T + 'static)>, _>`
found type `std::result::Result<std::boxed::Box<U>, _>`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
This version is ok:
trait T {}
fn f<U: 'static + T, V, E>(f2: V) -> impl Fn() -> Result<Box<dyn T>, E>
where
V: Fn() -> Result<U, E>,
{
move || -> Result<Box<dyn T>, E> {
match f2() {
Ok(result) => Ok(Box::new(result)),
Err(e) => Err(e),
}
}
}
In my opinion, (dyn T + 'static) and U are the same; am I right?
I'm using rustc 1.39.0-nightly (f0b58fcf0 2019-09-11).
It's a limitation and I don't know if it will compile one day. The reason is that Rust doesn't know to convert between the two Result types, (dyn T + 'static) and U are totally different things. If this acceptable you can do f2().map(|x| Box::new(x) as _).
The cast will allow the compiler to transform U into (dyn T + 'static) before put it in the result, we don't need to explicit the cast type the compiler inference will do it for us (on most case).
A trait object can be obtained from a pointer to a concrete type that implements the trait by casting it (e.g. &x as &Foo)
See dynamic dispatch section of the book (didn't find any information in the new book).

Resources