Proposal: Result struct providing generic error only
See original GitHub issueProblem:
For some operations, we don’t need to know what the resulting value was, only the execution status (explicitly) and failure errors. There are no structs in functional Result mechanics designed specifically for such scenarios.
Limitations of existing solutions:
We can pass generic errors with struct Result<T, E>.
But it has some problems:
-
We must pass value even if we dont want to
-
If we create and use some kind of
voidorunittype as value, we still must specify both types explicitly:Result.Ok<UnitTypeStub, ErrorClass>(UnitTypeStub.Default) Result.Fail<UnitTypeStub, ErrorClass>(new ErrorClass())while we want
Result.Fail(new ErrorClass()) Result.Ok<ErrorClass>()
References: The same idea was raised at the end of the discussion within issue #35. According to #124 and #122 having Result struct with generic error and no value would help people to code their intentions and expectations more clearly.
Proposed solution:
We need additional type to existing Result, Result<T>, Result<T, E>.
Unfortunately, we cannot implement this struct as a generic Result<E> literally, but we can make semantic deviation of a new struct as small as possible.
Struct draft I came up with
[Serializable]
public struct UnitResult<E> : IResult, ISerializable
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly ResultCommonLogic<E> _logic;
public bool IsFailure => _logic.IsFailure;
public bool IsSuccess => _logic.IsSuccess;
public E Error => _logic.Error;
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
_logic.GetObjectData(info, context);
}
[DebuggerStepThrough]
internal UnitResult(bool isFailure, E error)
{
_logic = new ResultCommonLogic<E>(isFailure, error);
}
UnitResult(SerializationInfo info, StreamingContext context)
{
bool isFailure = info.GetBoolean("IsFailure");
E error = isFailure ? (E)info.GetValue("Error", typeof(E)) : default;
_logic = new ResultCommonLogic<E>(isFailure, error);
}
public static implicit operator Result(UnitResult<E> result)
{
if (result.IsSuccess)
return Result.Ok();
else
return Result.Fail(result.Error.ToString());
}
public void Deconstruct(out bool isSuccess, out bool isFailure)
{
isSuccess = IsSuccess;
isFailure = IsFailure;
}
public void Deconstruct(out bool isSuccess, out bool isFailure, out E error)
{
isSuccess = IsSuccess;
isFailure = IsFailure;
error = IsFailure ? Error : default;
}
}
public static class UnitResult
{
[DebuggerStepThrough]
public static UnitResult<E> Ok<E>()
{
return new UnitResult<E>(false, default);
}
[DebuggerStepThrough]
public static UnitResult<E> Fail<E>(E error)
{
return new UnitResult<E>(true, error);
}
public static UnitResult<E> Create<E>(bool isSuccess, E error)
{
return isSuccess
? Ok<E>()
: Fail(error);
}
public static UnitResult<E> Create<E>(Func<bool> predicate, E error)
{
return Create(predicate(), error);
}
public static async Task<UnitResult<E>> Create<E>(Func<Task<bool>> predicate, E error)
{
bool isSuccess = await predicate().ConfigureAwait(Result.DefaultConfigureAwait);
return Create(isSuccess, error);
}
}
What do you think? Does it worth the implementation? Does it fit library’s direction?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:7
- Comments:5 (2 by maintainers)

Top Related StackOverflow Question
Apology for the late answer. I personally don’t have any objections, looks like a good feature well worth adding to the library.
Unfortunately, no