So, I wanted to take a step back and look at this type hole concerning
mutability. I think there are a number of intertwined concerns.
Anyway, I've been thinking over the "menu" of possible solutions and I
wanted to lay out the ones that I see at the moment. This is not to say
that there are not others. I apologize that this is kind of a long mail.
1. *Create a class of assignable types.* This is what I initially
proposed. I have a patch in a sandbox that implements this, hopefully
today I can come back with some data about how disruptive this is to the
existing codebase.
2. *Give the type checker a clearer view of when memory is mutable. *The
worst part of the problem right now is that if you have a parameter
declared like so: `&x: {f: T}`, the field `f` is considered immutable by
the type checker, although in fact the mode `&` allows `x` to be
overwritten. This combines with subtyping to create an inconsistent
view over mutable memory: the callee can think that the memory has type
T but the caller thinks it has type T' where T' <: T. Bad. If the type
checker were aware of modes, we could have the type checker treat the
field `f` as mutable even though it is declared immutable. Integrating
modes in the type checker is somewhat unappealing to me, because they
are supposed to be orthogonal, so an alternative would be to integrate
first-class reference types as is proposed in the regions proposal.
This would mean that the parameter `x` would be declared as `x: &mut {f:
T}`. Now the type checker can more easily see that the field `f` may be
mutated. (Note that the use of `&` types does not require the full
region proposal, we could impose arbitrary limitations that make them
basically equivalent to modal arguments).
The main downside to option #2 from my point of view is that it presents
a kind of complex story. I was initially opposed to it because it
complicates the type checker and it seems hard to explain: we say fields
are immutable by default but that's not really true (although it is true
in a lot of important cases). Therefore, I was thinking about a third
option that builds on the `&` types to provide a simpler story about
mutability. I think this more closely meets peoples intutions:
3. *Change mutability to be "all-or-nothing".* This actually builds on
solution #2. The goal is to address the complicated story about when
fields are mutable or immutable that results. The basic idea is that we
do not mark individual fields of a record as mutable or immutable, but
rather the entire record itself. Interior records then naturally
inherit the mutability of their container, and records stored in mutable
local variables can be overridden without problems. I spelled out this
idea a bit in
<https://github.com/mozilla/rust/wiki/Proposal-for-all-or-nothing-mut>,
though this is more of a sketch than a detailed proposal. The main
downside of this option is that we lose the ability to specify "field x
is mutable but field y is not" (but then, that was the idea: the
mutability of fields is supposed to come from the outside now).
*Another cross-cutting concern so to speak has to do with the alias
checker.* One of the motivations for exploring this issue was to try
and find alternatives that do not require an alias checker to be sound:
we could still keep the alias checker in the code base, as it can
provide very useful guarantees for reasoning about your program, but I
would prefer if the user has to "opt-in" to use it. [1] This way there
would be no problems with functions that invoke closures having to make
extra copies simply to please the alias checker and so forth.
Both options #2 and #3 will basically result in the type checker being
more conservative about when fields are considered potentially mutable.
Right now we consider any field that is not declared mutable as
immutable. Only option #1 allows us to keep doing that. This in turn
means that things like the Dan's bug proposal I put forward can get away
with making fewer copies at runtime.
However, it may be that having (1) immutable local variable declarations
by default as proposed in #1273 and (2) immutable references of the form
(rather than read-only references, as we have today) could address that
last concern satisfactorily.
At some point I thought that options #2 and #3 would still require an
alias checker to be sound, but I am not sure why I thought that at the
moment. It seems to me that any potentially mutable piece of memory
storing an enum would either have to be in a mutable local variable or
reachable via a pointer with a `mut` or `const` qualifier, regardless of
how many times it is aliased: in either of those cases, the type system
can clearly see that it is mutable and enforce that it cannot be matched
with a copy to immutable memory.
Niko
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev