-
Notifications
You must be signed in to change notification settings - Fork 3
Note: Inherited Mutability
Variables can be declared mutable:
let mut x = 42;
x = 42;
This looks fine and sensible.
Now, assume we have a structure like this:
pub struct Foo {
mut bar : i32;
}
And we do this:
let struc = new Foo { bar = 42 };
struc.bar = 0;
This seems innocent at first. But suppose we have:
pub fn baz(s : &Foo) -> unit {
s.bar = 0;
}
And we do:
let struc = new Foo { bar = 42 };
baz(&struc);
assert struc.bar == 42; // Fails?!
This is not very intuitive; after all, we declared the struc
variable as immutable!
So, instead of allowing mut
on structure fields, we'll say that a structure's fields are only mutable if the slot the structure sits in is mutable.
pub struct Foo {
bar : i32;
}
let struc = new Foo { bar = 42 };
struc.bar = 0; // Illegal.
let mut struc = new Foo { bar = 42 };
struc.bar = 0; // Legal.
Now, it'd be useful to be able to say that a structure field can't change. So this should be allowed too:
pub struct Foo {
imm bar : i32;
}
let struc = new Foo { bar = 42 };
struc.bar = 0; // Illegal.
let mut struc = new Foo { bar = 42 };
struc.bar = 0; // Illegal.
The way this will interact with pointer types is:
let x = @42;
x = @0; // Illegal.
*x = 0; // Illegal.
let mut x = @42;
x = @0; // Legal.
*x = 0; // Illegal.
let x = @mut 42;
x = @mut 0; // Illegal.
*x = 0; // Legal.
let mut x = @mut 42;
x = @mut 0; // Legal.
*x = 0; // Legal.
But wait! In the third example, what we're doing pretty much amounts to the mutable field example in the beginning of this page. So why allow this seemingly unsound case? There are two reasons:
- It's actually useful (mutating things on the heap is essential in a systems language) and it's the lesser of two evils (partial mutability versus complete mutability).
- It maintains a clean separation between mutability of the slot a pointer sits in and the data it points to elsewhere. This is not the case for structures because they're in-place.
In the end, it's a trade-off like any other in language design.
Now, vectors should also be considered:
let vec = [1, 2, 3 .. 3];
vec.[0] = 0; // Illegal.
vec = [2, 3, 4 .. 3]; // Illegal.
let mut vec = [1, 2, 3 .. 3];
vec.[0] = 0; // Legal.
vec = [2, 3, 4 .. 3]; // Legal.
Keep in mind that vectors are stored in-place, similar to structures. So inherited mutability applies to them as well.
Arrays work similarly to pointers:
let arr = @[1, 2, 3];
arr = @[2, 3, 4]; // Illegal.
arr.[0] = 0; // Illegal.
let mut arr = @[1, 2, 3];
arr = @[2, 3, 4]; // Legal.
arr.[0] = 0; // Illegal.
let arr = @mut [1, 2, 3];
arr = @mut [2, 3, 4]; // Illegal.
arr.[0] = 0; // Legal.
let mut arr = @mut [1, 2, 3];
arr = @mut [2, 3, 4]; // Legal.
arr.[0] = 0; // Legal.
Tuples are completely unaffected by the inherited mutability rules as they are always immutable. All other types (bool
, unit
, integer types, floating point types, and function pointers) have no interesting indirect state so they are unaffected as well.
- Home
- Introduction
- Motivation
- Features
- Tutorial
- Library
- FAQ
- General
- Interoperability
- Syntax
- Type System
- Macros and CTE
- Specification
- Introduction
- Lexical
- Common Grammar Elements
- Modules and Bundles
- Type System
- Declarations
- Expressions
- Macros
- Compile-Time Evaluation
- Memory Management
- Application Binary Interface
- Foreign Function Interface
- Unit Testing
- Documentation Comments
- Style
- Indentation
- Braces
- Spacing
- Naming