Against enforce()

Jonathan M Davis jmdavisProg at gmx.com
Thu Mar 24 08:49:36 PDT 2011


> So how do you solve the problem?
> 
> ---------
> 
> > > This is a good example of why it's difficult to decide what "user
> > > input" is.  One could consider that the 'user' in this case is the
> > > developer using the library, but I don't think that's the right
> > > choice.
> > > 
> > > I'd say it's a bug, this is clearly a contract, since the data being
> > > passed into the ctor can easily not be user input (i.e. it's most
> > > likely two literals that will never depend on a user).  If it is user
> > > input, the caller of the ctor should enforce the user input before
> > > passing it to iota.
> > 
> > You can't validate all user input, so external data ends up spead across
> > your entire application. So I don't understand obsession with -release
> > switch, because contracts most of the time do validate user input. If we
> > think about -release switch as a HP-hack for exotic code, there will be
> > no ideological difference between assert and enforce.
> 
> As has been point out, the problem is in cases where it's not clear whether
> you should treat input as user input (and therefore needs to _always_ be
> checked and have exceptions thrown on error) or whether you should treat
> input as being from your program and guaranteed to be valid (at which
> point you use assert to check that that guarantee actually holds).
> ----------

It's a case by case thing. In some cases, you go with assertions and let the 
code choke horribly (or worse, silently sort of work but not quite right) if 
it's used in a case where it should have been an exception. In others, you use 
exceptions and just let the efficiency be degraded. It depends on the 
situation and what you're trying to do. In many cases, you'd go with an 
assertion and make it clear that the caller needs to check if they want the 
function to actually work correctly. Then it's up to the caller to check or 
not.

There is no good answer for what to _always_ do in this sort of situation, 
because the costs can vary considerably from situation to situation. If the 
function is very cheap, then having additional checks in release mode could be 
devastating, and you just can't afford to be throwing exceptions from it. On 
the other hand, if it's very expensive, then having the additional checks 
wouldn't matter at all. The big question is what the general policy should be 
in Phobos functions. Should the default choice be to use an assertion, which 
will usually then do no checks at all, because assertions are almost always 
compiled out in Phobos (unless people compile it themselves), or should 
enforce be used and then have the additional cost in there all of the time. I 
would guess that iota is most frequently used with known values at compile 
time - iota(1, 512) - in which case assert makes perfect sense. In others, the 
value used could be based on lots of calculations somewhere and maybe even 
depends on user input - iota(a, b). The best choice would depend on what we 
expect the typical use case to be and how high the cost is to pick the other 
choice.

The other possibility is to specifically have two versions of a function: one 
which uses an assertion (which may or may not be enabled) and therefore 
essentially does no checking, thereby requiring that the programmer ensure 
that the arguments are correct or the function could do funny things, and one 
which uses an exception. Then the programmer could choose whether they want 
the checks to occur or not (e.g. iota could assert or do no checks at all and 
iotoE could throw an exception). However, that doesn't scale very well if you 
try and do that with every function that has this problem.

So, there really is no good answer.

- Jonathan M Davis


More information about the Digitalmars-d mailing list