Built-in RAII in D

Nerve via Digitalmars-d digitalmars-d at puremagic.com
Sun May 28 10:34:30 PDT 2017


Thanks to Walter Bright's recent comments at Dconf about memory 
safety, and my own lamentations about the continued use of C in 
all contexts where memory safety is crucial by overconfident 
programmers who believe they can do no wrong, I've decided to 
propose a baked-in RAII implementation for D. I would like to 
submit a DIP, but first I'd like to run it by the forum community 
and see what improvements need to be made, possibly due to my own 
naivety on the subject.

To qualify that, I am nowhere near an expert on memory 
management. But, I've spent enough time absorbing discussion on 
the topic through osmosis and reading on it due to the D 
community's concerns about it that I may as well make myself 
heard and be shot down if I'm wrong.

I understand if many people are resistant to building it into the 
language. Phobos already has it, and there's the automem library 
by Atila Neves. However, I think the perception shift gained by 
baking in these features will benefit D enormously. D users can 
run around all day trying to convince people that different 
library implementations of RAII exist so one need not use the GC, 
but the only thing that is going to convince the programming 
world at large is a giant announcement on Reddit, HackerNews, and 
other places that "Hey, D has RAII now!" This will also drive 
many new eyes to the language that never would have looked 
otherwise.

There are also the obvious syntactical benefits. Referencing an 
RAII object and its members would be literally no different than 
referencing a GC heap object. No need to fiddle with library 
constructs to extract one's reference.

Without further adieu, let's get started.

--- refcounted ---

Keyword "refcounted" allocates an object on the heap, and 
therefore uses an allocator. By default, a built-in malloc() 
implementation is used.

>@nogc void main()
>{
>    auto refCountedObject = refcounted Object();
>} // References go to 0, object is destroyed

The allocation method used by refcounted can be overloaded. The 
overload is a function which expects a type and a set of 
parameters that were passed to the constructor. One can allocate 
the object however they choose, call the passed constructor, and 
then the function expects a return of a reference to the 
allocated object.

Forgive my ignorance, I'm unsure how to handle a collection of 
parameters. Haven't had to do it yet.

>ref T opRefCounted(T)(params)
>{
>    T* object = malloc(sizeof(T));
>    object.this(params);
>    return ref object;
>}

opRefCounted() is ALWAYS UNSAFE SYSTEM CODE! This could manifest 
as a compiler warning whenever it is present that must be 
suppressed by a flag so the developer must acknowledge they have 
used a custom allocation scheme. There are, of course, other 
options for handling this, I'm just stating the most obvious.

--- unique ---

Keyword "unique" allocates an object on the stack. It is only 
accessible to the given scope, child scopes, or functions it is 
explicitly passed to. Therefore, it does not use an allocator.

>@nogc void main()
>{
>    auto scopedObject = unique Object();
>} // Fall out of scope, object destroyed

--- How new and GC fit in ---

Keyword "new", which allocates to the heap for the D garbage 
collector, may not be used with the @nogc attribute. Only 
refcounted and unique. No objects, functions, methods, or any 
other code within the scope of, or called from the scope of, a 
@nogc context, may allocate using new.

>@nogc void main()
>{
>    auto refCountedObject = refcounted Object(); // Okay
>    auto scopedObject = unique Object();         // Okay
>    auto tracedObject = new Object();        // Error!
>
>    {
>        auto refCountedObject = refcounted Object(); // Okay
>        auto scopedObject = unique Object();         // Okay
>        auto tracedObject = new Object();        // Error!
>    }
>}

More examples using called functions.

>void refCountedUsed()
>{
>    auto refCountedObject = refcounted Object();
>}
>
>void uniqueUsed()
>{
>    auto scopedObject = unique Object();
>}
>
>void newUsed()
>{
>    auto tracedObject = new Object();
>}

>@nogc void main()
>{
>    refCountedUsed(); // Okay
>    uniqueUsed();     // Okay
>    newUsed();        // Error!
>}

>void main()
>{
>    refCountedUsed(); // Okay
>    uniqueUsed();     // Okay
>    newUsed();        // Okay
>}

All of these methods are legal when the GC is allowed.

>void main()
>{
>    auto refCountedObject = refcounted Object(); // Okay
>    auto scopedObject = unique Object();         // Okay
>    auto tracedObject = new Object();        // Okay
>
>    {
>        auto refCountedObject = refcounted Object(); // Okay
>        auto scopedObject = unique Object();         // Okay
>        auto tracedObject = new Object();        // Okay
>    }
>}

I may be missing some things, but I've left out some exhaustive 
details since I'm sure many of you are already experienced in the 
subject and aren't looking for complete documentation in a 
proposal like this.

Feel free to level criticism, and let me know if I should submit 
a DIP.


More information about the Digitalmars-d mailing list