Changing the Result signature from Result<Unit, string> to Result<Unit, Error>
See original GitHub issueI want to gather feedback on the change I’ve been pondering lately.
The problem
In most of my projects, I use a custom Error class (with a code and a message) to denote application errors. The problem with this approach is that it requires the use of Result<T, E> or UnitResult<E>, which looks clunky and bloated.
I would like to use the simple Result<T> and Result in such situations. These classes are currently a shortcut for Result<T, string> and Result<Unit, string>, meaning that the error type is string in both of them (Unit means “nothing” or “void”).
The solution
I propose to:
- Introduce a new class
Errorwithstring Codeandstring Messagefields - Change
Result<T>andResultsuch that they use theErrorclass as the error type instead ofstring
This will allow to reduce the Result<T, E> signature to just Result<T> and UnitResult<E> to just Result.
Potential issues
- This is a breaking change, though the consequences can be mitigated by introducing additional conversions between the
stringandErrorclasses. - I don’t know how many people use custom error types vs simple strings, this change might be disruptive to all of them
- I don’t know whether the signature of
Erroris sufficient for everyone (i.e the two string fields:CodeandMessage). People might need additional fields and thus won’t be able to use the built-inErrorand therefore will have to use the customResult<T, E>anyway
Personally, I feel that Error should have been introduced in the library from the very beginning. String is not a good type to represent errors anyway.
Please leave your thoughts in the comments.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:24
- Comments:45 (22 by maintainers)

Top Related StackOverflow Question
Personally, I never use Result, so I have nothing to say here.
I use
Result<T>quite rarely (as a return type for methods which parse value objects, for example), for me it serves just as a replacement forResult<T, string>(for cases when I do not distinguish between different error types). But it seems to me that for most projects, plainResult<T>is enough (in many places, in the case of web applications, developers just want to return 400 Bad Request + some kind of error message). Again, the entire README focuses onResult<T>only, so it seems to me that changing the error type forResult<T>would break a lot of code.Most projects don’t need anything other than
Result<T>. They are not going to do any compensation logic in case of errors (which they might need a strongly typed error representation for).Therefore, it seems to me that such a breaking change will not benefit the library. It seems like the ideal solution to your problem would be to implement this proposal (https://github.com/dotnet/csharplang/issues/1239) so that you can write
global using MyResult<T> = Result<T, MyError>. Unfortunately, we won’t see such a feature in C# 10.I would like to add that the proposed error format is unlikely to satisfy everyone. If, nevertheless, there is a desire to make this breaking change, then I could suggest such a format of the Error type:
By the way, I realized that we can take a look at the ProblemDetails structure (https://datatracker.ietf.org/doc/html/rfc7807), it was also designed as an error format suitable for everyone (however, we need to exclude everything that is specific to HTTP).
Personally, I now use
Result<T, TError>99% of the time, whereTErroris a very tricky self-written discriminated union that covers all possible outcomes (enum is not suitable, since you cannot add additional state to enum, and for some errors it is necessary to pass additional information). And TError is different for each operation.P.S. If I were asked to design a library from scratch, then I would probably implement
Result<T>asResult<T, Error>, and not asResult<T, string>, so my single concern is compatibility.Exceptions are not errors. Exceptions are not expected (like OutOfMemoryException), while errors are expected and documented (like validation errors). I lot of exception properties only make sense after exception was thrown. What’s the benefit of retuning exceptions? I think it’s even more counter-intuitive than returning results