tailhook/quick-error

Support access to std::fmt::Formatter inside display for more flexibility

Opened this issue · 4 comments

It would be nice to implement display via direct access to the std::fmt::Formatter - as another option. This would allow additional logic inside the display logic without temporary buffers.

Use case: Complex enum-variant with Option<...> fields.

At first I was thinking about closures, but this would require to capture all fields explicitly - not so nice. Instead I suggest we provide it like display(f, me) -> { /* ... */ }.

Suggested Grammar (example)

display(me, f) -> {
    try!(write!(f, "{} Expected token {:?} but found {:?} at {:?}.",
        me.description(), expected, found.token(), found.position()));

    // additional logic

    Ok(())
}

Current Workaround - needs redundant allocation for temporary buffer

display(me) -> ("{buf}", buf={
    use std::fmt::Write;
    let mut f = String::new()
    write!(f, "{} Expected token {:?} but found {:?} at {:?}.",
        me.description(), expected, found.token(), found.position()).unwrap();

    // additional logic

    f
}

EDIT:

  • changed argument order in display.

Well, at least display() should have the arguments in the right order (same as in trait).

But your examples do not demonstrate real purpose of the thing. There should be no additional logic in the display handler, except displaying the data.

Also I'm not inclined to add every feature that may ever be possible to do here. We need only about 80%, and I think we have already covered about 99% :)

May be we should have a way to opt-out the Display implementation here, so you can implement the trait yourself?

You are right about the argument order.

The purpose in this case would be to write some additional data only in some cases, like

if let Some(x) = reason {
    try!(write!(f, " {}", x));
}

At least that was my use case - I agree, it is not super important.

Also want this in two different cases:

  1. for formatting ManyErrors(errors: Vec<String>) (e.g. newline-separated) without additional allocation.

  2. for using temporary variables, I don't think I can write this without a Box or without declaring a binding before the write! invocation:

My(e: MyError) {
	display("{}", match e {
		&MyError::DontDisplay{..} => &e.description() as &fmt::Display,
		_ => e as _,
	})
}

@jethrogb
Well, for the first one I would like to have a crate that does Join(errors, ", ") (i.e. a wrapper type that implements Display). It has lot's of use cases and is quite common I think (is there such crate?).

For the second one, yes, I think it might be helpful. Would you like to make a PR?