dsimcha wrote:
Here's my proposal for how T[new] should work:
1. It should be a reference type to be consistent with slices. Yes, slices are
kind of a hybrid, but they're more semantically similar to reference types than
value types. If you can't modify the length of a slice anymore, then for all
practical purposes it will be a reference type.
Check.
2. A T[new] should support all the same operations as a T[] with semantics as
similar as common sense will allow, including indexing, ~, .dup, .idup, slice
assign, etc. Basically, it should have, to the greatest degree possible without
defeating the purpose, the same compile time interface.
Check.
3. A T[new] should be implicitly convertible to a slice. For example:
auto foo = someFunctionThatReturnsTnew();
// foo is a T[new].
T[] bar = someFunctionThatReturnsTnew();
// Works. bar is a T[]. The T[new] went into oblivion.
This solves the problem of slices not being closed over .dup and ~.
Check.
4. It should be guaranteed that no block of memory is ever referenced by more
than one T[new] instance. This is needed to guarantee safety when appending to
immutable arrays, etc.
That doesn't go with reference semantics. Uncheck.
5. Assigning a T[new] to another T[new] should be by reference, just like
assigning a class instance to another class instance.
Check. (BTW contradicts 4)
Assigning a T[] to a T[new]
should duplicate the memory block referenced by the T[] because this is probably
the only way to guarantee (4).
No check, but could have been done.
6. Since T[new] guarantees unique access to a memory block, it should have an
assumeUnique() method that returns an immutable slice and sets the T[new]'s
reference to the memory block to null. This solves the problem of building
immutable arrays without the performance penalty of not being able to
pre-allocate
or the unsafeness of having to cowboy cast it to immutable.
Uncheck.
7. As long as the GC is conservative, there absolutely *must* be a method of
manually freeing the memory block referenced by a T[new] provided that the GC
supports this operation, though it doesn't have to be particularly pretty. In
general, since D is a systems language, T[new] should not be too opaque. A good
way to do this might be to make all of the fields of the T[new] public but
undocumented. If you *really* want to mess with it, you'll read the source code
and figure it out.
Check while delete still exists. Please use malloc for that stuff.
8. The first call to opSlice on a T[new] should set a flag that indicates that
there may be multiple pointers to the underlying memory block. Before that flag
is set, appends to a T[new] should result in calls to GC.free() to free the old
block whenever it needs to be expanded (since we can guarantee that we own it
exclusively). This will help deal with false pointer issues, since D's GC looks
like it will remain conservative for the foreseeable future.
Uncheck.
So now: every place I've said "check" means there was implementation and
book-quality illustrated documentation that Walter and I have done with
the sweat of our brow. At the end we looked at the result and concluded
we should throw away all that work.
Andrei