Lỗi Rust E0614: type cannot be dereferenced (thừa dấu *)
E0614 nghĩa là: bạn viết * trước một thứ không phải tham chiếu, nên chẳng có gì để đi theo. Toán tử * nghĩa là “đi tới giá trị mà tham chiếu này trỏ tới” (dereference, giải tham chiếu). Một u32, i32 hay struct thông thường không trỏ đi đâu cả: nó chính là giá trị. Lỗi này thường xảy ra trong closure và vòng lặp, nơi rất dễ mất dấu mình đang có &u32 hay u32. rustc cho bạn biết kiểu thật ngay trong dòng tiêu đề, và cách sửa gần như luôn là xóa bớt một dấu *. (Trang này thuộc loạt bài Giải thích các lỗi trình biên dịch Rust.)
Thông báo lỗi
Phần tiêu đề “Thông báo lỗi”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 dereferencedThông báo của trình biên dịch luôn bằng tiếng Anh: “type u32 cannot be dereferenced” nghĩa là “kiểu u32 không thể giải tham chiếu”. Đây là chương trình gây ra lỗi đó (chú thích được giữ bằng tiếng Anh vì nó xuất hiện nguyên văn trong thông báo lỗi):
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);}Ý nghĩa
Phần tiêu đề “Ý nghĩa”Dấu ^^^^^^ chỉ vào *count ở dòng 3, kèm nhãn “can’t be dereferenced” (không thể giải tham chiếu). Dòng tiêu đề cho bạn biết count thật ra là gì: một u32, không phải &u32.
Hãy lần theo các kiểu dọc chuỗi lời gọi:
visits.iter()cho bạn các tham chiếu tới phần tử:&u32,&u32, ….copied()biến mỗi&u32thành một bản saou32thông thường. Các tham chiếu đã biến mất.- Vậy trong
.map(|count| ...),countlà mộtu32, và*countbảo Rust đi theo một con trỏ không hề tồn tại.
Khi dòng tiêu đề nói type `{integer}` cannot be dereferenced, đó vẫn là chuyện này. {integer} là cách rustc viết “một kiểu số nguyên nào đó mà tôi chưa chốt”, thường là một con số như 72 mà bạn chưa bao giờ cho kiểu.
Giống như hỏi ai đó “ở địa chỉ này có gì?” mà lại đưa cho họ cả gói hàng thay vì một mảnh giấy ghi địa chỉ. Họ đã cầm món đồ rồi; chẳng có địa chỉ nào để tới.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Trong Rust, tham chiếu (&T) và bản thân giá trị (T) là hai kiểu khác nhau. * chỉ dùng được với những thứ trỏ tới đâu đó: tham chiếu &T và &mut T, con trỏ thông minh (smart pointer) như Box<T> và Rc<T>, và mọi kiểu cài đặt trait Deref. Một u32 hay một struct thông thường không có gì như vậy, nên * trên nó chẳng có nghĩa gì.
Rust có thể lặng lẽ bỏ qua dấu * thừa, nhưng nó không làm thế. Một dấu * thừa thường có nghĩa là bạn đang hiểu sai về kiểu mình đang có, và sự nhầm lẫn đó hay gây ra bug khác: so sánh nhầm thứ, hoặc move một giá trị mà bạn định mượn. Vì vậy rustc dừng lại và cho bạn biết kiểu thật. Xem Mượn và Con trỏ thông minh.
Sai sót ngược lại có lỗi riêng. Nếu bạn quên một dấu * trên tham chiếu, như count > 10 khi count là &u32, bạn sẽ gặp E0308 kiểu không khớp.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Bỏ dấu *
Phần tiêu đề “Cách sửa 1: Bỏ dấu *”Gần như lần nào cũng nên chọn cách này, nhất là sau .copied(), .cloned(), .into_iter() trên một Vec, hoặc bất cứ thứ gì đưa cho bạn giá trị. Giá trị đã ở đó rồi, cứ thế mà dùng:
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` cho closure mượn từng phần tử, nên ở đây `count` ĐÚNG LÀ một `&u32` // và dấu `*` là đúng. let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect(); println!("{:?}", busy);}Kết quả:
[24, 0, 14, 60][12, 30]Phần thứ hai đó làm rất nhiều người vấp: trong cùng một chuỗi, map nhận chính phần tử còn filter nhận một tham chiếu tới phần tử. Khi còn phân vân, hãy xem phương thức đó trong tài liệu. filter nhận FnMut(&Self::Item) -> bool, có dấu &. Xem Iterator và Closure.
Cách sửa 2: Trong vòng for, chọn giá trị hoặc tham chiếu
Phần tiêu đề “Cách sửa 2: Trong vòng for, chọn giá trị hoặc tham chiếu”Chọn cách này khi dấu * nằm trong một vòng for. for score in scores tháo rời Vec và đưa cho bạn từng giá trị, nên *score bị lỗi:
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 dereferencedChương trình bị lỗi:
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);}Hoặc bỏ dấu *, hoặc lặp qua &scores. Khi đó mỗi score là một &i32, dấu * là đúng, và bạn vẫn dùng được Vec sau vòng lặp:
fn main() { let scores = vec![72, 95, 88];
let mut total = 0; for score in &scores { total += *score; // giờ `score` là một `&i32` } println!("tổng: {}", total);
// `scores` không bị move vào vòng lặp, nên nó vẫn còn đây. println!("{} điểm số, trung bình {}", scores.len(), total / scores.len() as i32);}Kết quả:
tổng: 2553 điểm số, trung bình 85Một quy tắc nhanh: for x in v cho giá trị, for x in &v cho tham chiếu &, for x in &mut v cho tham chiếu &mut. Chỉ hai trường hợp sau mới cần *. Xem Vòng lặp for và Vector.
Cách sửa 3: Khớp tham chiếu bằng &x, hoặc dùng *x, nhưng đừng dùng cả hai
Phần tiêu đề “Cách sửa 3: Khớp tham chiếu bằng &x, hoặc dùng *x, nhưng đừng dùng cả hai”Chọn cách này khi closure hoặc vòng lặp của bạn đã có & trong mẫu, như |&p|. Một & trong mẫu tự tháo tham chiếu giúp bạn, nên p là một con số thông thường và *p là thừa một bước:
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 dereferencedChương trình bị lỗi:
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);}Hãy chọn một cách để bỏ tham chiếu. Hai dòng dưới đây làm cùng một việc:
fn main() { let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // mẫu bỏ dấu `&` let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // `*` bỏ dấu `&` println!("{:?} {:?}", with_fee, same);
// `filter` trên `.iter()` nhận `&&i32`: hai dấu `&`, nên mẫu là `&&p`. let pricey = prices.iter().filter(|&&p| p > 4).count(); println!("{} món đắt", pricey);}Kết quả:
[4, 6, 9] [4, 6, 9]2 món đắtVòng lặp cũng vậy: for &(name, qty) in &orders tháo từng tham chiếu, nên hãy dùng name và qty mà không có *. Xem So khớp mẫu.
Cách sửa 4: Kiểm tra kiểu, và dùng trường hoặc Deref cho kiểu bọc
Phần tiêu đề “Cách sửa 4: Kiểm tra kiểu, và dùng trường hoặc Deref cho kiểu bọc”Chọn cách này khi dòng tiêu đề nêu tên kiểu của chính bạn, như type `Meters` cannot be dereferenced. Một struct cũng không phải tham chiếu, kể cả khi nó chỉ bọc đúng một giá trị:
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 dereferencedChương trình bị lỗi:
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);}Để lấy con số bên trong một tuple struct, hãy dùng trường của nó, .0:
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = run.0 + 1.0; println!("{} m", longer);}Kết quả:
3.5 mNếu kiểu của bạn thật sự là một kiểu bọc giống con trỏ thông minh và bạn muốn * dùng được, hãy cài đặt std::ops::Deref cho nó. Chỉ làm vậy với các kiểu bọc; với một “newtype” như Meters, .0 hoặc một phương thức sẽ rõ ràng hơn. Không chắc một biến có kiểu gì? Hãy rê chuột lên nó trong editor (rust-analyzer sẽ hiện kiểu), hoặc tin vào dòng tiêu đề: nó luôn nêu đúng kiểu thật. Xem Struct và Con trỏ thông minh.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”* trên một trường của struct. *config.retries, khi config là một struct (hoặc tham chiếu tới struct) và retries là u32, cho ra type `u32` cannot be dereferenced. Dấu . đã tự đi theo tham chiếu giúp bạn. Cách sửa: config.retries.
Lặp qua HashMap theo giá trị. for (word, count) in counts { total += *count; } cho ra type `u32` cannot be dereferenced, vì lặp qua chính counts cho bạn khóa và giá trị được sở hữu. Cách sửa: bỏ dấu *, hoặc lặp qua &counts nếu bạn còn cần map sau đó. Xem HashMap.
Sau .copied().max() hoặc .cloned().find(...). Những lời gọi này cho ra Option<u32>, không phải Option<&u32>, nên trong if let Some(best) = ... { *best } dấu * là thừa. Cách sửa: dùng thẳng best. Không có .copied() thì .iter().max() cho ra Option<&u32> và *best là đúng.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0308: kiểu không khớp; sai sót ngược lại, thiếu
*ở chỗ một tham chiếu gặp một giá trị thông thường. - E0369: không áp dụng được phép toán hai ngôi cho một kiểu, một lỗi khác bạn có thể gặp khi trộn tham chiếu và giá trị.
- E0507: không thể move ra khỏi một tham chiếu; ở đây
*có dùng được, nhưng nó sẽ move một giá trị không phảiCopy. - E0610:
{integer}là kiểu nguyên thủy và không có trường, “họ hàng” kiểu “nó chỉ là một con số” của lỗi này. - E0599: không tìm thấy phương thức, khi giá trị không phải kiểu bạn tưởng.
- Tất cả lỗi trình biên dịch: danh sách đầy đủ các trang lỗi.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Trong visits.iter().copied().map(|count| *count + 1), vì sao *count gây ra E0614?
2. for score in &scores { total += *score; } với scores là Vec<i32>. Nó có biên dịch được không?
3. prices.iter().map(|&p| *p * 2) gây ra E0614. Phiên bản đúng là gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0614 — Rust error codes index (tiếng Anh): phần giải thích chính thức, với một ví dụ tối giản và cách sửa.
- References and Borrowing — The Rust Book (tiếng Anh):
&và*nghĩa là gì, và vì sao tham chiếu và giá trị là hai kiểu khác nhau. - Treating Smart Pointers Like Regular References — The Rust Book (tiếng Anh):
*đi theo tham chiếu thế nào, và traitDereflàm cho nó dùng được với kiểu của bạn ra sao. Iterator::copied— tài liệu thư viện chuẩn (tiếng Anh): phương thức biến phần tử&TthànhT, lý do phổ biến nhất khiến một dấu*trở thành thừa.- Destructuring pointers — Rust by Example (tiếng Anh): khác biệt giữa
&valtrong mẫu và*valtrong biểu thức.