Returning currying FnOnce


#1

Hi all,

I’m trying to “curry” a function to pass to map_err:

do_something()
  .map_err(HandlerFunc::wrap("My message"))

This is the wrap() definition:

impl HandlerError {
  pub fn wrap<E>(msg: &str) -> Box<FnOnce(E) -> HandlerError>
    where E: Error + 'static,
  {
    let msg = msg.to_owned();
    Box::new(move |e: E| {
      HandlerError {
        msg: msg,
        cause: Some(Box::new(e))
      }
    })
  }
}

This is the error I’m getting:

error[E0277]: the trait bound `std::boxed::Box<std::ops::FnOnce(_) -> errors::HandlerError>: std::ops::FnOnce<(std::io::Error,)>` is not satisfied
  --> src/errors.rs:89:6
   |
89 |     .map_err(HandlerError::wrap("Some error"))
   |      ^^^^^^^ the trait `std::ops::FnOnce<(std::io::Error,)>` is not implemented for `std::boxed::Box<std::ops::FnOnce(_) -> errors::HandlerError>`
   |
   = help: the following implementations were found:
             <std::boxed::Box<std::boxed::FnBox<A, Output=R> + 'a> as std::ops::FnOnce<A>>
             <std::boxed::Box<std::boxed::FnBox<A, Output=R> + std::marker::Send + 'a> as std::ops::FnOnce<A>>

FnBox is nightly-only and I’m on stable.

I don’t understand how I can make the compiler happy.

Suggestions?
Thanks for any help.


#2

I don’t think this is possible because a FnOnce() consumes the closure, meaning you’d get some sort of move out of box error. You can implement this sort of currying with impl Trait, although that’s not stable either.

What’s wrong with using a normal closure?


#3

Thanks for your answer.

HandlerError is an error returned by the handle() function in a Handler trait my library is exporting:

pub trait Handler {
	fn handle(&self, data: &HandlerData) -> Result<bool, HandlerError>;
}

I wanted to make it opaque by not forcing whoever implements my trait to build the error themselves, so that I can add fields or behaviour to the error in the future without forcing all the software using the crate to update their code.

Returning a builder closure instead of a builder struct looked like a much simpler solution both for me and the users.


#4

Using a macro possibly what you want to try for current stable.


#5

You can achieve this goal by not exposing the internals of the HandlerError in the public API. It would provide constructor functions and/or implement conversion traits (eg From) that callers can use to make values.


#6

Yes, that was my idea, but I wanted to record the (optional) cause.

I already have a new(msg: &str) method on it.

I can expose a with_error(e: Error, msg: &str) and call it like map_err(|e| HandlerError::with_error(e, "message"), but I wanted to simplify it further by currying it.

My first try was implementing Into, but it doesn’t allow me to add a message.

It’s not a tragedy if it’s not possible, but the pattern could have been useful for other things too. :slight_smile:

It’ll have to wait until impl Trait is stable, then.

Thanks everyone!


#7

You can impl<E, S: Into<String>> From<(E, S)> for HandlerError (or something like that) if you want to shorten the callers up a bit. Personally, I’d find the currying or this approach overkill here :slight_smile:.


#8

A quick update to say that this now works beautifully with Rust 1.26:

impl HandlerError {
	pub fn wrap<E>(msg: &str) -> impl FnOnce(E) -> HandlerError
		where E: Error + 'static,
	{
		let msg = msg.to_owned();
		move |e: E| {
			HandlerError {
				msg: msg,
				cause: Some(Box::new(e))
			}
		}
	}
}