Const vs Mutable
13 December 2011
I keep thinking about parallel blocks although I know I probably shouldn’t; but so long as I write these notes while rustc builds, everybody wins, right?
Anyhow, pcwalton and dherman yesterday pointed out to me
that const
is not exactly one of the most beloved features of C++:
“const-ification” is no fun, and if we’re not careful, Rust could walk
right down that path. To some extent my reaction is, “Well,
something’s gotta’ give.” You can’t have modular static race freedom
without some way to know what function will write what. But
nonetheless they are quite correct.
I think the problem with const
is that it’s not the default, despite
the fact that most operations are reads. So perhaps we could make
const
the default for Rust: I think this would fit with the general
philosophy of making mutation explicit. In practice what this would
mean is that declarations like x: @T
, &&x: T
, x: [T]
would all
be declaring a pointer x
to read-only (i.e., const
) data. This
would be a deep const. A pointer of type @mut T
would permit
modification of the data it points at. (I am not quite sure what to do
with ~
; since there is no fear of aliasing, permitting mutation
implicitly makes sense to me but it is somewhat inconsistent).
This makes subtle changes in the meaning of existing types. For
example, [T]
no longer means an immutable array but rather what
today would be written as [const T]
. We could add an explicit imm
qualifier if we wanted to allow for immutable data: it would be usable
not only on arrays but also on records and the like, so you could
write [imm T]
or @imm T
. I won’t talk about imm
further,
though; the details of its addition are left as an exercise to the
reader (always wanted to write that…).
An important difference between mut
and the other qualifiers is that
mut
is shallow where the others are deep. For example, given a variable
x: @mut T
where the type T
is defined as:
type T = {
f: @U, g: @mut U
};
x.f
has type @U
and x.g
has type @mut U
. But if we have a
non-mut
variable y: @T
, then both y.f
and y.g
have type @U
(the mut
on the field g
is lost because y
is a const pointer).
One final note: in comparison to C, I have only discussed the mut
qualifer in pointer types. For example, @mut T
and [mut T]
.
Types like const int
that you can find in C don’t make sense to me:
it’s a value, it can’t be modified, only overwritten. That seems like
a property of the lvalue not the type. But if you want to interpret
things in the C way, then you could say that types are mutable by
default, but pointers are const by default. So the type int
is a
mutable int (i.e., a variable which can be changed), but the type
@int
is a const pointer to an int. I don’t think this is a very
important point, personally; it sounds like it’s deep but I think it’s
not.
Ok, this is just a sketch, but it’s enough for me to remember what I was thinking later. I am pretty sure this actually hangs together rather nicely (I thought about it a lot on the train and went through various iterations). I am not sure how it would feel to program in, but I suspect it would be very nice. More to come in later posts I’m sure.