🦀

Rustの &* がわかる!

参照とデリファレンス入門 — ***&mut argarg.as_mut() を図で理解する

1.そもそも「参照」ってなに? — 住所のメモにたとえる

Rustでは、値(データ)はどこかのメモリに置かれています。参照(reference)とは、その値が置いてある「住所を書いたメモ」のようなものです。

値そのもの
📦 42
家(実体)
指している
参照 &x
📮 住所メモ
「42はあそこにあるよ」

なぜ住所メモ(参照)が必要なのでしょう? Rustには所有権(ownership)というルールがあり、値を関数に「そのまま渡す」と、値の持ち主が移ってしまいます(ムーブ)。元の場所からは使えなくなります。

fn eat(s: String) {}   // String を「もらう」関数

fn main() {
    let s = String::from("apple");
    eat(s);            // 所有権が eat に移動(ムーブ)
    // println!("{}", s);  // ❌ もう s は使えない!
}

「家ごとあげる」のではなく「住所だけ教える(貸す)」のが参照です。貸すだけなら、あとで自分も使えます。これを借用(borrow)と呼びます。

fn look(s: &String) {}  // String を「借りる」関数

fn main() {
    let s = String::from("apple");
    look(&s);           // 住所だけ渡す(借用)
    println!("{}", s);  // ✅ s はまだ自分のもの
}

2.&* — 借りる、中身を取り出す

2つの記号はペアで覚えましょう。逆の操作です。

🔒 &x … 値 x参照を作る(住所メモを書く)

🔓 *r … 参照 r中身を取り出す(メモの住所に行って家を開ける)=デリファレンス

let x: i32 = 42;
let r: &i32 = &x;   // & で参照を作る

println!("{}", *r); // * で中身を取り出す → 42
assert_eq!(*r, 42); // *r は i32 の 42 そのもの
x: i32
42
*r でここへ
r: &i32
&x
&x で作った
POINT
型で読むとわかりやすい:& を付けると型に & が増える(i32&i32)。* を付けると型から & が1枚はがれる(&i32i32)。

3.&mut arg — 「書きこみOK」で貸す

参照には2種類あります。

書き方意味ルール
&x読むだけの貸し出し(不変参照)同時に何枚でも配れる
&mut x書きこみOKの貸し出し(可変参照)同時に1枚だけ。しかも他の参照と共存できない

「図書館の本(みんなで読める)」と「原本の編集権(同時に1人だけ)」の違いだと思ってください。編集する人が2人いたら本がぐちゃぐちゃになるからです。

fn add_ten(n: &mut i32) {   // 「書きこみOKの住所メモ」を受け取る
    *n += 10;               // * で中身を開けて書きこむ
}

fn main() {
    let mut x = 5;          // 貸す側も mut にしておく必要がある
    add_ten(&mut x);        // &mut で「編集権つき」で貸す
    println!("{}", x);      // → 15 (呼び出し元の x が変わった!)
}
mainx
5 → 15
関数の中から書き換えられた
*n += 10
add_tenn: &mut i32
✏️ &mut x
編集権つきメモ

✅ OK:不変参照は何枚でも

let x = 1;
let a = &x;
let b = &x;   // 読むだけなら何枚でもOK
println!("{} {}", a, b);

❌ NG:可変参照は同時に1枚

let mut x = 1;
let a = &mut x;
let b = &mut x; // ❌ 編集権は同時に1つ
println!("{} {}", a, b);

4.** — 参照の参照を2回はがす

「住所メモの住所メモ」も作れます。&&i32 は「i32 への参照」への参照です。中の値にたどり着くには *2回使います。それが ** です。

let x: i32   = 42;
let r: &i32  = &x;    // 参照
let rr: &&i32 = &r;   // 参照の参照

println!("{}", **rr); // * を2回 → 42
//  *rr は &i32(1枚はがれた)
// **rr は  i32(2枚はがれた)
x: i32
42
2回目の *
r: &i32
&x
1回目の *
rr: &&i32
&r
メモのメモ

実際によく出会う場所:イテレータ

わざわざ && を自分で書くことは少ないですが、イテレータの中で自然に発生します。

let nums = vec![1, 2, 3, 4];

// .iter() は &i32 を返す。filter のクロージャは
// その参照をさらに借りるので、引数 n は &&i32 になる!
let evens: Vec = nums
    .iter()                     // Item = &i32
    .filter(|n| **n % 2 == 0)   // n: &&i32 → ** で i32 に
    .copied()
    .collect();

assert_eq!(evens, vec![2, 4]);
TIP
**n % 2 == 0 の代わりに、パターンで1枚はがす書き方も人気です:.filter(|&&n| n % 2 == 0)(引数側で && を受けると ni32 になる)。どちらも意味は同じです。

5.自動デリファレンス — * を書かなくていい場面

「え、でも普段 * なんてほとんど書いてないけど?」——そのとおりです。Rustはメソッド呼び出し(.)や比較の多くで、自動的に * を補ってくれます(auto-deref)。

let s = String::from("hello");
let r: &String = &s;

// .len() を呼ぶとき、r を自分で *r にしなくていい
println!("{}", r.len());     // ✅ 自動で (*r).len() になる

// println! も参照を自動で追いかけて表示してくれる
println!("{}", r);           // ✅ hello

逆に、自分で * を書かないといけないのは主にこんなときです。

✏️ 代入・演算で中身そのものを変えるとき*n += 10;n += 10 は「メモに足す」ことになり型エラー)

⚖️ 値そのものと比較・マッチしたいときif *flag { ... }match *state { ... }

📤 参照からコピーを取り出したいときlet v: i32 = *r;i32 のようなコピーできる型)

POINT
.(ドット)は賢いので * 不要。演算子(=, +=, % など)や match は素直なので自分で * をはがす」と覚えるとだいたい合っています。

6.arg.as_mut() — 中身への可変参照をもらう

&mut x が「変数を直接借りる」だったのに対して、as_mut()「箱に入った値の、中身への可変参照をもらう」メソッドです。代表選手は Option です。

Option::as_mut() — 箱を開けずに中身を編集する

Option<T> は「値が入っているかもしれない箱」。as_mut() を使うと、箱の所有権を奪わずに(箱を壊さずに)、中身 T への &mut を取り出せます。

let mut maybe_score: Option = Some(80);

// as_mut(): Option → Option<&mut i32>
if let Some(score) = maybe_score.as_mut() {
    *score += 5;              // score: &mut i32 なので * で書きこむ
}

assert_eq!(maybe_score, Some(85));  // ✅ 箱はそのまま、中身だけ変わった
maybe_score: Option<i32>
Some( 80→85 )
箱は手元に残ったまま
中身だけ指す
score: &mut i32
✏️ 中身へのメモ
as_mut() でもらった

❌ as_mut() を使わないと…

let mut m: Option =
    Some("hi".into());

// if let Some(s) = m { ... }
// ❌ 中身がムーブして箱が壊れる
//    (String はコピーできない)

✅ as_mut() なら箱を保てる

let mut m: Option =
    Some("hi".into());

if let Some(s) = m.as_mut() {
    s.push_str(" there"); // 中身を編集
}
// m は Some("hi there") のまま健在

&mut x と x.as_mut() の使い分け

書き方できること使う場面
&mut x 変数 x そのものへの可変参照を作る 関数に「書きこみOK」で貸したいとき
x.as_mut() ラッパー(Option / Box など)の中身への可変参照をもらう 箱の所有権を保ったまま中身だけ編集したいとき
TIP
読むだけ版の兄弟 as_ref()Option<T>Option<&T>)もセットで覚えましょう。「as_ref() は読書用、as_mut() は編集用」です。ほかにも Box<T>Vec<T> にも AsMut 系のメソッドがあり、「外側の型はそのまま、中への &mut をもらう」という考え方は共通です。

7.まとめチートシート

記号 / メソッドひとことで型の変化たとえ
&x 読むだけで借りる T&T 住所メモを渡す
&mut x 書きこみOKで借りる(同時に1枚) T&mut T 編集権つきメモを渡す
*r 参照を1枚はがして中身へ &TT メモの住所へ行き、家を開ける
**rr 参照を2枚はがす(イテレータで頻出) &&TT メモのメモをたどる
x.as_mut() 箱の中身への可変参照をもらう Option<T>Option<&mut T> 箱を壊さず中身だけ編集
x.as_ref() 箱の中身への参照をもらう(読書用) Option<T>Option<&T> 箱を壊さず中身を読む

最後に、今日の全部が入ったコードです。

fn add_ten(n: &mut i32) {
    *n += 10;                          // ③ * で中身に書きこむ
}

fn main() {
    let mut x = 5;
    add_ten(&mut x);                   // ① &mut で編集権つきで貸す
    assert_eq!(x, 15);

    let nums = vec![1, 2, 3, 4];
    let evens: Vec = nums
        .iter()
        .filter(|n| **n % 2 == 0)      // ② ** で2枚はがす
        .copied()
        .collect();
    assert_eq!(evens, vec![2, 4]);

    let mut best: Option = Some(evens[0]);
    if let Some(b) = best.as_mut() {   // ④ as_mut で中身への &mut
        *b = 100;
    }
    assert_eq!(best, Some(100));
}
まとめ
& は「貸す」、* は「はがす」。** は「2枚はがす」。&mut は「編集権つきで貸す(同時に1人)」。as_mut() は「箱を保ったまま中身の編集権をもらう」。型に付いた & の枚数を数えれば、* を何回書くかは自然に決まります。