I'm of a stance that good code should have many throws and few try/catch statements. I was curious how Phobos does there, so I ran a few simple greps around.

Seems like there's about 145 try statements in Phobos. Of these, more than a third (51) belong to std.datetime.

Looks like the following idiom is often present in std.datetime:

    @property FracSec fracSec() const nothrow
    {
        try
        {
            auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime);

            if(hnsecs < 0)
                hnsecs += convert!("hours", "hnsecs")(24);

            hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs);

            return FracSec.from!"hnsecs"(cast(int)hnsecs);
        }
        catch(Exception e)
            assert(0, "FracSec.from!\"hnsecs\"() threw.");
    }

(It may seem I chose this example to discuss the "let's insert one empty line every other line" idiom. I didn't.) Essentially the code relies on calls that may generally throw, but calls them with parameters that should guarantee there won't be any throwing. In wanting to offer as many "nothrow" guarantees as possible, the code ends up inserting these try/catch statements - seemingly needlessly.

This is quite heavy-handed, so I was wondering what we could do to improve on this. I thought of the following possibility:

    @property FracSec fracSec() const nothrow
    {
        scope(failure) assert(0, "FracSec.from!\"hnsecs\"() threw.");
        auto hnsecs = removeUnitsFromHNSecs!"days"(adjTime);
        if (hnsecs < 0)
            hnsecs += convert!("hours", "hnsecs")(24);
        hnsecs = removeUnitsFromHNSecs!"seconds"(hnsecs);
        return FracSec.from!"hnsecs"(cast(int)hnsecs);
    }

This at least leaves the normal path unaltered and deals with the unexpected in a more isolated manner. I was pleased that the changed code compiled just fine. My happiness was short-lived because before long I figured this compiles as well:

        ...
        scope(failure) {}
        ...

So it's not that the compiler cleverly figured the function will not throw. Instead, the compiler considers everything dandy as soon as it sees a scope(failure), no matter of the statement it controls. I'll report that bug soon.

What would be the best approach here?

0. Do nothing. This is as good as it gets.

1. Fix scope(failure) and then use it.

2. Relax the nothrow guarantees. After all, nothrow is opt-in.

3. Look into API changes that add nothrow, narrower functions to the more general ones that may throw.

4. ...?


Andrei

Reply via email to