Skip to content

Conversation

julianthurner
Copy link
Contributor

@julianthurner julianthurner commented Aug 26, 2025

Currently, DotNext lacks support for Result Types that don't hold values, but instead only indicate success or failure, smiliar to Rust's Result<(), io::Error> for example. This draft aims to provide that functionality. It provides

  • ActionResult Type that represents Result<void>
  • static ActionResult.Try() Function that executes a potentially error throwing function and catches those errors (if they occur) within an ActionResult
  • AwaitableResult Type that represents AwaitableResult<void>

ActionResult Type and static ActionResult.Try() Function:

public ActionResult DoSomethingThatWillNotThrow()
{
    var result = ActionResult.Try(() => File.WriteAllText("some/path", "42")); // returns ActionResult

    if (!result.IsSuccessful)
    {
        Console.WriteLine(result.Error.Message);
        return new(result.Error);
    }

    // Proceed with code
    return new();
}

AwaitableResult Type:

var awaitableResult = File.WriteAllTextAsync("some/path", "42").CatchException(); // returns AwaitableResult
var result = await awaitableResult; // resolves to ActionResult

The aim of these additions is 1) to give the user the option to write a strong API that tells the consumer "This function might fail (but not throw Errors)" and 2) to give the user a way of handling exceptions without try catch blocks.
Both 1) and 2) are currently only possible if the function in question returns an actual value in the form of Result<T> or Result<T, TError>, but not if the function is a non-returning Action.

To Do's:

  • It may be better to discard the parameterless new ActionResult() constructor and new ActionResult(Exception e) in favor of something like static ActionResult.Success and static ActionResult.Error(Exception e).
  • Also, an extension method Action.TryInvoke() just like the existing Func<T>.TryInvoke() should be added as well.
  • Unit Tests once the implementation is complete

@sakno
Copy link
Collaborator

sakno commented Sep 10, 2025

DotNext lacks support for Result Types that don't hold values

That is questionable, because in C# such a result type can be easily represented by simple Exception? data type. If it's null, then no exception is returned.

Also, the same can be emulated with existing Result<T> data type. System.Reflection.Missing class can be used as generic argument to indicate that there is no meaningful value within the result.

ActionResult.Try(Action) allocates on every invocation, because the lambda typically has closures. This is why the library doesn't have Try helpers to return Result<T> from Func<T>.

sakno added a commit that referenced this pull request Sep 11, 2025
@julianthurner
Copy link
Contributor Author

julianthurner commented Sep 12, 2025

@sakno Thank you for explaining, that makes lots of sense. I never thought of the heap allocation problem with lambdas.

I also saw that you already included my suggestion to preserve Exceptions with Task.CatchException(), thank you for that!

That only leaves one suggestion I still have: An extension method for Action to support calling native C# functions safely like it's already possible with Func:

Action<string, string> writeAllText = File.WriteAllText;
Exception? result = writeAllText.TryInvoke("some/path", "42");

Is that something you think is worth being implemented?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants