Thread overview
Auto objects and scope
Nov 17, 2006
genie
Nov 17, 2006
Bill Baxter
Nov 17, 2006
genie
Nov 17, 2006
Boris Kolar
November 17, 2006
Forgive my ignorance, guys, but there is something in GC for D I can't spin my head about - look at this bit of code:

Test tst=new Test;

...
if(b)
{
   auto Test t1=new Test;
   ...
   tst=t1;
}
...
tst.func();

auto modifier forces the object to be deleted even though it was assigned to something else before leaving the scope - is this behaviour correct?

Gene
November 17, 2006
genie wrote:
> Forgive my ignorance, guys, but there is something in GC for D I
> can't spin my head about - look at this bit of code:
> 
> Test tst=new Test;
> 
> ...
> if(b)
> {
>    auto Test t1=new Test;
>    ...
>    tst=t1;
> }
> ...
> tst.func();
> 
> auto modifier forces the object to be deleted even though it was
> assigned to something else before leaving the scope - is this
> behaviour correct?
> 
> Gene

Yep.  auto (now 'scope') is not a reference counter.  It just says delete this one variable when this one variable goes out of scope.

I think it's more or less equivalent to "scope(exit) delete t1;".

In fact it will merrily delete whatever t1 happens to point to at the end of the scope:
---------------------------
import std.stdio : writefln;

class C {
    this(char[]n) { name=n; }
    ~this() { writefln("Bye bye: ", name); }

    char [] name;
}

void main()
{
    auto a = new C("a");
    {
        auto scope b = new C("b");

        b=a;
        writefln("Leaving scope...");
    }
    writefln("Left scope.");

    writefln("Ending main");
}
/*
Output:
Leaving scope...
Bye bye: a
Left scope.
Ending main
Bye bye: b
*/

a gets deleted at the end of the scope instead of b, and b only gets cleaned up later by the final garbage collection sweep.

So it seems it really is just syntactic sugar for the scope(exit) statement.

I think some sort of reference-counting construct is needed for the type of cases your example illustrates.  For things like File objects, you want to be able to return them, but you want them to be cleaned up immediately when no longer in use.  'scope' is too limited I think.

I'm not sure how to do it though.  It's not clear how to do in a clean way as a library (like a shared_ptr!()) without an overloadable opAssign.


--bb

November 17, 2006
genie wrote:
> Forgive my ignorance, guys, but there is something in GC for D I
> can't spin my head about - look at this bit of code:
> 
> Test tst=new Test;
> 
> ....
> if(b)
> {
>    auto Test t1=new Test;
>    ...
>    tst=t1;
> }
> ....
> tst.func();
> 
> auto modifier forces the object to be deleted even though it was
> assigned to something else before leaving the scope - is this
> behaviour correct?
> 
> Gene

Bill gave you a pretty complete answer, but I just wanted to point out you could do this to prevent the deletion:

# if (b) {
#   scope Test t1 = new Test;
#   // ...
#   tst = t1;
#   t1 = null; // <-- this will prevent the deletion!
# }

-- Chris Nicholson-Sauls
November 17, 2006
Thanks, Bill - that's the answer I was looking for. I assumed that some kind of a reference counting was used in D for looking after the state of the objects.

Gene
November 17, 2006
== Quote from Bill Baxter (dnewsgroup@billbaxter.com)'s article
> I think some sort of reference-counting construct is needed for the type
> of cases your example illustrates.  For things like File objects, you
> want to be able to return them, but you want them to be cleaned up
> immediately when no longer in use.  'scope' is too limited I think.
> I'm not sure how to do it though.  It's not clear how to do in a clean
> way as a library (like a shared_ptr!()) without an overloadable opAssign.
> --bb

I agree completely. Here is a different solution for a similar problem: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=44163

Ideally, even cases like this should have deterministic finalization:

  void test() {
    getFile().read(buffer);
    // the File instance returned by getFile is destroyed here
    // (assuming it's not used outside this scope, of course)
  }

The easiest way IMHO is intruduction of value classes (which are immutable) and using reference counting for deterministic finalization. Note that (immutable) value classes can never form cyclic references, so this technique would work well.

Potentially, value classes offer additional benefits by providing better opportunities for compile-time optimization (multithreading, caching,...).