Rust 错误 E0614:type cannot be dereferenced(类型不能解引用)
E0614 的意思是:你在一个不是引用的东西前面写了 *,所以没有什么可以“顺着找过去”的。 * 运算符的意思是“到这个引用指向的值那里去”。一个普通的 u32、i32 或结构体并没有指向任何地方:它本身就是那个值。这个错误通常出现在闭包和循环里,因为在那里很容易搞不清手上拿的是 &u32 还是 u32。rustc 会在第一行告诉你真正的类型,而修复几乎总是删掉一个 *。这个系列的其他错误,请看 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` 已经是 `u32` 了 | ^^^^^^ can't be dereferenced第一行的意思是“类型 u32 不能解引用”。
产生这个错误的程序是这样的:
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` 已经是 `u32` 了 println!("{:?}", doubled);}这个错误是什么意思
Section titled “这个错误是什么意思”^^^^^^ 指向第 3 行的 *count,标注是“can’t be dereferenced”(不能解引用)。第一行告诉你 count 到底是什么:一个 u32,而不是 &u32。
顺着这条链看一看类型:
visits.iter()给你的是元素的引用:&u32、&u32…….copied()把每个&u32变成一份普通的u32副本。引用就没有了。- 所以在
.map(|count| ...)里,count是u32,而*count是在让 Rust 顺着一个根本不存在的指针找过去。
如果第一行写的是 type `{integer}` cannot be dereferenced,也是同一回事。{integer} 是 rustc 表示“某种还没确定下来的整数类型”的写法,通常是像 72 这样你从没指定类型的数字。
就像你问别人“这个地址上有什么?”,递过去的却是包裹本身,而不是一张写着地址的纸条。东西已经在他手上了,没有地址可以去。
Rust 为什么要这样做
Section titled “Rust 为什么要这样做”在 Rust 里,引用(&T)和值本身(T)是不同的类型。* 只能用在指向别处的东西上:引用 &T 和 &mut T,Box<T>、Rc<T> 这样的智能指针,以及任何实现了 Deref trait 的类型。u32 或普通的结构体都不是这些,所以对它们用 * 没有意义。
Rust 本可以悄悄忽略多出来的 *,但它没有这么做。多写一个 *,通常说明你搞错了自己手上的类型,而这种混乱往往会引出别的 bug:比较了错误的东西,或者把本想借用的值移动走了。所以 rustc 会停下来,告诉你真正的类型。参见借用和智能指针。
相反的错误有自己的错误代码。如果你在引用上忘了写 *,比如 count 是 &u32 时写了 count > 10,得到的是 E0308 mismatched types(类型不匹配)。
修复 1:去掉 *
Section titled “修复 1:去掉 *”几乎每次都选这个,尤其是在 .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]第二部分会难住很多人:在同一条链里,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` 是数字,不是引用 | ^^^^^^ can't be dereferenced出错的程序:
fn main() { let scores = vec![72, 95, 88]; let mut total = 0; for score in scores { total += *score; // ❌ `score` 是数字,不是引用 } println!("总分:{}", 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 引用。只有后两种需要 *。参见 for 循环和 vector。
修复 3:用 &x 匹配引用,或者用 *x,但不要两个都用
Section titled “修复 3:用 &x 匹配引用,或者用 *x,但不要两个都用”当闭包或循环的模式里已经有一个 & 时,比如 |&p|,选这个。模式里的 & 会替你把引用拆开,所以 p 已经是普通的数字,*p 就多走了一步:
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` 已经去掉了 `&` | ^^ can't be dereferenced出错的程序:
fn main() { let prices = [3, 5, 8]; let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` 已经去掉了 `&` println!("{:?}", with_fee);}去掉引用的方法只选一种。下面两行做的是同一件事:
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`:两个 `&`,所以模式写成 `&&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,选这个。结构体也不是引用,哪怕它只包着一个值:
error[E0614]: type `Meters` cannot be dereferenced --> src/main.rs:5:18 |5 | let longer = *run + 1.0; // ❌ `run` 是 `Meters`,不是引用 | ^^^^ can't be dereferenced出错的程序:
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = *run + 1.0; // ❌ `run` 是 `Meters`,不是引用 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 拿到的是有所有权的键和值。修复:去掉 *,或者如果之后还要用这个 map,就遍历 &counts。参见哈希 map。
在 .copied().max() 或 .cloned().find(...) 之后。 它们给出的是 Option<u32>,而不是 Option<&u32>,所以在 if let Some(best) = ... { *best } 里,* 是多余的。修复:直接用 best。如果没有 .copied(),.iter().max() 给出的是 Option<&u32>,这时写 *best 就是对的。
- E0308:mismatched types(类型不匹配)。相反的错误:引用碰上普通的值时少了一个
*。 - E0369:二元运算不能用在某个类型上。引用和值混在一起时,也可能出现这个错误。
- E0507:cannot move out of a reference(不能从引用中移出值)。这里
*确实能用,但它会移动一个不是Copy的值。 - E0610:
{integer}is a primitive type and has no fields(基本类型没有字段),这个错误“它只是个数字”的近亲。 - 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
- E0614 — Rust error codes index(英文):这个错误的官方解释,附有一个最小示例和修复方法。
- 引用与借用 — Rust 程序设计语言:
&和*是什么意思,以及为什么引用和值是不同的类型。 - 使用 Deref Trait 将智能指针当作常规引用处理 — Rust 程序设计语言:
*怎样顺着引用找到值,以及Dereftrait 怎样让它也能用在你自己的类型上。 Iterator::copied— 标准库文档(英文):把&T元素变成T元素的方法,也是*变得多余的最常见原因。- 指针和引用 — 通过例子学 Rust:模式里的
&val和表达式里的*val有什么区别。