Idiomatic way to express errors without resorting to exceptions

Basile B. b2.temp at gmx.com
Sat Feb 29 15:01:37 UTC 2020


On Saturday, 29 February 2020 at 12:50:59 UTC, Adnan wrote:
> I have a struct that has to arrays. Each of those must have the 
> same sizes.
>
> So while constructing the array, if you pass two arrays of 
> different sizes the constructor must return nothing.
>
> In Rust I could easily use Option<T>. D has no answer to 
> Optional types as far as I am concerned. Is throwing exceptions 
> the only idiomatic way?
>
>
> ---
>
> What I already considered:
>
> * Using Nullable!T: Okay but Nullable!T has all the overloads 
> for regular T which makes the  API even more unpredictable.
>
> In Rust you don't just add a Some(44) and 34; No overloads for 
> Some<T> and i32 are allowed (purposefully).
>
> * Option!T from the optional package: Has even worse problem 
> IMO. Not only it allows None + int but also it returns a `[]`. 
> This API is not to my liking. You could say well Haskell has 
> fmap for Optional etc, and I am aware of that, so does Rust 
> with map etc. But I am talking about basic things: like `+`.
>
> * Value-based error handling like Go and C: well, that works 
> but the error checking is opt-in. There's no such thing as 
> [[nodiscard]] in D too so the user of the API might as well 
> forget to check for error value.
>
> * if specialization: Clever workaround but sometimes the struct 
> may fail for complex reasons, not only for a single condition.

There's no idiomatic way since D lang is based on exceptions...

However I'd use one of those system:

1. return error, write result in ref parameter.

     alias CheckedValueProto(RT, P...) = bool function(ref RT, P 
params);

2. the same using a special struct and no more ref param. So more 
like Nullable/Optional but with a dedicated generic type that 
contain a single opover used to indicate if there's been an error 
or not.

     struct CheckedValue(T) {
         bool noError;
         T t;
         B opCast(B : bool)() inout pure nothrow @safe {
             return noError;
         }
     }

and you make your functions to return CheckedValues...

     CheckedValue!int strToInt(string input);
     ....
     if (const CheckedValue!int = strToInt("a") {} else {}

Although
- both still require self-discpline or a specialized linter to 
detect unchecked calls ;
- the whole standard library is incompatible ;

I have a personal preference for 2. even if it causes problems 
when T is of same size as a pointer. Now the question is also 
what's the more costly ? try/catch or this non atomic return ?


More information about the Digitalmars-d-learn mailing list