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