If we run this then we correctly get the error "cannot assign to immutable field a.x
".
If we remove the two //
comments, and comment out this bad line, then we get the error "cannot assign to data in a &
reference". This makes sense because &mut
does not provide interior mutability. We can reborrow an &A
freely, so this must not give mutable access, ala &&mut
is &&
.
If we remove both the //
comments and the /* */
comments, then the whole thing compiles, permitting the bad line that violates our invariant that a.x
must never be pointed to anything else.
pub struct A<'a> {
pub x: &'a mut [u8; 3],
}
fn main() {
let y = &mut [7u8; 3];
let /*mut*/ a = A { x: &mut [0u8; 3] };
a.x[0] = 3;
a.x = y; //// This must be prevented!
{
// let b = &/*mut*/ a;
// b.x[1] = 2;
}
println!("{:?}", a.x);
}
How should one maintain this invariant that x
must not be changed? We could make the field private while providing public dereferencing methods, except writing constructors for A
in unacceptable.
We can avoid the obnoxious constructor by making a A
a private member of a wrapper struct AA(A)
which itself hosts the public dereferencing methods. Now AA
needs a trivial constructor, but it does not need arguments for all fields of A
, does not impact order of execution, etc. This becomes painful if we need some traits implemented for both A
and AA
though.
Yet, another approach would be to use interior mutability by working with Cell<A>
, accessing it with Cell::replace
, and putting it back later. This sounds highly problematic, but shows that more solutions exist.
Any cleaner approaches?
Rather than use a Cell<A>
you could make the array inside the A
contain Cell<u8>
s:
use std::cell::Cell;
pub struct A<'a> {
x: &'a [Cell<u8>; 3],
}
fn main() {
// let y = &mut [7u8; 3];
let a = A { x: &[Cell::new(0u8), Cell::new(0u8), Cell::new(0u8)] };
a.x[0].set(3);
// a.x = y;
{
let b = &a;
b.x[1].set(2);
}
println!("{:?}", a.x);
}
This will still behave how you want, with the same performance, but now the a
variable is immutable, so you can't change a.x
. You also don't need to make the array reference mutable either.
The slight downside with your example is that you can't use the array repetition syntax, since Cell<T>
does not implement Copy
. This seems like an omission, but there is some explanation as to why that is here.