www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Result Types and void usage

reply nik <this_is_not_an_email_address notanemailaddress.com> writes:
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);
//}
Jul 15 2016
next sibling parent Jerry <Kickupx gmail.com> writes:
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
Jul 15 2016
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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);
 //}
Jul 15 2016
parent nik <this_is_not_an_email_address notanemailaddress.com> writes:
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
Jul 15 2016