Overzealous immutable and classes
Jonathan M Davis
jmdavisprog at gmail.com
Thu Jul 15 18:08:14 PDT 2010
On Thursday, July 15, 2010 17:40:26 Gareth Charnock wrote:
> So having got a collectors' edition TDPL, I though I'd have a try at
> writing some concurrent code. The idea was a worker thread(s) would do
> some work and write the results to some immutable objects. These would
> get passed to an indexer thread that would do neat stuff like indexing
> the references to the objects in things like hash tables and such,
> performing any reduce type operations along the way. These views would
> then be passed back to workers in due course. Unfortunately I hit a
> snag. It appears you can't do this:
>
> class A {
> ...
> }
> immutable A a1 = new immutable(A); //pointer to a real, immutable object
> immutable A a2; //pointer to null
> a2 = a1; //error, guess I doomed a2 to a life of being useless
>
> I thought that as such assignments don't change the underlying objects
> in memory, they would be fine. Compare to:
>
> immutable(char)[] s1 = "hello world"; //fat pointer to a section of
> immutable memory
> immutable(char)[] s2; //fat pointer to null
> s2 = s1; //fine
>
> The only solutions I can think of:
> 1) Use a pointer to a class. Works, but that just seems very unsafe and
> just plain un-D-ish. We also have to dereference two pointers.
> 2) Work entirely with immutable(A)[], which is not quite as crazy as it
> seems as we are copying about arrays of pointers rather than arrays of
> A. It's still quite crazy.
> 3) Write some sort of wrapper struct to hide the pointer.
> 4) Perhaps ref works like C++ int&?
> ref immutable(A) ref_a //error
> Nope.
>
> So is this intended behavior? Am I missing something obvious?
First off, you're using using a reference, not a pointer. They're similar but
quite different. If you were using a pointer, you could do
immutable (A)* a1;
and the object would be immutable while the pointer would be mutable. It's a
mutable pointer to an immutable object of type A. The problem with a reference
is that it has no syntactic way to differentiate between what's doing the
refering and what's being referred. Because immutable is transitive,
immutable A a1;
is an immutable reference to an immutable object of type A. As soon as you try
and make a reference immutable, what it refers to is immutable as well. There is
no syntactic way to fix the problem. The solution is Rebindable!(T) in
std.typecons.
Rebindable!(T) is a wrapper struct. It allows you to have const and immutable
references and still change what they refer to. Take this example from the docs:
// Rebindable references to const and immutable objects
void bar()
{
const w1 = new Widget, w2 = new Widget;
w1.foo();
// w1 = w2 would not work; can't rebind const object
auto r = Rebindable!(const Widget)(w1);
// invoke method as if r were a Widget object
r.foo();
// rebind r to refer to another object
r = w2;
}
By wrapping the const (or immutable) Widget, you can "rebind" what is "bound" by
Rebindable!(T) and effectively get a mutable reference to a const or immutable
object. It's not terribly pretty, but apparently no one could come up with a
satistfactory way of doing it in the language itself given the syntax for
references. So, Rebindable!(T) is the solution.
- Jonathan M Davis
More information about the Digitalmars-d-learn
mailing list