An alternative to .init

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Tue Sep 9 08:52:50 PDT 2008


downs wrote:
> Andrei Alexandrescu wrote:
>> What's the deal with destroyed but not deleted? Consider:
>> 
>> int[] a; if (condition) { Array!(int) b; a = b.all; } writeln(a);
>> 
>> This is a misuse of the array in that a range crawling on its back
>> has survived the array itself. What should happen now? Looking at
>> other languages:
>> 
>> 1) All Java objects are unowned, meaning the issue does not appear
>> in the first place, which is an advantage. The disadvantage is that
>> scarce resources must be carefully managed by hand in client code.
>> 
>> 2) C++ makes the behavior undefined because it destroys data AND 
>> recycles memory as soon as the array goes out of scope. Mayhem
>> ensues.
>> 
>> We'd like:
>> 
>> 1.5) Allow object ownership but also make the behavior of incorrect
>> code well-defined so it can be reasoned about, reproduced, and
>> debugged.
>> 
>> That's why I think an Array going out of scope should invoke
>> destructors for its data, and then obliterate contents with
>> ElementType.init. That way, an Array!(File) will properly close all
>> files AND put them in a "closed" state. At the same time, the
>> memory associated with the array will NOT be deallocated, so a
>> range surviving the array will never crash unrelated code, but
>> instead will see closed files all over.
>> 
> 
> I don't think this is a good thing, for reasons similar to the
> Error/Exception flaw - specifically, code that works in debug mode
> might end up failing in release mode.
> 
> To explain what I mean by Error/Exception flaw, consider the case of
> an array out of bounds error, wrapped carelessly in a try .. catch
> (Exception) block.
> 
> This would work fine in debug mode, and presumably retry the
> operation until it succeeded.
> 
> In release mode, however, the above would crash.
> 
> This is clearly undesirable, and arises directly from the fact that
> Error is derived from Exception, not the other way around or
> completely separate (as it clearly should be).
> 
> After all, an Error ![is] an Exception, since Exceptions are clearly
> defined as recoverable errors, and the set of unrecoverable errors is
> obviously not a subset of the recoverable ones.
> 
> This leads to my actual point: I suggest an extension of .init: the
> .fail state, indicating data that should not be accessed.
> 
> Any standard library function that encounters data that is
> intentionally in the .fail state should throw an Error.
> 
> For instance, the .fail state for strings could be a deliberately
> invalid UTF8 sequence.
> 
> When this state could reasonably come up in normal operations, it is
> recommended to use values that will readily be visible in a debugger,
> such as the classical 0xDEADBEEF.
> 
> This is imnsho superior to using .init to fill this memory, which
> doesn't tell the debugging programmer much about what exactly
> happened, and furthermore, might cause the program to treat "invalid"
> memory the same as "fresh" memory, if only by accident.

I hear you. I brought up the same exact design briefly with Bartosz last 
week. We called it T.invalid. He argued in favor of it. I thought it 
brings more complication than it's worth and was willing to go with 
T.init for simplicity's sake. Why deal with two empty states instead of 
one.

One nagging question is, what is T.fail for integral types? For pointers 
fine, one could be found. For chars, fine too. But for integrals I'm not 
sure that e.g. T.min or T.max is a credible fail value.


Andrei


More information about the Digitalmars-d-announce mailing list