Someone said without ref-counting or borrow-check it is not possible to create a @safe swap function.
I think this is not true, so I propose that phobos should provide the following swap-function which is usable in @safe code:
/// Generic swap function without temporary variables
/// Types with indirections need to define their own specific swap functions.
void swap(T)(ref T x, ref T y) @trusted if(is(typeof(x ^= y)) || !hasIndirections!T || isPointer!T || isArray!T)
{
static if(is(typeof(x ^= y))) { x ^= y; y ^= x; x ^= y; } // numeric types
else // flat structs, simple pointers and arrays
{
ubyte[] ax = (cast(ubyte*)&x)[0 .. T.sizeof];
ubyte[] ay = (cast(ubyte*)&y)[0 .. T.sizeof];
ax[] ^= ay[]; ay[] ^= ax[]; ax[] ^= ay[];
}
}
@system unittest
{
// type with xor operator
int a = 100;
int b = -123_456;
swap(a,b);
assert(a == -123_456);
assert(b == 100);
// basic array
int[] c = [5,4,3,2,1];
int[] d = [6,6,6,6];
swap(c,d);
assert(c == [6,6,6,6]);
assert(d == [5,4,3,2,1]);
// basic pointer (maybe to different types)
void* e = &a;
void* f = &c;
swap(e,f);
assert(e == &c);
assert(f == &a);
// flat struct with gaps
struct F
{
ubyte x;
short y;
int z;
}
assert(!hasIndirections!F);
assert(F.sizeof == 8); // not 7
F p = F(100, -3, int.min);
F q = F(0, 0, -1);
(cast(byte*)&q)[1] = 2; // fill the gap with some garbage
swap(p,q);
assert(p.x==0 && p.y==0 && p.z==-1);
assert(q.x==100 && q.y==-3 && q.z==int.min);
assert((cast(byte*)&p)[1] == 2); // the garbage was swapped together with the data
assert(!(p is F(0, 0, -1))); // memcmp() checks also the garbage in the gaps to be equal
// assert(p != F(0, 0, -1)); // with -preview=fieldwise the default comparison is fixed
assert(q == F(100, -3, int.min));
}