Bỏ qua để đến nội dung

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.

Đâ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 here
2 | 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 here
2 | 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ã.

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 T mà 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.

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 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 EUR
▶ Tự chạy thử

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)
true
▶ Tự chạy thử

Nế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.

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ả:

100
y
▶ Tự chạy thử

Struct 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 EUR
▶ Tự chạy thử

Chiề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.

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.

  • 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() hay models::User.
  • E0432: import không phân giải được, khi chính dòng use trỏ 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 (Self bê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

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