Is there any crate that allows me to return multiple errors the same way as the typescript?

There are discussions about an anonymous version of an enum that would do something like that but it doesn't seem to be available.
So my last solution is to consult you and ask if there is any magic crate that does this. I wanted to avoid using Box and above all to avoid creating a generic enum for each new group of multiple errors.

https://github.com/rust-lang/rfcs/issues/294

Not really. If you want to avoid a Box, then you need an enum. That said, crates like thiserror make the process of defining such enums easier.

1 Like

Are you working on a library or application? The majority of application-level code should be fine with something like anyhow—a single, "open" error type which can be downcast into specific errors.

For library code (or certain parts of application code where it is useful to constrain the error type), one should provide something more meaningful, but this can be largely automated via the thiserror crate mentioned above.

But ultimately, rust does not have ad-hoc unions like typescript, and the resulting loss of expressivity is, well, something you'll have to come to terms with. Some libraries use one error type for many methods, even though not all of the variants apply to all of the methods. Some provide highly granular errors for different functions. It really depends on how much utility you expect the caller to get out of the ability to match on your errors.

2 Likes

I wonder if there should be guidelines (proposed to and by the error-handling working group) for library authors, like : provide both granular error structs and convenience error enum and result type and make your functions return the granular errors not the convenience enum.

pub struct ErrorFoo { ... }
pub struct ErrorBar { ... }
pub enum Error {
    Foo(ErrorFoo),
    Bar(ErrorBar),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

That's a maintenance burden for sure, but it pays off in flexibility for the user I think.

1 Like

Hm, this would be an interesting pattern, though in many cases there are multiple types of errors that can come from one function.

E.g.

fn foo() -> Result<(), A | B>;
fn bar() -> Result<(), A | C>;
fn baz() -> Result<(), A | B | C>;

I believe it really needs to be considered on a case-by-case basis whether such a library should have all of these use a unified enum, or if they should return three separate enums. I don't think there's a one-size-fits all solution here.

1 Like

I'm aware of the traditional way of handling the situation, I was looking for a more expressive and more direct way to do it. The traditional way is very unpleasant.
I made this post hoping to have some news about returning multiple errors.

I don't understand why there are anonymous tuples but no anonymous enum.
The user would clearly know all possible errors and the developer would have no extra work to return multiple errors.

So....

One possible alternative is for your error to be something like frunk::Coproduct. This is a variable length, tagged enum.

Would I recommend this? Not really. It would put an unusual concept from an experimental library in your public API, and I feel like that's bringing on a lot of technical debt (and backwards compatibility concerns) for a library.

You could also try to restart discussions on introducing ad-hoc enums as a language feature, but those mostly went in circles and I imagine something new will have to be brought to the table if there's any hope of moving forward.

2 Likes

You can search for past discussions on "disjoins"

edit: huh, I really thought that term would be unique enough to give good Google results but it doesn't. I'll look around a bit...

Here: Disjoins (anonymous enums) by canndrew · Pull Request #1154 · rust-lang/rfcs · GitHub

1 Like

In a recent lang subteam meeting, we decided to close this RFC (and list it under issue #294). While anonymous disjoint unions are interesting and have their uses, the advantages seem outweighed by the complexity introduced into the type system and runtime (as well as the duplication with existing enums).

powerset_enum and cex implement something similar to what you're looking for. I haven't used them so I can't comment on how they are in practice but they're interesting proofs of concept at least.

I'm not sure I like anonymous enums but I do wish there was language level support for using subsets of enums. powerset_enum only supports 1-tuple variants and is not as ergonomic as it could be, plus whatever impact it has on compile times. I found an old blog post exploring the topic and an RFC, but it doesn't seem like there's been too much interest since 1.0.

Maybe one day code like this will compile

enum MyError {
    A, B, C
}

fn fallible() -> Result<(), MyError::{A}> {
    Err(MyError::A)
}

fn another_fallible() -> Result<(), MyError::{A, B}> {
    fallible()?;
    Err(MyError::B)
}

fn main() {
    match another_fallible() {
        Ok(()) => {}
        Err(MyError::A) => {}
        Err(MyError::B) => {}
        // no need to handle C
    }
}
1 Like

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.