Null references redux

Jeremie Pelletier jeremiep at gmail.com
Sat Sep 26 21:43:41 PDT 2009


Daniel Keep wrote:
> 
> Jeremie Pelletier wrote:
>> ...
>>
>> This is something for the runtime or the debugger to deal with. My
>> runtime converts access violations on windows or segfaults on linux into
>> exception objects, which unwind all the way down to main where it
>> catches into the unhandled exception handler (or crash handler) and I
>> get a neat popup with a "hello, your program crashed at this point, here
>> is a backtrace with resolved symbols and filenames along with current
>> registers and loaded modules, would you like a cup of coffee while you
>> solve the problem?". I sent that crash handler to D.announce last week too.
> 
> See my long explanation that NPEs are only symptoms; very rarely do they
> put up a big sign saying "what ho; the problem is RIGHT HERE!"
> 
>> The compiler won't be able to enforce *every* nonnull reference and
>> segfaults are bound to happen, especially with casting. While it may
>> prevent most of them, any good programmer would too, I don't remember
>> the last time I had a segfault on a null reference actually.
> 
> I do.  It took a day and a half to track it back to the source.

Happens to me on some issues too, I don't ask for a workaround in the 
compiler, I just learn my lesson and never repeat that error.

>> I can see what the point is with nonnull references, but I can also see
>> its not a bulletproof solution. ie "Object foo = cast(Object)null;"
>> would easily bypass the nonnull enforcement, resulting in a segfault the
>> system is trying to avoid.
> 
> Why lock the door when someone could break the window?

Easier to prove someone broke in when the window is shattered than if 
someone just went through the door, stole your stuff and left without 
any traces.

> Why have laws when people could break them?

People break the law, some of them only for the challenge of it, some of 
them to survive, some just don't care. Remove the laws and you remove 
most of these behaviors you're trying to prohibit in the first place. 
Most of the time laws are there so corporate criminals can get rid of 
street criminals legally.

> Why build a wall when someone could park a hydrogen bomb next to it?

They keep most people out, or in. Hydrogen bombs are not something you 
expect the first guy on the street to own.

> Why have a typesystem when you could use casting to put the float
> representation of 3.14159 into a void* and then dereference it?

Because it also allows for countless different optimizations, at the 
price of also being able to shoot your own foot.

There, four similar questions and four completely different answers. My 
point is, there is no perfect all-around solution.

> Casting is not an argument against non-null references because casting
> can BREAK ANYTHING.
> 
> "Doctor, it hurts when I hammer nails into my shin."
> 
> "So stop doing it."

Why tell him to stop it? The guy will just kill himself at some point 
and raise the collective IQ of mankind in the process. Same for 
programming or anything else, if someone is dumb enough to repeat the 
same mistake over and over, he should find a new domain to work in.

>> What about function parameters, a lot of parameters are optional
>> references, which are tested and then used into functions whose
>> parameters aren't optional. It would result in a lot of casts, something
>> that could easily confuse people and easily generate segfaults.
> 
> So what you're saying is: better to never, ever do error checking and
> just start fixing things after they've broken?

No, but you shouldn't rule out the fact that they may break, no matter 
what system you're working with.

> And why is everything solved via casting?  Look: here's a solution
> that's less typing than a cast, AND it's safe.  You could even put
> nonnull it in object.d!
> 
> T notnull(U : T?, T)(U obj)
> {
>     if( obj is null ) throw new NullException;
>     return cast(T) obj;
> }
> 
> void foo(Quxx o)
> {
>     o.doStuff;
> }
> 
> void foo(Quxx? o)
> {
>     foo(notnull(o));
> }

Also slower than a cast if the compiler doesn't use -inline. Debug 
builds are already painful enough as it is with realtime code.

>> Alls I'm saying is, nonnull references would just take the issue from
>> one place to another.
> 
> YES.
> 
> THAT'S THE POINT.
> 
> It would take the error from a likely unrelated location in the
> program's execution and put it RIGHT where the mistake initially occurs!

That's a case for variable initialization, not nullable/non-null types.

A nonnull type does not guarantee the value will *never* be null, even 
the simplest hack can get around it.

>> Like Walter said, you can put a gas mask to ignore
>> the room full of toxic gas, but that doesn't solve the gas problem in
>> itself, you're just denyinng it exists. Then someday you forget about
>> it, remove the mask, and suffocate.
>>
>> Jeremie
> 
> That's what NPEs are!  They're a *symptom* of you passing crap in to
> fields or functions.  They very, VERY rarely actually point out what the
> underlying mistake is.

There again, I favor stronger initialization semantics over nonnull 
types. This will get rid of most of these errors and still keep you on 
your toes when a segfault arise, if you only see a segfault once a year 
how will you know how to handle it :)

Most segfaults I have take me at most a few minutes to pinpoint. Its 
finding backdoors to compiler enforcements thats annoying.



More information about the Digitalmars-d mailing list