Let's improve D's exceptions

Adam D. Ruppe via Digitalmars-d digitalmars-d at puremagic.com
Wed May 13 08:08:57 PDT 2015


Have you ever done:

if(something) {
    import std.conv;
    throw new Exception("some error " ~ to!string(some_value));
}

Don't you hate it?

* having to import std.conv to see data from your exception is a 
pain
* it allocates eagerly and thus isn't suitable for a lot of places
* inspecting the data can be a pain as the string is unstructured

This assumes the data is even bothered to be added. Anyone who 
has gotten a RangeError in D knows important information is often 
just dropped!

A good solution is to make a new exception subclass for each 
error type, storing details as data members. However, that's a 
bit of a pain in D because of all the work you have to do to make 
a complete subclass:

* you need to forward the constructors
* you need to reimplement the toString to print out data members
* you still have a string member there that is encouraged to be 
used


Finally, with the discussion of @nogc exceptions, it would be 
nice to simplify the constructor and get file/line info separated 
from it at the same time too. Can we tackle all these problems at 
once?

I propose we do some library changes, one minor compiler change, 
and a culture shift toward better exceptions. Here's my proof of 
concept:

http://arsdnet.net/dcode/exception.d


Read that code and the comments to see what I'm going for and 
what it looks like. Here's the summary:

* The Exception constructor is simplified: no required arguments, 
everything else is done with members. (The Throwable next = null 
argument might come back, but it can also be set after 
construction anyway so I'd prefer not to worry about it in most 
places either.)

* The string is de-emphasized. It is still there so we don't 
break all the code that uses it now, but it is no longer the main 
thing and we really would prefer that it isn't used at all. 
Exception messages are instead done with the class name and data 
members. The string may be gotten by the class though in cases 
like converting errno to a message.

* File and line is set at the throw point instead of the 
construction point.

* A mixin template uses reflection to print data to the user 
instead of constructing a string.

* This is allocation-free, except for the exception object 
itself. User code would NOT have to import std.conv, it just 
stores the data in the object.



Changes:

TO THE COMPILER:
   * make the call to d_throw add file and line number 
information. This is completely backward compatible and allows 
the runtime to store it in the object if it wants to. Then my 
"fly" method becomes unnecessary.

TO THE LIBRARY:
   * Throwable's toString implementation changes a bit to call a 
couple more virtual functions to print details. Very similar to 
the current code, as you can see in my file there, just some 
overridable hooks.

   * A PrintMembers and perhaps other helper mixins are added to 
the library. Doesn't matter where they go, druntime might do a 
simpler one for its own needs and then Phobos offers a full 
featured one for user code. Or whatever. It'd need to be a bit 
more complex than my proof of concept to cover more data (at 
least let it try calling toString on types too) but this is 
already pretty useful as is.


TO USER CODE:
   * We start using subclasses with data members instead of string 
arguments, migrating to it incrementally and encouraging people 
to write exceptions this way going forward.

   * Note that this should be completely backward compatible, it 
won't break old code, just encourage a new better way.



What do you all think? I encourage you to grab my example there 
and play with it a bit to see how you like it too and see if you 
can think of any improvements.

D's exceptions kinda suck right now but they don't have to!


More information about the Digitalmars-d mailing list