&mut 型と let mut の違い

ふと気になってしまったため

🎵 今日に一曲は、ひとくちメモでは廃止します

ストックする曲数が減ってしまったため、廃止します。

mut について

イミュータブルをミュータブルにするあれです。

let式にmutという語をつけると、値を束縛した変数がミュータブル(変更可能)となります。

&mut 型について

fn main() {
// 生の値
let mut i = 32;
println!("{}", i);
// 可変参照の値
let m = &mut i;
*m = 64;
println!("{}", i);
}

&mutは、可変参照であることを示す。 *は参照外し記号である。

*&mutに用いると、その一瞬だけ、生の値が見えるようになる。

let m = &mut i; // mはiの可変参照
let x = *m; // xとiは別物になる

別の変数で束縛すると、clone したという扱いとなり、別の値になってしまう。

let m = &mut i; // mはiの可変参照
*m = 64;

ここでは、

i = 64

と同義となる。

note

本来参照が不要なところで使用しているため。
参照が必要なところでこれを行うと、値を move してしまい、元のスコープで継続して使用できなくなってしまう

可変参照の制約

// 可変参照の値
let m = &mut i;
let n = &mut i; // mが無効になる
*m = 64; // mは無効なのでエラー

可変参照は、言語の制約によって参照が一つまでに制限される。 よって、上記のように宣言するのは NG だし

#![allow(unused)]
fn main() {
// 可変参照の値
let m = &mut i;
*m = 64;
println!("{}", i); // mの参照がまだ残ってるので、新たに参照が作れない
*m = 128;
}

別の関数を呼び出すことによって、参照を無効化してしまう恐れもある。 コンパイラー曰く「m の参照が残ってるので新たに参照が作れない」となるらしい。

可変変数の可変参照

fn main() {
let mut x:i32 = 0;
let mut y:i32 = 0;
let mut z:f64 = 0.0;
let mut edit: &mut i32 = &mut x;
*edit = 3;
edit = &mut y;
*edit = 6;
edit = &mut z;
*edit = 9.0;
println!("x: {}, y: {}, z: {}", x, y, z);
}

これが一番ややこしい例です。可変変数に可変参照を束縛しているという状態です。
edit 変数は i32 型の可変参照を持つため、x,y と参照を変えながら、各値を変えています。
しかし、z は f64 型なので、コンパイルエラーとなります。

fn main() {
let mut x:i32 = 0;
let mut y:i32 = 0;
let mut edit: &mut i32 = &mut x;
*edit = 3;
edit = &mut y;
*edit = 6;
println!("x: {}, y: {}", x, y);
}

z を取り除くと、コンパイルに成功します。
なお、こういうのもありということです。

fn main() {
let mut x:i32 = 0;
let mut y:i32 = 0;
let edit: &mut i32 = &mut x;
*edit = 3;
let edit: &mut i32 = &mut y;
*edit = 6;
println!("x: {}, y: {}", x, y);
}

edit が可変でなくとも参照が可変なので、値の変更が可能です。
とてもややこしい。