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