On Sunday, September 22, 2013 18:15:08 Daniel Davidson wrote: > In this thread > (http://forum.dlang.org/thread/uvhwkgljavskqfueq...@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