Giải thích các lỗi trình biên dịch Rust
Trình biên dịch Rust vừa báo một lỗi đỏ chót và bạn chưa hiểu nó muốn gì? Bạn đến đúng chỗ rồi. Lập trình viên Rust nào cũng gặp những lỗi này, thường là nhiều lần mỗi ngày. Tin vui là: chỉ có một nhóm nhỏ lỗi mà bạn sẽ gặp đi gặp lại, và khi đã quen với chúng, chúng không còn đáng sợ nữa.
Mỗi trang bên dưới nói về một mã lỗi: cho bạn xem thông báo thật, giải thích bằng lời dễ hiểu, và đưa ra các cách sửa mà bạn có thể chạy ngay trên trình duyệt.
Những lỗi thường gặp nhất
Phần tiêu đề “Những lỗi thường gặp nhất”| Lỗi | Trình biên dịch nói gì | Nói đơn giản là |
|---|---|---|
| E0382 | borrow of moved value | Bạn đã trao giá trị đi, rồi lại cố dùng nó lần nữa |
| E0502 | cannot borrow as mutable because it is also borrowed as immutable | Bạn sửa một thứ trong khi vẫn còn chỗ khác đang đọc nó |
| E0499 | cannot borrow as mutable more than once at a time | Hai nơi cùng muốn sửa một thứ vào cùng lúc |
| E0308 | mismatched types | Bạn đưa một kiểu trong khi chỗ đó cần kiểu khác |
| E0277 | the trait bound is not satisfied | Một kiểu không làm được việc bạn yêu cầu (in ra, so sánh, cộng…) |
| E0106 | missing lifetime specifier | Rust không biết tham chiếu được trả về còn hợp lệ trong bao lâu |
| E0425 | cannot find value in this scope | Một cái tên bị gõ sai, hoặc được dùng bên ngoài khối nơi nó tồn tại |
| E0599 | no method named … found | Kiểu đó không có phương thức này, hoặc trait của nó chưa được import |
| E0384 | cannot assign twice to immutable variable | Bạn thay đổi một biến không được khai báo mut |
| E0596 | cannot borrow as mutable, as it is not declared as mutable | Bạn cố thay đổi một thứ qua biến hoặc tham chiếu không phải mut |
| E0597 | borrowed value does not live long enough | Một tham chiếu vẫn đang được dùng sau khi giá trị mà nó trỏ tới đã mất |
| E0515 | cannot return reference to local variable | Bạn trả về tham chiếu tới một thứ sẽ bị hủy khi hàm kết thúc |
| E0373 | closure may outlive the current function, but it borrows … | Một closure (thường là luồng) mượn một biến cục bộ có thể đã mất trước khi nó chạy |
| E0369 | binary operation == cannot be applied to type … |
Kiểu của bạn (chưa) hỗ trợ ==, <, + và các toán tử tương tự |
| E0432 | unresolved import | Một dòng use trỏ tới crate, module hoặc thành phần không tìm thấy |
| E0433 | cannot find type … in this scope (older: failed to resolve) | Một đường dẫn như HashMap::new() dùng tên chưa được import hoặc không tồn tại |
| E0424 | self value is a keyword only available in methods with a self parameter |
Bạn dùng self trong một hàm không nhận self |
| E0061 | this function takes N arguments but M arguments were supplied | Bạn truyền thừa hoặc thiếu đối số (Rust không có đối số tùy chọn) |
| E0594 | cannot assign to p.hp, as p is not declared as mutable |
Bạn thay đổi một trường hoặc một giá trị sau & mà không có quyền mut |
| E0004 | non-exhaustive patterns: X not covered |
match của bạn chưa xử lý hết mọi giá trị có thể |
| E0507 | cannot move out of x which is behind a shared reference |
Bạn cố lấy quyền sở hữu một thứ mà bạn chỉ đang mượn |
| E0603 | function x is private |
Bạn dùng một hàm, struct hoặc module không phải pub |
| E0716 | temporary value dropped while borrowed | Bạn giữ tham chiếu tới một giá trị bị bỏ đi ngay cuối dòng |
| E0463 | can’t find crate for x |
Một crate (hoặc thư viện chuẩn cho một target) chưa được cài |
| E0368 | binary assignment operation += cannot be applied to type … |
Kiểu của bạn không hỗ trợ +=, -= và các toán tử tương tự |
| E0057 | this function takes N arguments but M were supplied (note: closure defined here) | Bạn gọi một closure với thừa hoặc thiếu đối số |
| E0282 | type annotations needed | Rust không tự suy ra được một kiểu, nên bạn phải ghi rõ |
| E0609 | no field x on type T |
Tên trường bị gõ sai, hoặc giá trị đang được bọc (ví dụ trong Option) |
| E0593 | closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments | Closure nhận các đối số riêng lẻ trong khi chỗ đó cần một tuple (hoặc số lượng khác) |
| E0631 | type mismatch in closure arguments | Kiểu tham số của closure không khớp với thứ nó nhận được (thường là i32 với &i32) |
| E0005 | refutable pattern in local binding | let Some(x) = … có thể thất bại, nên let thường không chấp nhận |
| E0615 | attempted to take value of method len on type String |
Bạn viết s.len thay vì s.len() |
| E0610 | {integer} is a primitive type and therefore doesn’t have fields |
Bạn dùng .field trên một số, bool hoặc char |
| E0560 | struct User has no field named nmae |
Khi tạo struct, bạn dùng một tên trường mà struct không có |
| E0616 | field balance of struct Account is private |
Bạn đọc một trường không phải pub từ bên ngoài module của nó |
| E0451 | field x of struct Y is private |
Bạn tạo struct bằng cú pháp literal trong khi có trường là private |
| E0624 | method x is private |
Bạn gọi một phương thức hoặc hàm liên kết không phải pub |
| E0283 | type annotations needed (cannot infer type … multiple impls satisfy this bound) | Có nhiều kiểu cùng hợp (ví dụ .collect(), .sum()), nên bạn phải chọn một |
| E0284 | type annotations needed: cannot satisfy <_ as FromStr>::Err == _ |
"42".parse() không biết phải phân tích thành kiểu nào |
| E0790 | cannot call associated function on trait without specifying the corresponding impl type |
Bạn viết Default::default() hoặc Trait::new() mà không nói rõ kiểu nào |
| E0559 | variant Shape::Circle has no field named r |
Khi tạo một biến thể enum, bạn dùng tên trường mà biến thể đó không có |
| E0063 | missing field email in initializer of User |
Bạn tạo struct nhưng bỏ sót một hoặc vài trường |
| E0689 | can’t call method pow on ambiguous numeric type {integer} |
2.pow(10) hay 2.0.sqrt() cần một kiểu số cụ thể như i32 hoặc f64 |
| E0034 | multiple applicable items in scope | Hai trait cho kiểu của bạn một phương thức cùng tên, nên Rust không chọn được |
| E0412 | cannot find type X in this scope (Rust 1.93+ prints E0425) |
Tên kiểu bị gõ sai, chưa import hoặc chưa khai báo |
| E0428 | the name area is defined multiple times |
Hai thành phần dùng chung một tên; Rust không có nạp chồng hàm (overloading) |
| E0026 | variant Shape::Circle does not have a field named r |
Một mẫu nhắc tới trường mà struct hoặc biến thể không có |
| E0027 | pattern does not mention fields id, paid |
Mẫu struct bỏ sót trường; hãy thêm vào hoặc kết thúc bằng .. |
| E0533 | expected unit struct, unit variant or constant, found struct variant Shape::Circle |
Một biến thể dạng struct (hoặc một hàm) được dùng ở chỗ cần một tên đơn thuần; thêm { .. } |
| E0592 | duplicate definitions with name reset |
Hai khối impl định nghĩa phương thức trùng tên |
| E0255 | the name Error is defined multiple times |
Một dòng use import một tên trùng với kiểu hoặc hàm của chính bạn |
| E0252 | the name Result is defined multiple times |
Hai dòng use đưa vào cùng một tên; đổi tên một cái bằng as |
| E0411 | cannot find type Self in this scope |
Bạn dùng Self bên ngoài impl hoặc trait |
| E0405 | cannot find trait Display in this scope |
Tên trait bị gõ sai hoặc chưa import (ví dụ use std::fmt::Display;) |
| E0532 | expected unit struct, unit variant or constant, found tuple variant Shape::Circle |
Mẫu ghi tên một biến thể tuple (hoặc struct) mà thiếu ( ), hoặc thêm ( ) ở chỗ không cần |
| E0164 | expected tuple struct or tuple variant, found struct variant Shape::Circle |
Mẫu dùng ( ) cho biến thể dạng struct hoặc thành phần liên kết; hãy dùng { } hoặc guard |
| E0404 | expected trait, found derive macro Debug |
Bạn viết impl X for hoặc T: X trong khi X là struct, alias hoặc derive macro chưa import |
| E0401 | can’t use generic parameters from outer item | Một fn bên trong dùng T hoặc Self của hàm bên ngoài; cho nó generic riêng hoặc dùng closure |
| E0107 | enum takes 2 generic arguments but 1 generic argument was supplied | Result<u32>, HashMap<String> và những trường hợp tương tự: thiếu hoặc thừa đối số trong < > |
| E0530 | match bindings cannot shadow tuple variants | Một tên biến thể trần như Circle => (sau use Shape::*) không thể là biến mới |
| E0434 | can’t capture dynamic environment in a fn item | Một fn bên trong không thấy được biến cục bộ bên ngoài; dùng closure hoặc truyền vào |
| E0601 | main function not found in crate main |
Chương trình không có fn main() (thiếu, gõ sai, nằm trong mod, hoặc thực ra là thư viện) |
| E0658 | use of unstable library feature iter_intersperse |
Bạn dùng API chỉ có trên nightly với Rust stable; dùng cách thay thế stable hoặc cập nhật |
| E0614 | type u32 cannot be dereferenced |
Bạn viết *x trong khi x đã là giá trị thường, không phải tham chiếu |
| E0435 | attempt to use a non-constant value in a constant | Một const, độ dài mảng hoặc static dùng biến let; đổi nó thành const hoặc dùng Vec |
| E0554 | #![feature] may not be used on the stable release channel |
Code chép từ nightly; bỏ thuộc tính đó và dùng API stable, hoặc chủ động dùng nightly |
Cách đọc một lỗi Rust
Phần tiêu đề “Cách đọc một lỗi Rust”Thông báo lỗi của Rust nổi tiếng là hữu ích, nhưng chúng chứa khá nhiều thông tin. Đây là một lỗi thật:
fn main() { let name = String::from("Ferris"); let greeting = name; println!("{name}");}error[E0382]: borrow of moved value: `name` --> src/main.rs:4:16 |2 | let name = String::from("Ferris"); | ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait3 | let greeting = name; | ---- value moved here4 | println!("{name}"); | ^^^^ value borrowed here after move |help: consider cloning the value if the performance cost is acceptable |3 | let greeting = name.clone(); | ++++++++Thông báo của trình biên dịch luôn bằng tiếng Anh. Hãy đọc nó theo thứ tự này:
- Dòng đầu tiên là phần tóm tắt: mã lỗi (
E0382) và một dòng mô tả. - Dòng có mũi tên (
--> src/main.rs:4:16) cho bạn biết file, dòng và cột nơi xảy ra vấn đề. - Các dấu
^^^^chỉ đúng vị trí của lỗi. Các dấu----là “câu chuyện” dẫn tới lỗi đó. Ở đây chúng nói: giá trị được tạo ở dòng 2, bị move (di chuyển) ở dòng 3, rồi lại được dùng ở dòng 4. help:là cách sửa mà trình biên dịch gợi ý, thường kèm đúng đoạn code cần thêm (các dấu++++cho thấy ký tự mới). Đó không phải lúc nào cũng là cách sửa tốt nhất, nhưng là điểm khởi đầu rất tốt.
Hãy bắt đầu đọc từ lỗi đầu tiên. Một sai sót có thể kéo theo cả chuỗi lỗi phía sau, và sửa lỗi đầu tiên thường làm những lỗi còn lại biến mất.
Ba thói quen giúp ích
Phần tiêu đề “Ba thói quen giúp ích”-
Hỏi trình biên dịch thêm. Mỗi mã lỗi đều có sẵn một phần giải thích dài hơn. Hãy chạy lệnh này trong terminal:
Terminal window rustc --explain E0382Bạn sẽ nhận được một mô tả ngắn, một ví dụ lỗi và một ví dụ đã sửa (bằng tiếng Anh).
-
Kiểm tra thường xuyên.
cargo checktìm lỗi mà không cần build cả chương trình, nên nhanh hơncargo buildrất nhiều. Chạy nó sau mỗi vài dòng code nghĩa là bạn gặp từng lỗi một, chứ không phải hai mươi lỗi một lúc. Xem Cargo và crate. -
Để trình soạn thảo hiện lỗi ngay khi bạn gõ. Với tiện ích rust-analyzer, lỗi hiện ngay trong code của bạn, kèm cùng phần gợi ý. Xem Công cụ và cài đặt Rust.
Vì sao Rust có nhiều lỗi đến vậy
Phần tiêu đề “Vì sao Rust có nhiều lỗi đến vậy”Phần lớn các lỗi này tồn tại vì Rust kiểm tra trước khi chương trình chạy những thứ mà các ngôn ngữ khác chỉ phát hiện lúc chạy (hoặc không bao giờ): dùng vùng nhớ đã giải phóng, hai luồng cùng sửa một dữ liệu, quên kiểm tra null. Mỗi lỗi bạn sửa là một bug sẽ không bao giờ đến tay người dùng.
Vì vậy nhiều người nói: với Rust, cuộc chiến diễn ra lúc biên dịch, sau đó chương trình cứ thế chạy. Các quy tắc đằng sau những lỗi thường gặp nhất được giải thích trong Quyền sở hữu, Mượn và Lifetime.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Một lỗi Rust báo nhiều vấn đề. Bạn nên sửa cái nào trước?
2. Lệnh rustc --explain E0382 làm gì?
3. Trong thông báo lỗi, các dấu ^^^^ cho thấy điều gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- Rust Compiler Error Index (tiếng Anh): danh sách chính thức mọi mã lỗi, mỗi mã có một ví dụ lỗi và một ví dụ đã sửa.
- Understanding Ownership — The Rust Book (tiếng Anh): các quy tắc đằng sau những lỗi mượn và move thường gặp nhất.
- Error Handling — The Rust Book (tiếng Anh): cách Rust xử lý lỗi trong chính chương trình của bạn (
Resultvàpanic!), không chỉ lỗi biên dịch. - What is rustc? — The rustc Book (tiếng Anh): trình biên dịch hoạt động thế nào, kể cả các cờ (flag) và mức lint.