www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why will the delete keyword be removed?

reply Trass3r <un known.com> writes:
Does it really add so much complexity to the compiler that it's worth  
removing?
Jul 13 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/13/2010 03:51 PM, Trass3r wrote:
 Does it really add so much complexity to the compiler that it's worth
 removing?
It adds misconception and confusion. delete must disappear from D. Andrei
Jul 13 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei:
 It adds misconception and confusion. delete must disappear from D.
Is this case important? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=113319 Bye, bearophile
Jul 13 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/13/2010 05:15 PM, bearophile wrote:
 Andrei:
 It adds misconception and confusion. delete must disappear from D.
Is this case important? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=113319 Bye, bearophile
I don't understand what you mean by "this case". All I'm saying is simply that all structures and algorithms of GCs are optimized for automatic bulk collections and all structures and algorithms of manual allocators are optimized for manual frees. So it is definitely not reasonable to ask one to do the job of the other. druntime's current allocator does offer a reasonably effective method of manual deallocation, but that risks at creating confusion among users who might think it's reasonable to expect that any later GC for D would have such a method. The reality is that D's current GC is rather old technology and we definitely must expect that any state-of-the-art GC added to D cannot honor manual deallocation requests. Andrei
Jul 13 2010
prev sibling next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wed, 14 Jul 2010 00:50:39 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 It adds misconception and confusion. delete must disappear from D.
Such answers only leave more questions open, and won't get you much street cred :) May I suggest writing an article (or just an NG post - something you can link to for all such future questions) detailing why delete was bad, why getting rid of it was good, and if you actually used delete, what should you now use instead of it, for all use cases? -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 13 2010
parent Trass3r <un known.com> writes:
 Such answers only leave more questions open, and won't get you much  
 street cred :) May I suggest writing an article (or just an NG post -  
 something you can link to for all such future questions) detailing why  
 delete was bad, why getting rid of it was good, and if you actually used  
 delete, what should you now use instead of it, for all use cases?
Yep, might be worth an article.
Jul 13 2010
prev sibling parent reply Max Samukha <spambox d-coding.com> writes:
On 07/14/2010 12:50 AM, Andrei Alexandrescu wrote:
 On 07/13/2010 03:51 PM, Trass3r wrote:
 Does it really add so much complexity to the compiler that it's worth
 removing?
It adds misconception and confusion. delete must disappear from D. Andrei
FWIW, the current implementation of clear() has at least one problem that guarantees my not using it: the default constructor may be arbitrarily complex (for example, it may allocate an expensive resource), so running it on the dead object is not acceptable. Also, as other people mentioned, resurrecting the dead object prevents the program from failing early in case a dangling pointer to that object gets dereferenced. I'd probably want clear() to run the destructor and zero the object's memory. This way the objects the dead object may reference can be GCed even in the presence of dangling pointers the dead object. Additionally, the probability of the program failing early on a dangling pointer dereference is high because the vtable pointer is null (any virtual call is bound to fail). Or something like that.
Jul 14 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/14/2010 02:38 PM, Max Samukha wrote:
 On 07/14/2010 12:50 AM, Andrei Alexandrescu wrote:
 On 07/13/2010 03:51 PM, Trass3r wrote:
 Does it really add so much complexity to the compiler that it's worth
 removing?
It adds misconception and confusion. delete must disappear from D. Andrei
FWIW, the current implementation of clear() has at least one problem that guarantees my not using it: the default constructor may be arbitrarily complex (for example, it may allocate an expensive resource), so running it on the dead object is not acceptable.
You mean class objects, right? I agree. I think it's okay to fill the object with its stateless .init members, which would assuage the issue.
 Also, as other people mentioned, resurrecting the dead object prevents
 the program from failing early in case a dangling pointer to that object
 gets dereferenced.
There is no early failure with dangling pointers.
 I'd probably want clear() to run the destructor and zero the object's
 memory.
No, because: class A { int x = -1; float y = 0; ... } You'd want that guy to be obliterated with the proper initializers for x and y.
 This way the objects the dead object may reference can be GCed
 even in the presence of dangling pointers the dead object. Additionally,
 the probability of the program failing early on a dangling pointer
 dereference is high because the vtable pointer is null (any virtual call
 is bound to fail).

 Or something like that.
I think the vtable could remain valid. Andrei
Jul 14 2010
next sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wed, 14 Jul 2010 22:43:00 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 You mean class objects, right? I agree. I think it's okay to fill the  
 object with its stateless .init members, which would assuage the issue.
Then that object may be in an invalid state, in cases when valid states are created by the constructor. Also, what about classes which don't have a default constructor? It is my understanding that you are trying to add something to the language which would destroy an object without deallocating it (and deprecate everything that involves on-the-spot deallocation), in order to allow creating simpler and more efficient garbage collectors. The only correct solution seems to be to call the destructor, and mark the object instance as invalid. The release version needn't do anything, the debug version can stomp on the object's memory to make sure that any code that attempts to access the object crashes quickly. (This is practically the same as deallocation, as far as the user can see - the difference lies in that the GC doesn't do anything immediately.) -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 14 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/14/2010 04:08 PM, Vladimir Panteleev wrote:
 On Wed, 14 Jul 2010 22:43:00 +0300, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 You mean class objects, right? I agree. I think it's okay to fill the
 object with its stateless .init members, which would assuage the issue.
Then that object may be in an invalid state, in cases when valid states are created by the constructor.
That is correct. This is an unavoidable consequence. It doesn't have anything to do with the existence or absence of delete. The notion of "zombie objects" that have been disposed of any resources yet still exist to avoid dangling pointers has been widely discussed in various circles. D handles the matter better than all languages I know.
 Also, what about classes which don't have a default constructor?
All classes have a state where all members are default initialized.
 It is my understanding that you are trying to add something to the
 language which would destroy an object without deallocating it (and
 deprecate everything that involves on-the-spot deallocation), in order
 to allow creating simpler and more efficient garbage collectors.
I'm not adding anything. I am removing a mistake that C++ made (i.e. conflating destruction with deallocation). And the purpose is not to assuage or help GCs. The only purpose is memory safety.
 The
 only correct solution seems to be to call the destructor, and mark the
 object instance as invalid. The release version needn't do anything, the
 debug version can stomp on the object's memory to make sure that any
 code that attempts to access the object crashes quickly. (This is
 practically the same as deallocation, as far as the user can see - the
 difference lies in that the GC doesn't do anything immediately.)
For safety there's no debug and release. It's either safe or unsafe. Adding a Boolean to each object to tell whether it was destroyed or not complicates matters without bringing a palpable benefit. Andrei
Jul 14 2010
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Andrei Alexandrescu wrote:

 Also, what about classes which don't have a default constructor?
All classes have a state where all members are default initialized.
Should that state deserve a default destructor?
 I'm not adding anything. I am removing a mistake that C++ made (i.e.
 conflating destruction with deallocation)
That makes perfect sense to me. One thing that is troubling is the fact that the destructor runs twice. Is that not important? Could double destructor call be avoided if the object received a 'default destructor' upon clear(), which is not what the programmer wrote? The reason is, the programmer's destructor has already been executed when clear() is called anyway. If the system puts the object into the .init state, shouldn't it also decide what 'default destructor' could safely be run? Perhaps empty for a class consisting merely of fundamental types... Also, how about un-branding the object upon clear()? Would that solve the double destructor call? Ali
Jul 14 2010
prev sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 15 Jul 2010 00:33:15 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 All classes have a state where all members are default initialized.
How is that state normally reached (for classes without a default constructor)?
 It is my understanding that you are trying to add something to the
 language which would destroy an object without deallocating it (and
 deprecate everything that involves on-the-spot deallocation), in order
 to allow creating simpler and more efficient garbage collectors.
I'm not adding anything. I am removing a mistake that C++ made (i.e. conflating destruction with deallocation). And the purpose is not to assuage or help GCs. The only purpose is memory safety.
 The
 only correct solution seems to be to call the destructor, and mark the
 object instance as invalid. The release version needn't do anything, the
 debug version can stomp on the object's memory to make sure that any
 code that attempts to access the object crashes quickly. (This is
 practically the same as deallocation, as far as the user can see - the
 difference lies in that the GC doesn't do anything immediately.)
For safety there's no debug and release. It's either safe or unsafe.
So your goal is to prevent new objects from being allocated at the same address for the sake of memory safety? If so, then something like this belongs in SafeD *only*, in my opinion. We have tools (stomping, valgrind, etc.) to allow catching bugs like these. "Memory safety", especially at the cost of performance, does not belong in a compiled language that allows pointer arithmetics.
 Adding a Boolean to each object to tell whether it was destroyed or not  
 complicates matters without bringing a palpable benefit.
I meant "mark the object instance as invalid" figuratively, not using a flag anywhere. ("Marking" would mean stomping on the object in the debug version to prevent further use of the object.) Now I understand that your goal is not to allow programmers to catch bugs, it's to stop the programmers from doing anything that might involve shooting themselves in the foot. Your approach has the following problems: 1) As discussed, the objects are left in an abnormal state (T.init without any work done by constructors). I don't think you can construct such objects normally. 2) When an object was deallocated via "delete", assuming it didn't have a destructor that accessed the object instance, the object's memory was not being accessed. This means that if an object was swapped out, it wouldn't need to be swapped back in just to destroy it. Your solution undoes this. (Does performance actually count as a factor at all in your design decisions?) However, I also noticed that your approach has an interesting advantage: after an object's memory is clobbered with T.init, it can be added to a free list for fast construction (when the GC doesn't detect any references if you want safety). -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 14 2010
parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, July 14, 2010 16:28:40 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 00:33:15 +0300, Andrei Alexandrescu
 
 <SeeWebsiteForEmail erdani.org> wrote:
 All classes have a state where all members are default initialized.
How is that state normally reached (for classes without a default constructor)?
It's the state that the object is in before the constructor is called. All of the object's members are initialized to their default value or whatever value you assigned to them at their point of declaration (which must be a value which can be determined at compile time). It's not necessarily a valid state for the object, logically speaking (with regards to invariants and the like), but it's a safe state memory-wise. - Jonathan M Davis
Jul 14 2010
parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 15 Jul 2010 02:46:22 +0300, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Wednesday, July 14, 2010 16:28:40 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 00:33:15 +0300, Andrei Alexandrescu

 <SeeWebsiteForEmail erdani.org> wrote:
 All classes have a state where all members are default initialized.
How is that state normally reached (for classes without a default constructor)?
It's the state that the object is in before the constructor is called. All of the object's members are initialized to their default value or whatever value you assigned to them at their point of declaration (which must be a value which can be determined at compile time). It's not necessarily a valid state for the object, logically speaking (with regards to invariants and the like), but it's a safe state memory-wise.
That was obvious and not what I really asked, but thanks anyway :) -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 14 2010
parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, July 14, 2010 16:52:24 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 02:46:22 +0300, Jonathan M Davis
 
 <jmdavisprog gmail.com> wrote:
 On Wednesday, July 14, 2010 16:28:40 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 00:33:15 +0300, Andrei Alexandrescu
 
 <SeeWebsiteForEmail erdani.org> wrote:
 All classes have a state where all members are default initialized.
How is that state normally reached (for classes without a default constructor)?
It's the state that the object is in before the constructor is called. All of the object's members are initialized to their default value or whatever value you assigned to them at their point of declaration (which must be a value which can be determined at compile time). It's not necessarily a valid state for the object, logically speaking (with regards to invariants and the like), but it's a safe state memory-wise.
That was obvious and not what I really asked, but thanks anyway :)
Well, then I'm afraid that I don't get what you were asking, because that's what it sounded like you were asking. - Jonathan M Davis
Jul 14 2010
parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 15 Jul 2010 03:05:34 +0300, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Wednesday, July 14, 2010 16:52:24 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 02:46:22 +0300, Jonathan M Davis

 <jmdavisprog gmail.com> wrote:
 On Wednesday, July 14, 2010 16:28:40 Vladimir Panteleev wrote:
 On Thu, 15 Jul 2010 00:33:15 +0300, Andrei Alexandrescu

 <SeeWebsiteForEmail erdani.org> wrote:
 All classes have a state where all members are default initialized.
How is that state normally reached (for classes without a default constructor)?
It's the state that the object is in before the constructor is called. All of the object's members are initialized to their default value or
whatever
 value
 you assigned to them at their point of declaration (which must be a
 value which
 can be determined at compile time). It's not necessarily a valid state
 for the
 object, logically speaking (with regards to invariants and the like),
 but it's a
 safe state memory-wise.
That was obvious and not what I really asked, but thanks anyway :)
Well, then I'm afraid that I don't get what you were asking, because that's what it sounded like you were asking. - Jonathan M Davis
By "reaching that state" I meant a state as would be visible by code other than the object constructor or destructor. As Michel said, Andrei's proposal leaves the object in a state where its invariants could fail. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 14 2010
parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Wednesday, July 14, 2010 17:42:21 Vladimir Panteleev wrote:
 By "reaching that state" I meant a state as would be visible by code other
 than the object constructor or destructor. As Michel said, Andrei's
 proposal leaves the object in a state where its invariants could fail.
Ah, okay. That state could indeed be invalid from the perspective of the code logic. It is, however, defined. So, it's a lot like floats being NAN after an error. It shouldn't happen, but if it does, the result is well-defined. The main issue, however, is how this state is seen. It's not flagged in any way, so it's not necessarily clear when an error occurs that it's due to the object having been cleared rather than it being a valid object in an unexpected state. At least with NAN, it's pretty clear that you have an error due to an operation resulting in a NAN, and you can track it down. With things being put in the pre- constructor state by clear(), it's not as obvious what's going on when things go wrong. Ideally, you'd want things to blow up when such an object was used, with it clearly indicating that it was because you used an object which isn't supposed to exist anymore. So, clear() does result in a nice, defined state - which is far better than undefined behavior - but it's not a state where tracking down the error is necessarily going to be at all obvious or simple. So, it's a step in the right direction, but I'm not sure that it's enough. - Jonathan M Davis
Jul 14 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:
 With things being put in the pre-
 constructor state by clear(), it's not as obvious what's going on when things
go 
 wrong. Ideally, you'd want things to blow up when such an object was used,
with 
 it clearly indicating that it was because you used an object which isn't 
 supposed to exist anymore.
If you want the "failfast" (http://en.wikipedia.org/wiki/Failfast ) system, then you can use the last bit of the monitor or virtual table pointer to store store a boolean that denotes the cleared state of the object. This free space is not available for structs allocated on the heap, dynamic arrays, etc, and then you have to test this bit (in nonrelease mode only?) often, this slows down code a little. Bye, bearophile
Jul 14 2010
prev sibling parent reply "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 15 Jul 2010 04:00:49 +0300, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 Ideally, you'd want things to blow up when such an object was used, with
 it clearly indicating that it was because you used an object which isn't
 supposed to exist anymore.
I suggested this as well, by stomping on the object's memory in debug builds. Andrei has different goals. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 15 2010
parent reply "Rory McGuire" <rmcguire neonova.co.za> writes:
On Thu, 15 Jul 2010 09:08:24 +0200, Vladimir Panteleev  
<vladimir thecybershadow.net> wrote:

 On Thu, 15 Jul 2010 04:00:49 +0300, Jonathan M Davis  
 <jmdavisprog gmail.com> wrote:

 Ideally, you'd want things to blow up when such an object was used, with
 it clearly indicating that it was because you used an object which isn't
 supposed to exist anymore.
I suggested this as well, by stomping on the object's memory in debug builds. Andrei has different goals.
Surely you can't just stomp on the memory? You'd have to keep it allocated so nothing else ends up being allocated there, and you get weird inconsistent errors, debug mode or not.
Jul 15 2010
parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thu, 15 Jul 2010 11:03:07 +0300, Rory McGuire <rmcguire neonova.co.za>  
wrote:

 On Thu, 15 Jul 2010 09:08:24 +0200, Vladimir Panteleev  
 <vladimir thecybershadow.net> wrote:

 On Thu, 15 Jul 2010 04:00:49 +0300, Jonathan M Davis  
 <jmdavisprog gmail.com> wrote:

 Ideally, you'd want things to blow up when such an object was used,  
 with
 it clearly indicating that it was because you used an object which  
 isn't
 supposed to exist anymore.
I suggested this as well, by stomping on the object's memory in debug builds. Andrei has different goals.
Surely you can't just stomp on the memory? You'd have to keep it allocated so nothing else ends up being allocated there, and you get weird inconsistent errors, debug mode or not.
If you want to keep the stomped-on object allocated to prevent that, there is no problem doing it - just don't do immediate deallocation in debug builds, and let the GC collect the object when it sees no references. However, if you want to catch dangling pointer bugs with absolute certainty, the best solution is to use tools such as Valgrind, which keep track of which memory is safe to read, etc. -- Best regards, Vladimir mailto:vladimir thecybershadow.net
Jul 15 2010
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-07-14 15:43:00 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 You mean class objects, right? I agree. I think it's okay to fill the 
 object with its stateless .init members, which would assuage the issue.
That risks breaking the object's invariants. Next thing you know you'll get an assertion error in the object's invariants and will look inside your object wondering how its private state could ever have gone like that. What really happened is that somewhere far far away clear() will have been called, putting your object in an invalid state. Try to debug that! In my opinion, if you need to clear an object's state, the object itself should be designed with that in mind (probably offering a clear() method). The object should be the one to decide if it can become zombie or not, not the user. clear() is memory safe (unlike delete), but beyond that it doesn't look much less dangerous to use to me. I'm also doubtful it won't violate immutability in the presence of immutable members. Take a look at this problem for instance: class A { immutable int i; this(int i) { this.i = i; } } A a = new A(10); immutable int* i = &a.i; assert(*i == 10); clear(a); assert(*i == 10); // i is immutable, isn't it? Be sure to try it with a struct too. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 14 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 07/14/2010 04:47 PM, Michel Fortin wrote:
 On 2010-07-14 15:43:00 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> said:

 You mean class objects, right? I agree. I think it's okay to fill the
 object with its stateless .init members, which would assuage the issue.
That risks breaking the object's invariants. Next thing you know you'll get an assertion error in the object's invariants and will look inside your object wondering how its private state could ever have gone like that. What really happened is that somewhere far far away clear() will have been called, putting your object in an invalid state. Try to debug that!
I think you're not properly balancing the advantages and the disadvantages of the system.
 In my opinion, if you need to clear an object's state, the object itself
 should be designed with that in mind (probably offering a clear()
 method). The object should be the one to decide if it can become zombie
 or not, not the user.
That disables any guarantee that could be reliably made.
 clear() is memory safe (unlike delete), but beyond that it doesn't look
 much less dangerous to use to me. I'm also doubtful it won't violate
 immutability in the presence of immutable members. Take a look at this
 problem for instance:

 class A {
 immutable int i;

 this(int i) {
 this.i = i;
 }
 }

 A a = new A(10);
 immutable int* i = &a.i;
 assert(*i == 10);
 clear(a);
 assert(*i == 10); // i is immutable, isn't it?

 Be sure to try it with a struct too.
Good point. Andrei
Jul 14 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-07-14 17:51:27 -0400, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:

 In my opinion, if you need to clear an object's state, the object itself
 should be designed with that in mind (probably offering a clear()
 method). The object should be the one to decide if it can become zombie
 or not, not the user.
That disables any guarantee that could be reliably made.
(Be sure to read the conclusion at the end before starting a reply.) It might disable guaranties as to how clear() behaves, but a global clear() that works with everything breaks guarenties (invariants) an object normally offer. A broken invariant normally indicates a broken object. If clear enters the picture, a broken invariant can mean anything. I'm trying to think of the ramifications with some of my code for examples. Calling clear() on most objects that passes through the D/Objective-C bridge is going to cause a crash. This is because upon construction, those objects keep a pointer to the corresponding Objective-C object. clear() would essentially make that pointer null, as well as reduce the reference count on the Objective-C object. That's "fine", until you call a method. Such a method assume the pointer is not null, and you'll get a segfault. Now, imagine that somewhere in the Cooca framework your object is retained and a call to one of its functions is made when a user click somewhere... it could happen 10 minutes after you called clear(). If clear() was to call the default constructor after calling the constructor, that'd be more acceptable, but even there the behaviour would be quite strange in my case because a D object could become associated with two Objective-C objects (the one before the clear that still thinks it's D counterpart exists, and the new one created by the constructor). So even by calling the destructor *and* the default constructor you could end up breaking invariants of the whole system. Conclusion: I'm not really against having a clear() function somewhere, but users of this function should be aware that it might causes problems. I'd put it in the same veins as calling the destructor manually, with the interesting addition to that is that it is memory safe. It might break the immutability of immutable members, might break any system that has some expectations about the lifetime of an object, and might breaks object invariants. Clearly, it's no different than calling the destructor, except that it is memory safe (which is certainly a desirable feature). Advertise it as low-level runtime-level function that is memory-safe and we'll all be fine. (Perhaps it belongs in druntime.) -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 14 2010
prev sibling parent reply Max Samukha <spambox d-coding.com> writes:
On 07/14/2010 10:43 PM, Andrei Alexandrescu wrote:

 There is no early failure with dangling pointers.
There is: class A { void foo() {} } class B : A { override void foo() {} } A a = new B; A a2 = a; clear(a); a2.foo(); If you reset the object state to .init, foo will succeed and the program will happily crawl on. If you zero out the object's memory, the call to foo will fail. I'd prefer the latter. The same with an interface: interface I { void foo(); } class A : I { void foo() {} } void main() { A a = new A; I i = a; clear(a); i.foo(); // would segfault } One more (rare but possible): void bar() { } class A { void function() p = &bar; void foo() { p(); } } void main() { A a = new A; A a2 = a; clear(a); a2.foo; // would segfault }
 I'd probably want clear() to run the destructor and zero the object's
 memory.
No, because: class A { int x = -1; float y = 0; ... } You'd want that guy to be obliterated with the proper initializers for x and y.
Not that I fiercely disagree but ideally I'd want it to be obliterated to an invalid but easily recognizable state. It might help to discover dangling pointer errors early. Otherwise, leaving a destroyed object in a perfectly valid state may make debugging more fun than it needs to be. In my world, clear() would run the dispose handlers, unset the memory block's BlkAttr.FINALIZE (if the object is on GC heap), call the object's destructor, destroy the monitor and zero the memory. I guess a call to rt_finalize could be used as part of this procedure. Or something like that.
Jul 15 2010
parent PercentEwe <make example.org> writes:
== Quote from Max Samukha (spambox d-coding.com)'s article
 Not that I fiercely disagree but ideally I'd want it to be obliterated
 to an invalid but easily recognizable state. It might help to discover
 dangling pointer errors early. Otherwise, leaving a destroyed object in
 a perfectly valid state may make debugging more fun than it needs to be.
You could use: class A { int guard = 0xdeadbeef; this () { guard = 0; } invariant () { assert (guard == 0); } } Really annoying, though, and it makes more sense to set the guard in the destructor. (A compiler option to do something like this automatically would be nice.)
Jul 15 2010