Double error conversion with question mark operator

I have a function that can return several layers of errors. Here's a simplification to demonstrate the issue:

fn my_func() -> nb::Result<(), MyFuncError<InternalError>> {
  if device_busy() {
    return Err(nb::Error::WouldBlock);
  }
  if some_problem() {
    return Err(nb::Error::Other(MyFuncError::SomeProblem);
  }
  do_something_else()?;// return type Result<(), InternalError>
  // ...
}

The problem comes from the do_something_else function with the question mark ? operator. Rust complains that there is no impl of the From<InternalError> for the nb::Error<MyFuncError<InternalError>> type.

While this is true, there are From implementations for both steps of that conversion: one for InternalError -> MyFuncError<InternalError>, another for MyFuncError<InternalError> -> nb::Error<MyFuncError<InternalError>. I can't figure out a way to get Rust to realize it can use those two From impls together to achieve the desired result. I tried implementing the specific trait it asks for, but it also doesn't let you implement traits for structs that are outside the current crate (which nb is).

The only thing I've been able to do to get it working is this:

  do_something_else().map_err(MyErrorWrapper::from)?;

That way Rust only has to make one conversion step. I'm not a huge fan of this approach because it makes the code much busier, thereby eliminating benefit of the ? operator. Is there a better way to handle this?

Rust will never automatically do two-step conversions, because there may be multiple ways to do it, and even if there aren't now, multiple ways may be added later, silently changing the behavior of the code.

The alternative is to add a From impl that directly performs the desired conversion.

1 Like

That's too bad. Re: your suggestion, as I pointed out in my question, Rust will not let me impl the From trait in this circumstance between both the struct (nb::Result) and the trait (From) come from outside of my crate. The only way to get around this that I found was to create a crate-internal copy of nb::Result, and then another From impl to convert my internal version to the external nb:Result.