[api design] Returning owned error semantics

I'm developing a crate where there are two connected end-points using a shared context. The library allows application-defined errors to be passed from one end-point to the other:

pub enum Error<E> {
  // ...
  App(E),
  // ...
}

impl<E> Endpoint<E> {
  /// Report an error to the remote end-point and terminate
  /// the internal worker.
  pub fn fail(self, e: E) {
    // ...
    shared.error = Some(e);
    // ...
   }

  pub fn do_something_that_requires_no_internal_error(&self) -> Result<(), Error<E>> {
    // ...
    if let Some(e) = shared.error.take() {
      return Err(Error::App(e));      
    }
    // ...
  }
}

Note that the error's ownership is transferred. When the error is returned by the other endpoint, it returns ownership. The error is "consumed" by the application once it has been returned from the library -- meaning the same error can not be returned twice.

I could solve this by using a Clone bound. However, I do have reasons for not wanting to do so.

Assuming the error is not made Cloneable:

Which would you prefer:

  • Once the other endpoint has returned an Err(Error::App(E)) it returns the error, but marks the internal state as having failed. Next call will return a different error (like Error::BadInternalState) indicating that the endpoint is in a bad state and can no longer be used (but the specifics are lost at this point).
  • Same as the above, but on subsequent calls will panic! and the docs clearly document that this is the case.
  • Stick the error in an Arc, and return a clone of the Arc instead.
  • [Ignoring the emphasized assumption that it's not Cloneable] Add a Clone bound and clone the error.

Also, I do not want to turn the error into a string and return that instead (related to the reason I don't want to impose a Clone bound on the caller).

The idea to return two different errors irks me, but the reason I'm even contemplating this is because looking through a bunch of code (both written by others as well as by me), I don't think I've encountered a single place where this would be an issue. Errors seem to be handled on first instance they are caught. I have yet to encounter this type of construction:

let res = foo.read();
if res.is_err() {
  // Get the error again, but now with more conviction
  foo.read()?;
}

(.. though I'm sure it exists -- my point is that well documented consuming errors might raise eyebrows, but I don't think in practice that it'll make anyone have to write code differently than they usually do).

How badly would I be violating POLS by documenting that failed calls should/must not be repeated?

I don't have an opinion on your actual question at this time, but that pattern is common when transient errors such as OS interruptions or other "try again later" errors can occur.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.