Skip to content

Instantly share code, notes, and snippets.

@John-Colvin
Last active November 12, 2019 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save John-Colvin/c9c0b79bc9d47ed8d57ec9e959d0542b to your computer and use it in GitHub Desktop.
Save John-Colvin/c9c0b79bc9d47ed8d57ec9e959d0542b to your computer and use it in GitHub Desktop.

The DIP provides 2 examples to justify itself:

  1. unpredictable mutable aliasing after appends

  2. unpredictable changes of ownership caused by appends

They can share the same cause (~=), but are two distinct symptoms, and can also have other causes. They have a unifying aspect which is unpredictable aliasing.

The breakage of the DIP is immense, so there needs to be a commensurately large upside and no reasonable alternative path with less breakage.

Let's consider the first example:

int[] slice = cast(int*)malloc(10 * int.sizeof)[0 .. 10];
slice ~= 1;
free(slice.ptr); // Oops!

free is not an @safe operation and requires some conditions to be met for correct use. These conditions are as follows:

  • free happens after all accesses to the allocation referenced by the passed pointer.
  • the allocation referenced by the passed pointer was made by {m,c,re}alloc

as such, you can't really ever call free without completely trusting the path by which the data travelled, from inception ({m,c,re}alloc) to the call to free. This may be achieved by a variety of ways (e.g. a MallocSlice type that encapsulates the slice, or by having the path be very short and doing it manually) but fundamentally if I write a function taking a slice and then freeing the pointer, I cannot mark it as @trusted, regardless of whether ~= is allowed or not.

That ~= will cause a new array to be allocated unpredictably is not the problem, the problem is that it can happen at all. If we look at it probabilistically: the problem is not that the probability of re-allocation is in (0, 1), the problem is that it's not 0. Someone can write slice = slice ~ 1 and get the same behaviour, so my considerations when calling free have not changed by disallowing ~=.

On to the second example:

enum { dead, alive }
int[] cat = new int[6];
cat[5] = alive;
int[] b = cat;
b ~= 1;      // may or may not move b to new location
b[5] = dead; // indeterminate whether cat[5] is dead or alive

This is a sticky one. ~= applied to data where there are >1 references and >0 are mutable is liable to cause problems of unpredictable action at a distance. Removing ~= makes it slightly harder to do this, but seeing as it's so very easy to do the same thing in other ways (a = b ? a : (a ~ 2) and many other trivial examples) it arguably doesn't matter much. I could believe it makes it less likely to do by accident.

Conclusion:

Overall, this DIP seems to not buy anyone much, but costs a lot. It correctly identifies a problem (or 2 problems, depending how you look at it), but the proposed solution does not appear on a simple reading to solve the problem.

The DIP does not provide any examples of what is directly enabled by the change and I was not able to infer it from the current text.

The author should explain what previously impossible/unsafe code can now be made possible/safe given the proposed change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment