komora-io / terrors

ergonomic and precise error handling built atop type-level set arithmetic
Apache License 2.0
200 stars 4 forks source link

InterOp with anyhow crate #12

Open bodymindarts opened 5 months ago

bodymindarts commented 5 months ago

I am investigating migrating a large library / binary crate that uses thiserror internally and anyhow at the cli boundary.

Unfortunately this doesn't compile:

use terrors::OneOf;

fn main() -> anyhow::Result<()> {
    test()?;
    Ok(())
}

fn test() -> Result<(), OneOf<(String,)>> {
    Ok(())
}

The compilation error is:

error[E0277]: the trait bound `terrors::End: std::error::Error` is not satisfied
 --> test-anyhow/src/main.rs:4:11
  |
4 |     test()?;
  |           ^ the trait `std::error::Error` is not implemented for `terrors::End`, which is required by `Result<(), anyhow::Error>: FromResidual<Result<Infallible, OneOf<(String,)>>>`
  |
  = help: the following other types implement trait `FromResidual<R>`:
            <Result<T, F> as FromResidual<Yeet<E>>>
            <Result<T, F> as FromResidual<Result<Infallible, E>>>
  = note: required for `Cons<String, terrors::End>` to implement `std::error::Error`
  = note: 1 redundant requirement hidden
  = note: required for `OneOf<(String,)>` to implement `std::error::Error`
  = note: required for `anyhow::Error` to implement `From<OneOf<(String,)>>`
  = note: required for `Result<(), anyhow::Error>` to implement `FromResidual<Result<Infallible, OneOf<(String,)>>>`

error[E0277]: `(dyn Any + 'static)` cannot be sent between threads safely
   --> test-anyhow/src/main.rs:4:11
    |
4   |     test()?;
    |           ^ `(dyn Any + 'static)` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `(dyn Any + 'static)`, which is required by `Result<(), anyhow::Error>: FromResidual<Result<Infallible, OneOf<(String,)>>>`
    = help: the following other types implement trait `FromResidual<R>`:
              <Result<T, F> as FromResidual<Yeet<E>>>
              <Result<T, F> as FromResidual<Result<Infallible, E>>>
    = note: required for `Unique<(dyn Any + 'static)>` to implement `Send`
note: required because it appears within the type `Box<(dyn Any + 'static)>`
   --> /nix/store/36813l3qgxqj6krm48099sqr3fv0j5yy-rust-default-1.78.0/lib/rustlib/src/rust/library/alloc/src/boxed.rs:197:12
    |
197 | pub struct Box<
    |            ^^^
note: required because it appears within the type `OneOf<(String,)>`
   --> /Users/jcarter/.cargo/registry/src/index.crates.io-6f17d22bba15001f/terrors-0.3.0/src/one_of.rs:32:12
    |
32  | pub struct OneOf<E: TypeSet> {
    |            ^^^^^
    = note: required for `anyhow::Error` to implement `From<OneOf<(String,)>>`
    = note: required for `Result<(), anyhow::Error>` to implement `FromResidual<Result<Infallible, OneOf<(String,)>>>`

There seems to be 2 issues. std::error::Error is not implemented for End and the issue with thread safety. Would appreciate any ideas / comments for moving towards interoperability.

d4h0 commented 5 months ago

@bodymindarts: OneOf only implements Error if all types in the set implement Error and String doesn't implement Error (see here).

Regarding the other error: OneOf doesn't implement Send (see here), and I'm not really sure why (I'm not the author of this crate, btw.). You should be able to wrap the error in a Mutex to make it Send, but that doesn't seem great to me.

bodymindarts commented 5 months ago

@bodymindarts: OneOf only implements Error if all types in the set implement Error and String doesn't implement Error (see here).

Yeah I noticed that but its the same with a struct that does implement Error:

use terrors::OneOf;

#[derive(thiserror::Error, Debug)]
#[error("some error")]
struct SomeError;

#[derive(thiserror::Error, Debug)]
#[error("some other error")]
struct SomeOtherError;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    test()?;
    Ok(())
}

fn test() -> Result<(), OneOf<(SomeOtherError, SomeError)>> {
    Ok(())
}

Error:

error[E0277]: the trait bound `terrors::End: std::error::Error` is not satisfied
  --> test-anyhow/src/main.rs:12:11
   |
12 |     test()?;
   |           ^ the trait `std::error::Error` is not implemented for `terrors::End`, which is required by `Result<(), Box<dyn std::error::Error>>: FromResidual<Result<Infallible, OneOf<(SomeOtherError, SomeError)>>>`
   |
   = help: the following other types implement trait `FromResidual<R>`:
             <Result<T, F> as FromResidual<Yeet<E>>>
             <Result<T, F> as FromResidual<Result<Infallible, E>>>
   = note: required for `Cons<SomeError, terrors::End>` to implement `std::error::Error`
   = note: 2 redundant requirements hidden
   = note: required for `OneOf<(SomeOtherError, SomeError)>` to implement `std::error::Error`
   = note: required for `Box<dyn std::error::Error>` to implement `From<OneOf<(SomeOtherError, SomeError)>>`
   = note: required for `Result<(), Box<dyn std::error::Error>>` to implement `FromResidual<Result<Infallible, OneOf<(SomeOtherError, SomeError)>>>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `test-anyhow` (bin "test-anyhow" test) due to 1 previous error
error: command `/nix/store/36813l3qgxqj6krm48099sqr3fv0j5yy-rust-default-1.78.0/bin/cargo test --no-run --message-format json-render-diagnostics` exited with code 101
[Finished running. Exit status: 101]

So the auto implementing of std::error::Error seems to have issues.