& と * がわかる!参照とデリファレンス入門 — *・**・&mut arg・arg.as_mut() を図で理解する
Rustでは、値(データ)はどこかのメモリに置かれています。参照(reference)とは、その値が置いてある「住所を書いたメモ」のようなものです。
&xなぜ住所メモ(参照)が必要なのでしょう? 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つの記号はペアで覚えましょう。逆の操作です。
🔒 &x … 値 x の参照を作る(住所メモを書く)
🔓 *r … 参照 r の中身を取り出す(メモの住所に行って家を開ける)=デリファレンス
let x: i32 = 42;
let r: &i32 = &x; // & で参照を作る
println!("{}", *r); // * で中身を取り出す → 42
assert_eq!(*r, 42); // *r は i32 の 42 そのもの
x: i32*r でここへr: &i32&x で作った& を付けると型に & が増える(i32 → &i32)。* を付けると型から & が1枚はがれる(&i32 → i32)。
&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 が変わった!)
}
main の x*n += 10add_ten の n: &mut i32let x = 1;
let a = &x;
let b = &x; // 読むだけなら何枚でもOK
println!("{} {}", a, b);
let mut x = 1;
let a = &mut x;
let b = &mut x; // ❌ 編集権は同時に1つ
println!("{} {}", a, b);
** — 参照の参照を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*r: &i32*rr: &&i32わざわざ && を自分で書くことは少ないですが、イテレータの中で自然に発生します。
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]);
**n % 2 == 0 の代わりに、パターンで1枚はがす書き方も人気です:.filter(|&&n| n % 2 == 0)(引数側で && を受けると n は i32 になる)。どちらも意味は同じです。
* を書かなくていい場面「え、でも普段 * なんてほとんど書いてないけど?」——そのとおりです。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 のようなコピーできる型)
.(ドット)は賢いので * 不要。演算子(=, +=, % など)や match は素直なので自分で * をはがす」と覚えるとだいたい合っています。
arg.as_mut() — 中身への可変参照をもらう&mut x が「変数を直接借りる」だったのに対して、as_mut() は「箱に入った値の、中身への可変参照をもらう」メソッドです。代表選手は Option です。
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>score: &mut i32as_mut() でもらったlet mut m: Option =
Some("hi".into());
// if let Some(s) = m { ... }
// ❌ 中身がムーブして箱が壊れる
// (String はコピーできない)
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 そのものへの可変参照を作る |
関数に「書きこみOK」で貸したいとき |
x.as_mut() |
ラッパー(Option / Box など)の中身への可変参照をもらう |
箱の所有権を保ったまま中身だけ編集したいとき |
as_ref()(Option<T> → Option<&T>)もセットで覚えましょう。「as_ref() は読書用、as_mut() は編集用」です。ほかにも Box<T> や Vec<T> にも AsMut 系のメソッドがあり、「外側の型はそのまま、中への &mut をもらう」という考え方は共通です。
| 記号 / メソッド | ひとことで | 型の変化 | たとえ |
|---|---|---|---|
&x |
読むだけで借りる | T → &T |
住所メモを渡す |
&mut x |
書きこみOKで借りる(同時に1枚) | T → &mut T |
編集権つきメモを渡す |
*r |
参照を1枚はがして中身へ | &T → T |
メモの住所へ行き、家を開ける |
**rr |
参照を2枚はがす(イテレータで頻出) | &&T → T |
メモのメモをたどる |
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() は「箱を保ったまま中身の編集権をもらう」。型に付いた & の枚数を数えれば、* を何回書くかは自然に決まります。