const and immutable members

Jonathan M Davis jmdavisProg at gmx.com
Sun Sep 22 13:16:52 PDT 2013


On Sunday, September 22, 2013 18:15:08 Daniel Davidson wrote:
> In this thread
> (http://forum.dlang.org/thread/uvhwkgljavskqfueqyxo@forum.dlang.org)
> 
> I asked this:
> >> 3) Also, is storing immutable(STUFF) in a struct in the general
> >> case (as opposed to just this one) useful or silly?
> 
> Johnathan M Davis replied:
> > As soon as you have a const or immutable member in a
> > struct, you can't ever assign anything to it, even if
> > all of its other members are mutable (you could
> > assign the individual, mutable members but not the
> > whole struct). That means stuff like if the struct is
> > ever default-initialized, you can't even give it
> > another value, which tends to mean that you can't use
> > such a struct in places like arrays.
> > 
> > All in all, I think that it's pretty much always a
> > bad idea to have a struct with const or immutable
> > members. It's just begging for problems. Tail-const
> > or tail-immutable is fine, because the members
> > themselves aren't const or immutable (just what they
> > refer to), but having them be directly const or
> > immutable is a bad idea IMHO.
> 
> I don't really understand the _tail-const_, _tail-immutable_
> argument. Why is _tail-const_ fine but not _const_ when there is
> transitivity anyway?

If you have

struct S
{
    immutable int[] arr;
}

then arr can never be assigned to, so a variable of type S can never be 
assigned to. But if you have

struct S
{
    immutable(int)[] arr;
}

then arr can be reassigned as much as you'd like, so S can be assigned to. In 
both cases, the elements of the array are immutable, so it can freely be a 
slice of another array and not care or affect that other array, but the array 
variable itself - which only exists local to the struct - is not restricted in 
being assigned to. So, you get the immutability of the data without 
restricting the struct. All making arr itself immutable does (rather than 
tail-immutable) is make it so that you can't reassign arr, which can be useful 
sometimes, but in the case of a struct, it makes it so that the whole struct 
can't be reassigned, so it's pretty much never a good idea IMHO to have a 
struct with const or immutable members - but having them be tail-const or 
tail-immutable still makes it so that what they refer to gets all of the 
benefits of const or immutable without restricting the struct.

If you're dealing with a class, then the situation is a bit different, because 
the class is always on the heap, and you don't normally assign to classes. You 
reassign the reference that refers to them or you assign to their member 
variables, but you don't assign to the block of data that is the class itself 
like you would with a struct. So, making a class' member variable const or 
immutable does not restrict the class, which means that if you want to make it 
so that the member variable can't be reassigned, making it fully const or 
fully immutable is fine.

> I think there are only a handful of places you consider using
> const/immutable:
> 
> - Global variables
> - Local variables
> - Member variables
> - Function signatures
> 
> Are there any missing?

You can use const and immutable anywhere that you declare a variable or member 
function.

> If right out of the gate the feeling is member variables should
> not be const or immutable, doesn't that greatly reduce the value
> of the mutability specificiers? Members are used to hold onto the
> data for the lifecycle of the object and if those should not make
> claims or guarantees on mutability then that seems a shame.

It's only a problem to make members fully const or immutable with structs, 
because then you can't reassign the struct, which does nasty things like make 
it so that you can't put them in arrays unless you want all the elements of 
the array to have the init value for that struct or you want to append each 
element individually (which won't work with static arrays).

Also, the benefits of sharing data are only gained when that data is on the 
heap, in which case you can just make the data const or immutable without 
making the member variable in the struct const or immutable - you make it 
tail-const or tail-immutable rather than const or immutable.

If the data were directly in the struct (i.e. the member variable is a value 
type), then the only way to share it would be via a pointer or via ref. ref 
doesn't really matter, since you can make that const, and it only refers to 
the data until the function call has completed. And pointers don't matter, 
because you can't actually rely on a pointer to a struct's member variable 
staying valid longer than ref would have anyway, because structs can be moved 
rather than copied. So, sharing via the heap is the only viable way, and if 
you do that, you can use tail-const or tail-immutable for all of the same 
benefits that making the member variable fully const or immutable would have 
given you (since you're sharing the data, not the variable).

And again, the problems with making a member variable fully const or immutable 
lie with structs, because they sit directly on the stack or directly in an 
array, whereas classes sit on the heap and are only referred to via a 
reference.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list