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

Reply via email to