xxxxxxxxxx
--- app.d
import tail_unqual;
void main() pure
{
// Qualifiers on a TailUnqual variable are not transitive--that is,
// they apply only to the variable itself, not the data it points to.
immutable TailUnqual!(int*) p = new int(123);
assert(*p == 123);
// We can't mutate `p` itself, because it's immutable...
assert(!__traits(compiles,
p = immutable(TailUnqual!(int*))(new int(789))
));
// ...but we can mutate the data it points to!
*p = 456;
assert(*p == 456);
}
--- tail_unqual.d
import std.traits: isPointer;
private union HiddenPointer
{
// Stores a pointer, cast to an integer
size_t address;
// Prevents undefined behavior
void* unused;
}
struct TailUnqual(P)
if (isPointer!P)
{
private HiddenPointer data;
this(P ptr) inout
{
data.address = cast(size_t) ptr;
}
P ptr() inout
{
// ok because:
// - address is always the active field of data
// - data.address was originally cast from a P
return cast(P) data.address;
}
alias ptr this;
}