Overzealous immutable and classes

Gareth Charnock gareth.charnock at gmail.com
Thu Jul 15 18:22:06 PDT 2010


On 16/07/10 02:08, Jonathan M Davis wrote:
> 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

Thanks, that should work just fine. Looking at the phobos source for 
Rebindable, I was nowhere near close to reinventing that wheel.



More information about the Digitalmars-d-learn mailing list