Thread overview
Result Types and void usage
Jul 15, 2016
nik
Jul 15, 2016
Jerry
Jul 15, 2016
ag0aep6g
Jul 15, 2016
nik
July 15, 2016
Hi all,

I've been using D for a week now to compare it to Rust as a replacement to C++.
One thing I miss from Rust is https://doc.rust-lang.org/std/result/ and its companion https://doc.rust-lang.org/std/macro.try!.html

They make for some nice syntax so I had a go at recreating them in D (below):
One thing I cant figure out/don't know if is possible is to have a type that takes "void" (see last unittest)

For example in Rust a can declare a Result of Result<(), ErrorType>
however I'm struggling to find the same semantics in D. Of course I can just use a bool but I'd prefer not to.


Thank you for your time

Nik


Result type:

struct Result(T, E)
{	
	this(inout T result) inout
	{
		_result = result;
		_is_result = true;
	}

	this(inout E error) inout
	{
		_error = error;
		_is_result = false;
	}

	bool is_result() const pure nothrow @safe @property
	{
		return _is_result;
	}

	T result() const pure nothrow @safe @property
	{
		return _result;
	}

 	bool is_error() const pure nothrow @safe @property
	{
		return !_is_result;
	}

	E error() const pure nothrow @safe @property
	{
		return _error;
	}
private:
	T _result;
	bool _is_result;
	E _error;
}

unittest
{
	auto result = Result!(int, string)(1);
	assert(result.is_result);
	assert(result.result == 1);
}

unittest
{
	auto result = Result!(int, string)("error");
	assert(result.is_error);
	assert(result.error == "error");
}

unittest
{
	auto result_1 = Result!(int, string)(1);
	auto result_2 = Result!(int, string)(1);
	assert(result_1 == result_2);
}

unittest
{
	auto result_1 = Result!(int, string)(1);
	auto result_2 = Result!(int, string)(2);
	assert(result_1 != result_2);
}

unittest
{
	auto result_1 = Result!(int, string)(1);
	auto result_2 = Result!(int, string)("error");
	assert(result_1 != result_2);
}

unittest
{
	auto result_1 = Result!(int, string)("error");
	auto result_2 = Result!(int, string)("error");
	assert(result_1 == result_2);
}

unittest
{
	auto result_1 = Result!(int, string)("an error");
	auto result_2 = Result!(int, string)("error");
	assert(result_1 != result_2);
}

unittest
{
	enum ErrorType{A_Error, B_Error}
	auto result = Result!(int, ErrorType)(ErrorType.A_Error);
	assert(result.error() == ErrorType.A_Error);
	auto result_2 = Result!(immutable(bool), ErrorType)(true);
	assert(result_2.result());

	Result!(immutable(bool), ErrorType) a_method()
	{
		return Result!(immutable(bool), ErrorType)(true);
	}
	assert(a_method().result());
}

unittest
{
	auto result_1 = Result!(bool, string)(true);
	assert(result_1.result);
}

//unittest
//{
//	auto result_1 = Result!(void, string)(void);
//	auto result_2 = Result!(void, string)(void);
//	assert(result_1.is_result);
//	assert(result_1 == result_2);
//}
July 15, 2016
On Friday, 15 July 2016 at 08:11:13 UTC, nik wrote:
> //unittest
> //{
> //	auto result_1 = Result!(void, string)(void);
> //	auto result_2 = Result!(void, string)(void);
> //	assert(result_1.is_result);
> //	assert(result_1 == result_2);
> //}

You wanted to handle the void case?

Because there are no void type you have to check the type at compile time.
Enter static if...

static if(!is(T == void)) {
        T result() const pure nothrow @safe @property
	{
		return _result;
	}
}

static if(!is(T == void))
        T _result;

static if(!is(T == void)) {
        this(inout T result) inout
	{
		_result = result;
		_is_result = true;
	}
} else {
        this() {
                _is_result = true;
                 //Or whatever semantics Rust use for void result
        }
}


And about conditional compilation (static if, version etc...):
https://dlang.org/spec/version.html

July 15, 2016
On 07/15/2016 10:11 AM, nik wrote:
> One thing I cant figure out/don't know if is possible is to have a type
> that takes "void" (see last unittest)
[...]
> Result type:
>
> struct Result(T, E)
> {
>      this(inout T result) inout
>      {
>          _result = result;
>          _is_result = true;
>      }
>
>      this(inout E error) inout
>      {
>          _error = error;
>          _is_result = false;
>      }
>
>      bool is_result() const pure nothrow @safe @property
>      {
>          return _is_result;
>      }
>
>      T result() const pure nothrow @safe @property
>      {
>          return _result;
>      }
>
>       bool is_error() const pure nothrow @safe @property
>      {
>          return !_is_result;
>      }
>
>      E error() const pure nothrow @safe @property
>      {
>          return _error;
>      }
> private:
>      T _result;
>      bool _is_result;
>      E _error;
> }

void is somewhat special. It can't be used to declare variables or as a parameter type. So you'll have to approach this a bit differently. You also can't have a struct constructor with zero parameters.

You can detect void and make it a special case where slightly different code is generated:

----
struct Result(T, E)
{
    static if (!is(T == void)) this(inout T result) inout
    {
    ...
    }

    ...

    T result() const pure nothrow @safe @property
    {
        static if (!is(T == void)) return _result;
    }

    ...

    static if (!is(T == void)) T _result;
    bool _is_result = true; /* important when T is void */
}
----

[...]
> //unittest
> //{
> //    auto result_1 = Result!(void, string)(void);
> //    auto result_2 = Result!(void, string)(void);

`void` can't be an argument in D. Just leave the list empty: `Result!(void, string)()`.

> //    assert(result_1.is_result);
> //    assert(result_1 == result_2);
> //}

July 15, 2016
On Friday, 15 July 2016 at 11:36:27 UTC, ag0aep6g wrote:
> On 07/15/2016 10:11 AM, nik wrote:
>> [...]
> [...]
>> [...]
>
> void is somewhat special. It can't be used to declare variables or as a parameter type. So you'll have to approach this a bit differently. You also can't have a struct constructor with zero parameters.
>
> [...]

Cool, that's neat and has the syntax I'm looking for.

Thanks for your help

Nik