コンテンツにスキップ

Rustのエラー E0614: type cannot be dereferenced(参照ではない値に * を付けた)

E0614 は、参照ではないものの前に * を書いたので、たどる先がない、という意味のエラーです。* 演算子は「この参照が指している値のところへ行く」という意味です。ただの u32 や i32、構造体は、どこも指していません。それ自体が値なのです。このエラーはクロージャやループの中でよく起こります。手元にあるのが &u32 なのか u32 なのか、わからなくなりやすいからです。rustc は見出しで本当の型を教えてくれますし、直し方はほとんどの場合、* を1つ消すだけです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0614]: type `u32` cannot be dereferenced
--> src/main.rs:3:64
|
3 | let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32`
| ^^^^^^ can't be dereferenced

「type u32 cannot be dereferenced」は「型 u32 は参照外し(dereference)できない」という意味です。

このエラーが出るのは、次のプログラムです。

fn main() {
let visits: Vec<u32> = vec![12, 0, 7, 30];
let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32`
println!("{:?}", doubled);
}

コメントは「count はもう u32 になっている」という意味です(エラーになる例なので、コードはそのままにしています)。

^^^^^^ は3行目の *count を指していて、「can’t be dereferenced」(参照外しできない)というラベルが付いています。見出しは、count が本当は何なのかを教えてくれています。&u32 ではなく、u32 です。

チェーンに沿って、型をたどってみましょう。

  1. visits.iter() は、要素への参照をくれます。&u32、&u32、……です。
  2. .copied() は、それぞれの &u32 を、ただの u32 のコピーに変えます。参照はなくなりました。
  3. だから .map(|count| ...) の中の count は u32 で、*count は、存在しないポインタをたどるよう Rust に頼んでいることになります。

見出しが type `{integer}` cannot be dereferenced のときも同じです。{integer} は、rustc が「まだ決まっていない何かの整数型」を表すときの書き方です。たいていは、型を書かなかった 72 のような数値です。

誰かに「この住所には何がある?」と聞きながら、住所を書いた紙ではなく、荷物そのものを渡すようなものです。相手はもうその物を持っています。行くべき住所はありません。

Rust では、参照(&T)と値そのもの(T)は別の型です。* が使えるのは、どこかを指しているものだけです。参照の &T と &mut T、Box<T> や Rc<T> のようなスマートポインタ、そして Deref トレイトを実装した型です。u32 やただの構造体にはどれも当てはまらないので、* を付けても意味がありません。

Rust は余分な * をこっそり無視することもできたはずですが、そうはしません。余分な * があるということは、たいてい自分の持っている型を勘違いしているということで、その勘違いはほかのバグにつながりがちです。違うものどうしを比べたり、借用するつもりの値をムーブしてしまったりです。だから rustc は止まって、本当の型を教えてくれるのです。詳しくは借用とスマートポインタを見てください。

逆の間違いには、別のエラーがあります。参照に * を付け忘れたとき、たとえば count が &u32 なのに count > 10 と書くと、E0308(mismatched types、型の不一致)になります。

ほとんどの場合はこれを選びます。特に、.copied()、.cloned()、Vec の .into_iter() など、値そのものを渡してくれるもののあとです。値はもうそこにあるので、そのまま使いましょう。

fn main() {
let visits: Vec<u32> = vec![12, 0, 7, 30];
let doubled: Vec<u32> = visits.iter().copied().map(|count| count * 2).collect();
println!("{:?}", doubled);
// `filter` は各要素をクロージャに貸すので、ここでの `count` は `&u32` で、
// `*` を付けるのが正しい。
let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect();
println!("{:?}", busy);
}

実行結果:

[24, 0, 14, 60]
[12, 30]
▶ 実際に試してみよう

この2つ目の部分で、つまずく人がたくさんいます。同じチェーンの中でも、map は要素そのものを受け取り、filter は要素への参照を受け取るのです。迷ったら、ドキュメントでメソッドを見てみましょう。filter が受け取るのは FnMut(&Self::Item) -> bool で、& が付いています。詳しくはイテレータとクロージャを見てください。

直し方2:for ループでは、値か参照かを選ぶ

Section titled “直し方2:for ループでは、値か参照かを選ぶ”

* が for ループの中にあるときは、これを選びます。for score in scores は Vec をばらして、それぞれの値をくれるので、*score は失敗します。

error[E0614]: type `{integer}` cannot be dereferenced
--> src/main.rs:5:18
|
5 | total += *score; // ❌ `score` is a number, not a reference
| ^^^^^^ can't be dereferenced

このエラーが出るプログラムです(コメントは「score は数値で、参照ではない」という意味です)。

fn main() {
let scores = vec![72, 95, 88];
let mut total = 0;
for score in scores {
total += *score; // ❌ `score` is a number, not a reference
}
println!("total: {}", total);
}

* を消すか、&scores でループしましょう。そうすると、それぞれの score は &i32 になり、* が正しくなります。ループのあとも Vec を使えます。

fn main() {
let scores = vec![72, 95, 88];
let mut total = 0;
for score in &scores {
total += *score; // ここでは `score` は `&i32`
}
println!("合計: {}", total);
// `scores` はループにムーブされていないので、まだ使える。
println!("{} 件、平均 {}", scores.len(), total / scores.len() as i32);
}

実行結果:

合計: 255
3 件、平均 85
▶ 実際に試してみよう

覚え方:for x in v は値を、for x in &v は & 参照を、for x in &mut v は &mut 参照をくれます。* が必要なのは後の2つだけです。詳しくはforループとベクタを見てください。

直し方3:参照は &x でマッチするか *x を使うか、どちらか1つにする

Section titled “直し方3:参照は &x でマッチするか *x を使うか、どちらか1つにする”

クロージャやループのパターンに、|&p| のように & がもう付いているときは、これを選びます。パターンの中の & が参照をほどいてくれるので、p はただの数値になり、*p は1段階多すぎます。

error[E0614]: type `{integer}` cannot be dereferenced
--> src/main.rs:3:53
|
3 | let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&`
| ^^ can't be dereferenced

このエラーが出るプログラムです(コメントは「&p がもう & を取り除いている」という意味です)。

fn main() {
let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&`
println!("{:?}", with_fee);
}

参照の取り除き方を1つだけ選びましょう。次の2行は同じことをしています。

fn main() {
let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // パターンで `&` を取り除く
let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // `*` で `&` を取り除く
println!("{:?} {:?}", with_fee, same);
// `.iter()` のあとの `filter` は `&&i32` を受け取る。`&` が2つなので、パターンは `&&p`。
let pricey = prices.iter().filter(|&&p| p > 4).count();
println!("高い商品: {} 個", pricey);
}

実行結果:

[4, 6, 9] [4, 6, 9]
高い商品: 2 個
▶ 実際に試してみよう

ループでも同じです。for &(name, qty) in &orders はそれぞれの参照をほどくので、name と qty は * なしで使います。詳しくはパターンマッチングを見てください。

直し方4:型を確かめ、ラッパー型ならフィールドか Deref を使う

Section titled “直し方4:型を確かめ、ラッパー型ならフィールドか Deref を使う”

見出しに type `Meters` cannot be dereferenced のように自分で作った型の名前が出ているときは、これを選びます。構造体も参照ではありません。値を1つ包んでいるだけの構造体でも同じです。

error[E0614]: type `Meters` cannot be dereferenced
--> src/main.rs:5:18
|
5 | let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference
| ^^^^ can't be dereferenced

このエラーが出るプログラムです(コメントは「run は Meters で、参照ではない」という意味です)。

struct Meters(f64);
fn main() {
let run = Meters(2.5);
let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference
println!("{} m", longer);
}

タプル構造体の中の数値を取り出すには、フィールド .0 を使います。

struct Meters(f64);
fn main() {
let run = Meters(2.5);
let longer = run.0 + 1.0;
println!("{} m", longer);
}

実行結果:

3.5 m
▶ 実際に試してみよう

自分の型が本当にスマートポインタのようなラッパーで、* を使えるようにしたいなら、その型に std::ops::Deref を実装しましょう。ただし、それはラッパー型のときだけにします。Meters のような「ニュータイプ」(newtype)なら、.0 やメソッドのほうがわかりやすいです。変数の型がわからないときは、エディタでその変数にマウスを乗せる(rust-analyzer が型を表示してくれます)か、見出しを信じましょう。見出しにはいつも本当の型が書かれています。詳しくは構造体とスマートポインタを見てください。

構造体のフィールドに * を付ける。 config が構造体(またはその参照)で、retries が u32 のとき、*config.retries は type `u32` cannot be dereferenced になります。. がもう参照をたどってくれているからです。直し方:config.retries と書きます。

HashMap を値でループする。 for (word, count) in counts { total += *count; } は type `u32` cannot be dereferenced になります。counts そのものでループすると、所有権を持ったキーと値が手に入るからです。直し方:* を消すか、あとでマップを使うなら &counts でループします。詳しくはHashMapを見てください。

.copied().max() や .cloned().find(...) のあと。 これらは Option<&u32> ではなく Option<u32> を返すので、if let Some(best) = ... { *best } の * は1つ多すぎます。直し方:best をそのまま使います。.copied() なしの .iter().max() なら Option<&u32> が返ってくるので、*best が正しくなります。

  • E0308:mismatched types(型の不一致)。逆の間違いで、参照とただの値が出会う場所で * が足りない。
  • E0369:binary operation cannot be applied to a type(その型には二項演算を使えない)。参照と値が混ざったときに出ることがある、別のエラーです。
  • E0507:cannot move out of a reference(参照からムーブできない)。こちらは * 自体は使えますが、Copy でない値をムーブしてしまうことになります。
  • E0610:{integer} is a primitive type and has no fields({integer} はプリミティブ型なのでフィールドがない)。「ただの数値」という点で、このエラーの親戚です。
  • E0599:no method found(メソッドが見つからない)。値が思っていた型ではなかったときに出ます。
  • コンパイルエラーの一覧:エラー解説ページの全リストです。

理解度チェック

1. visits.iter().copied().map(|count| *count + 1) の *count が E0614 になるのはなぜですか?

2. scores が Vec<i32> のとき、for score in &scores { total += *score; } はコンパイルできますか?

3. prices.iter().map(|&p| *p * 2) が E0614 になりました。正しいのはどれですか?

スコア: 0 / 3