Lỗi Rust E0412: cannot find type in this scope (không tìm thấy kiểu)
E0412 nghĩa là bạn viết một tên kiểu mà Rust không nhìn thấy được từ chỗ đó, ví dụ trong chữ ký hàm như fn print_invoice(inv: &Invoce). Đây là lỗi rất phổ biến và dễ sửa: gõ nhầm, thiếu một dòng use, hoặc quên khai báo một generic <T>. Có một điều có thể làm bạn bối rối lúc đầu: từ Rust 1.93, trình biên dịch in lỗi này dưới mã E0425, với cùng thông báo cannot find type. Trang này nói về cả hai, và 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”Đây là thứ Rust 1.93 trở lên in ra (trình biên dịch hiện tại là 1.97):
error[E0425]: cannot find type `Invoce` in this scope --> src/main.rs:2:24 |1 | struct Invoice { total: u32 } | -------------- similarly named struct `Invoice` defined here2 | fn print_invoice(inv: &Invoce) { // ❌ typo: Invoce | ^^^^^^ |help: a struct with a similar name exists |2 | fn print_invoice(inv: &Invoice) { // ❌ typo: Invoce | +Và đây là cùng chương trình đó trên Rust 1.92 hoặc cũ hơn, phiên bản mà phần lớn các bài blog và câu trả lời trên Stack Overflow đã dùng:
error[E0412]: cannot find type `Invoce` in this scope --> src/main.rs:2:24 |1 | struct Invoice { total: u32 } | -------------- similarly named struct `Invoice` defined here2 | fn print_invoice(inv: &Invoce) { | ^^^^^^ help: a struct with a similar name exists: `Invoice`Cả hai đều báo không tìm thấy kiểu Invoce trong phạm vi này, và gợi ý struct có tên gần giống là Invoice. Đây là chương trình gây ra cả hai:
struct Invoice { total: u32 }fn print_invoice(inv: &Invoce) { // ❌ typo: Invoce println!("{}", inv.total);}
fn main() { print_invoice(&Invoice { total: 120 });}Trang E0412 chính thức (tiếng Anh) giờ mở đầu bằng câu “this error code is no longer emitted by the compiler” (mã lỗi này không còn được trình biên dịch đưa ra). Vấn đề không biến mất, nó chỉ chuyển sang E0425, mã lỗi vốn cũng dùng cho biến và hàm bị thiếu. Mọi thứ dưới đây áp dụng như nhau cho cả hai mã.
Ý nghĩa
Phần tiêu đề “Ý nghĩa”Dấu ^^^^^^ chỉ vào Invoce trong chữ ký. Để kiểm tra hàm này, Rust phải biết Invoce là gì: nó lớn bao nhiêu, có những field nào. Rust đã xem mọi kiểu nhìn thấy được từ dòng 2 (struct và enum của bạn, các dòng use và prelude) và không tìm thấy gì mang tên đó.
Dòng help: cho bạn biết mình đang ở tình huống nào:
- “a struct with a similar name exists”: gõ nhầm. Dấu
+chỉ chữ cái bị thiếu. - “consider importing this struct”: kiểu có tồn tại ở chỗ khác, và bạn cần một dòng
use. - “you might be missing a type parameter”: bạn dùng một generic
Tmà không khai báo<T>. - không có help nào: kiểu chưa tồn tại. Bạn chưa viết nó, hoặc nó nằm trong một file mà Rust không biết.
Giống như một công thức nấu ăn ghi “thêm 200 g Invoce”. Đầu bếp biết bột mì và đường là gì, nhưng không biết “Invoce”, nên dừng lại trước khi kịp bắt đầu.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Rust kiểm tra chữ ký của mọi hàm trước khi chương trình chạy. Mỗi tham số và kiểu trả về phải nêu tên một kiểu có thật, đã biết, vì Rust dùng nó để quyết định giá trị cần bao nhiêu bộ nhớ và có những phương thức, field nào. Một cái tên không tìm thấy không phải thứ mà Rust chịu đoán.
Chỉ một nhóm nhỏ tên kiểu nhìn thấy được ở mọi nơi mà không cần use: đó là prelude, với String, Vec, Option, Result, Box và vài cái nữa. Mọi thứ khác cần hoặc là định nghĩa của chính bạn, hoặc một dòng use, hoặc một đường dẫn đầy đủ như std::num::ParseIntError. Xem Module để biết đường dẫn và use hoạt động thế nào.
Vì sao mã lỗi lại thay đổi? Từ Rust 1.93, rustc báo kiểu bị thiếu dưới mã E0425, cùng mã mà nó đã dùng cho biến hay hàm bị thiếu. Thông báo vẫn ghi type, nên bạn luôn biết loại tên nào bị thiếu.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Sửa chính tả, hoặc định nghĩa kiểu
Phần tiêu đề “Cách sửa 1: Sửa chính tả, hoặc định nghĩa kiểu”Chọn cách này khi rustc nói có một tên gần giống, hoặc không đưa ra gợi ý nào. Tên kiểu phân biệt chữ hoa chữ thường: invoice, Invoce và Invoice là ba tên khác nhau. Nếu không có tên nào gần giống, hãy viết struct hoặc enum trước:
struct Invoice { total: u32,}
fn print_invoice(inv: &Invoice) { println!("Tổng hóa đơn: {} EUR", inv.total);}
fn main() { print_invoice(&Invoice { total: 120 });}Kết quả:
Tổng hóa đơn: 120 EURCách sửa 2: Thêm dòng use mà rustc gợi ý
Phần tiêu đề “Cách sửa 2: Thêm dòng use mà rustc gợi ý”Chọn cách này khi phần help nói consider importing this struct (hoặc enum, hoặc type alias). Các kiểu của thư viện chuẩn nằm ngoài prelude, như HashMap, ParseIntError hay Ordering, cần một dòng use. Ở đây một hàm trả về lỗi parse:
error[E0425]: cannot find type `ParseIntError` in this scope --> src/main.rs:1:40 |1 | fn parse_age(text: &str) -> Result<u8, ParseIntError> { | ^^^^^^^^^^^^^ not found in this scope |help: consider importing this struct |1 + use std::num::ParseIntError; |Hãy chép dòng được gợi ý lên đầu file:
use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u8, ParseIntError> { text.trim().parse()}
fn main() { println!("{:?}", parse_age(" 42 ")); println!("{:?}", parse_age("forty").is_err());}Kết quả:
Ok(42)trueNếu bạn viết HashMap::new() (một đường dẫn có ::) thay vì một kiểu trong chữ ký, lỗi sẽ là E0433, và cùng dòng use đó sửa được nó. Nếu rustc gợi ý nhiều dòng import, hãy đọc kỹ trước khi chọn; chúng là những kiểu khác nhau chỉ trùng tên.
Cách sửa 3: Khai báo tham số kiểu generic
Phần tiêu đề “Cách sửa 3: Khai báo tham số kiểu generic”Chọn cách này khi phần help nói you might be missing a type parameter. Trong Rust, T không phải từ khóa đặc biệt. Đó là một cái tên do bạn tự đặt, và bạn phải khai báo nó trong dấu ngoặc nhọn sau tên hàm. Không có <T>, Rust sẽ đi tìm một kiểu thật tên là T:
error[E0425]: cannot find type `T` in this scope --> src/main.rs:1:21 |1 | fn largest(items: &[T]) -> &T { | ^ not found in this scope |help: you might be missing a type parameter |1 | fn largest<T>(items: &[T]) -> &T { | +++Bạn nhận cùng lỗi này một lần cho mỗi chỗ T xuất hiện. Hãy thêm <T>, cùng với các trait mà hàm cần. Để so sánh các phần tử bằng >, T phải cài đặt PartialOrd:
fn largest<T: PartialOrd>(items: &[T]) -> &T { let mut best = &items[0]; for item in items { if item > best { best = item; } } best}
fn main() { println!("{}", largest(&[34, 50, 25, 100, 65])); println!("{}", largest(&['y', 'm', 'a', 'q']));}Kết quả:
100yStruct cũng vậy: struct Wrapper { items: Vec<Item> } sẽ lỗi trừ khi bạn viết struct Wrapper<Item> hoặc định nghĩa một kiểu Item thật. Bỏ : PartialOrd đi thì bạn sẽ nhận E0369 ở dấu >. Xem Generic.
Cách sửa 4: Import một kiểu từ module khác
Phần tiêu đề “Cách sửa 4: Import một kiểu từ module khác”Chọn cách này khi kiểu là của bạn nhưng nằm ở một module khác. Một điều hay gây bất ngờ: một module bên trong file của bạn cũng không nhìn thấy các kiểu ở đầu file. Mỗi module có phạm vi (scope) riêng:
error[E0425]: cannot find type `Order` in this scope --> src/main.rs:7:26 |7 | pub fn print(order: &Order) -> String { | ^^^^^ not found in this scope |help: consider importing this struct |7 + use crate::Order; |7 + nghĩa là “thêm dòng này bên trong module, ở dòng 7”. crate::Order là đường dẫn tính từ gốc crate của bạn; super::Order (“từ module cha”) cũng dùng được:
struct Order { id: u32, total: u32,}
mod receipts { use crate::Order;
pub fn print(order: &Order) -> String { format!("Đơn hàng #{}: {} EUR", order.id, order.total) }}
fn main() { let order = Order { id: 7, total: 42 }; println!("{}", receipts::print(&order));}Kết quả:
Đơn hàng #7: 42 EURChiều ngược lại cũng vậy: một kiểu định nghĩa trong mod shop cần pub struct Product và use crate::shop::Product; (hoặc đường dẫn đầy đủ shop::Product) thì main mới gọi tên được. Bạn sẽ gặp chuyện này nhiều nhất trong mod tests, nơi use super::*; ở đầu import mọi thứ từ module cha.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”Một kiểu từ file mà bạn chưa khai báo. fn save(user: models::User) khi có file src/models.rs nhưng không có dòng mod models; thì không phải E0412 hay E0425 mà là E0433: cannot find module or crate `models` in this scope. Cách sửa: thêm mod models; ở đầu main.rs, và cho struct là pub.
Self bên ngoài impl. fn new_counter() -> Self ở cấp cao nhất có mã lỗi riêng, E0411: cannot find type `Self` in this scope, kèm nhãn `Self` is only available in impls, traits, and type definitions. Cách sửa: chuyển hàm vào impl Counter { ... }, hoặc viết Counter thay cho Self.
Một trait thay vì một kiểu. impl Display for Point mà không import Display cho ra E0405, cannot find trait `Display` in this scope, kèm help: consider importing this trait, use std::fmt::Display;. Cùng ý tưởng, cùng cách sửa: thêm dòng use. Xem Trait.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0425: không tìm thấy giá trị, hàm, và từ Rust 1.93 còn cả kiểu. Đây là mã bạn thật sự thấy ngày nay.
- E0433: không tìm thấy phần đầu của một đường dẫn như
HashMap::new()haymodels::User. - E0432: import không phân giải được, khi chính dòng
usetrỏ vào thứ không tồn tại. - E0603: kiểu đã được tìm thấy, nhưng nó không
pub. - E0405 (không tìm thấy trait) và E0411 (
Selfbên ngoài impl): những “họ hàng” gần được nêu ở trên. - Tất cả cá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. Bạn làm theo một hướng dẫn có error[E0412]: cannot find type, nhưng trình biên dịch của bạn in error[E0425]: cannot find type. Vì sao?
2. fn first(items: &[T]) -> &T cho ra cannot find type T. Cách sửa là gì?
3. Một hàm bên trong mod billing dùng Order, một struct định nghĩa ở đầu main.rs. Dòng nào, đặt bên trong module, sửa được lỗi?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0412 — Rust error codes index (tiếng Anh): trang chính thức, với ghi chú rằng mã này không còn được đưa ra, cùng các ví dụ kinh điển.
- E0425 — Rust error codes index (tiếng Anh): mã mà trình biên dịch ngày nay dùng cho lỗi này.
- Generic Data Types — The Rust Book (tiếng Anh): cách khai báo
<T>, với cùng hàmlargestnhư Cách sửa 3. - Bringing Paths into Scope with the
useKeyword — The Rust Book (tiếng Anh):use,crate::vàsuper::cho các kiểu từ module khác. std::prelude— tài liệu thư viện chuẩn (tiếng Anh): danh sách các tên bạn dùng được mà không cần import.