Is mimicking a reference type with a struct reliable?
Denis Koroskin
2korden at gmail.com
Sat Oct 16 08:52:29 PDT 2010
irst I'd like to say that I don't really like (or rather use) Appender
because it always allocates (at least an internal Data instance) even when
I provide my own buffer.
I mean, why would I use Appender if it still allocates? Okay, you have to
store a reference to an internal representation so that Appender would
feel like a reference type. I'm not sure it's worth the trade-off, and as
such I defined and use my own set of primitives that don't allocate when a
buffer is provided:
void put(T)(ref T[] array, ref size_t offset, const(T) value)
{
ensureCapacity(array, offset + 1);
array[offset++] = value;
}
void put(T)(ref T[] array, ref size_t offset, const(T)[] value)
{
// Same but for an array
}
void ensureCapacity(ref char[] array, size_t minCapacity)
{
// ...
}
And all that functions that use an optional buffer have a signature like
this:
void foo(ubyte[] buffer = null);
Back to my original question, can we mimick a reference behavior with a
struct? I thought why not until I hit this bug:
import std.array;
import std.stdio;
void append(Appender!(string) a, string s)
{
a.put(s);
}
void main()
{
Appender!(string) a;
string s = "test";
append(a, s); // <
writeln(a.data);
}
I'm passing an appender by value since it's supposed to have a reference
type behavior and passing 4 bytes by reference is an overkill.
However, the code above doesn't work for a simple reason: structs lack
default ctors. As such, an appender is initialized to null internally,
when I call append a copy of it gets initialized (lazily), but the
original one remains unchanged. Note that if you append to appender at
least once before passing by value, it will work. But that's sad. Not only
it allocates when it shouldn't, I also have to initialize it explicitly!
I think far better solution would be to make it non-copyable.
TL;DR Reference semantic mimicking with a struct without default ctors is
unreliable since you must initialize your object lazily. Moreover, you
have to check that you struct is not initialized yet every single function
call, and that's error prone and bad for code clarity and performance. I'm
opposed of that practice.
More information about the Digitalmars-d
mailing list