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

Lỗi Rust E0405: cannot find trait in this scope (không tìm thấy trait)

E0405 nghĩa là bạn đã ghi tên một trait mà Rust không nhìn thấy được từ chỗ đó. Trường hợp kinh điển: bạn viết impl Display for Point để struct của mình in ra được, nhưng chưa bao giờ import Display. Gõ sai tên trait, hoặc một trait nằm ở module khác, cũng cho ra cùng lỗi này. Đây là một trong những lỗi dễ sửa nhất, vì rustc thường in ra đúng dòng use bạn cần. Khác với E0412 (“cannot find type”), mã đã trở thành E0425 từ Rust 1.93, mã lỗi này không thay đổi: rustc 1.97 vẫn in ra E0405 cho trait. Trang này thuộc loạt bài về lỗi trình biên dịch.

error[E0405]: cannot find trait `Display` in this scope
--> src/main.rs:6:6
|
6 | impl Display for Point { // ❌ `Display` was never imported
| ^^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::fmt::Display;
|

Thông báo của trình biên dịch luôn bằng tiếng Anh: “cannot find trait Display in this scope” nghĩa là “không tìm thấy trait Display trong phạm vi này”. Đâ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):

struct Point {
x: i32,
y: i32,
}
impl Display for Point { // ❌ `Display` was never imported
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
}

Dấu ^^^^^^^ chỉ vào Display ở dòng 6, kèm nhãn “not found in this scope” (không có trong phạm vi này). Rust đã xem mọi trait nhìn thấy được từ chỗ đó: những trait bạn định nghĩa trong module này, những trait mà các dòng use đưa vào, và một nhóm nhỏ trong prelude. Không cái nào tên là Display.

Dòng help: cho bạn biết nó nằm ở đâu: use std::fmt::Display;. Ký hiệu 1 + nghĩa là “thêm cái này thành dòng 1 mới”, ở đầu file.

Vì sao lại thiếu Display trong khi println! dùng nó suốt? Prelude, danh sách những tên mà mọi file Rust được dùng miễn phí, có Clone, Iterator, From, Default và các trait thường ngày khác, nhưng không có Display, Error, Hash hay Write. Những trait đó nằm trong module của chúng (std::fmt, std::error, std::hash, std::io), và bạn import chúng khi cài đặt chúng.

Giống như một công thức nấu ăn ghi “dùng cái chảo Display”. Người nấu biết những cái chảo đang nằm trên bếp, nhưng cái này lại cất trong tủ, và công thức chẳng hề nói là tủ nào.

Rust không bao giờ lục cả thư viện chuẩn, hay cả dự án của bạn, để tìm một cái tên. Nó chỉ nhìn những gì đang ở trong phạm vi: các mục của chính bạn, các dòng use và prelude. Nhờ vậy tên gọi luôn dễ đoán. Chỉ riêng trong std đã có vài trait tên Write và Error, và một crate bạn thêm sau này có thể mang tới nhiều hơn nữa. Nếu Rust tự chọn giúp bạn, việc thêm một dependency có thể âm thầm làm thay đổi ý nghĩa code của bạn.

Vì vậy bạn nói rõ mình muốn trait nào, một lần, ở đầu file. Dòng help đã tìm giúp bạn và gợi ý trait nó tìm được. Xem Module để biết use hoạt động thế nào, và Các trait thông dụng để biết về Display.

Chọn cách này khi help nói consider importing this trait (hãy cân nhắc import trait này), điều xảy ra trong phần lớn trường hợp. Chép dòng đó lên đầu file:

use std::fmt::Display;
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
println!("Ferris đang ở {}", Point { x: -1, y: 7 });
}

Kết quả:

(3, 4)
Ferris đang ở (-1, 7)
▶ Tự chạy thử

Nếu rustc gợi ý nhiều import, hãy đọc kỹ trước khi chọn. Đó là những trait khác nhau tình cờ trùng tên, như std::fmt::Write và std::io::Write.

Cách sửa 2: Import module và viết fmt::Display

Phần tiêu đề “Cách sửa 2: Import module và viết fmt::Display”

Chọn cách này khi bạn đang cài đặt Display, vì đằng nào bạn cũng cần Formatter và Result từ cùng module đó. Import std::fmt một lần và viết fmt:: phía trước cả ba. Đây là phong cách bạn sẽ gặp nhiều nhất trong code Rust thực tế:

use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
let label = p.to_string();
println!("{} có {} ký tự", label, label.len());
}

Kết quả:

(3, 4) có 6 ký tự
▶ Tự chạy thử

Cài đặt Display cũng cho bạn .to_string() miễn phí. Bạn có thể bỏ hẳn use và viết impl std::fmt::Display for Point; với trường hợp dùng một lần thì như vậy cũng ổn.

Chọn cách này khi rustc nói a trait with a similar name exists (có một trait với tên gần giống). Tên trait phân biệt chữ hoa chữ thường, nên shape, Shap và Shape là ba tên khác nhau:

error[E0405]: cannot find trait `Shap` in this scope
--> src/main.rs:9:6
|
1 | trait Shape {
| ----------- similarly named trait `Shape` defined here
...
9 | impl Shap for Square { // ❌ typo: Shap
| ^^^^
|
help: a trait with a similar name exists
|
9 | impl Shape for Square { // ❌ typo: Shap
| +

Chương trình bị lỗi:

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shap for Square { // ❌ typo: Shap
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

Dấu + dưới dòng 9 cho thấy chữ cái bị thiếu. Hãy thêm nó vào:

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

Kết quả:

9
▶ Tự chạy thử

Nếu không có tên gần giống nào và cũng không có gợi ý import, nghĩa là trait đó chưa tồn tại. Hãy định nghĩa nó bằng trait Shape { ... } trước. Xem Trait.

Cách sửa 4: Import trait của chính bạn từ module của nó

Phần tiêu đề “Cách sửa 4: Import trait của chính bạn từ module của nó”

Chọn cách này khi trait là của bạn nhưng nằm ở module khác. Mỗi module có phạm vi riêng, nên một trait trong mod shapes sẽ không nhìn thấy được ở đầu main.rs cho tới khi bạn import nó:

error[E0405]: cannot find trait `Shape` in this scope
--> src/main.rs:11:6
|
11 | impl Shape for Square { // ❌ `Shape` lives in `mod shapes`
| ^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use crate::shapes::Shape;
|

Chương trình bị lỗi:

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
struct Square {
side: f64,
}
impl Shape for Square { // ❌ `Shape` lives in `mod shapes`
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

Thêm dòng use, và kiểm tra xem trait có là pub không. Thiếu pub thì chính dòng import sẽ hỏng với E0603, “trait is private” (trait là riêng tư):

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
use crate::shapes::Shape;
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

Kết quả:

4
▶ Tự chạy thử

Trait từ crate cũng vậy: impl Serialize for User cần use serde::Serialize; trước. Xem Module.

Box<dyn Error> trong main hoặc một hàm hỗ trợ. fn run() -> Result<(), Box<dyn Error>> mà không import sẽ cho ra cannot find trait `Error` in this scope, kèm help: consider importing this trait, use std::error::Error;. Cách sửa: thêm dòng đó, hoặc viết Box<dyn std::error::Error>. Xem Lỗi tùy chỉnh.

Ràng buộc trait (trait bound) trên generic. fn print_all<T: Display>(items: &[T]) cho ra cùng lỗi E0405 với Display, chỉ vào ràng buộc đó. Cách sửa: use std::fmt::Display;. Xem Generic.

Debug cho ra mã khác. impl Debug for Point hoặc T: Debug mà không import thì không phải E0405 mà là E0404: expected trait, found derive macro `Debug` (cần một trait, nhưng gặp derive macro Debug). Prelude có macro #[derive(Debug)], nhưng không có trait Debug. Cách sửa: use std::fmt::Debug; (rustc gợi ý dòng này), hoặc chỉ cần #[derive(Debug)] nếu bạn không cần định dạng tùy chỉnh.

  • E0425: không tìm thấy một giá trị, hàm, hoặc (từ Rust 1.93) một kiểu trong phạm vi này.
  • E0412: mã cũ của “cannot find type”, giờ được báo là E0425.
  • E0433: không tìm thấy phần đầu của một đường dẫn, như fmt::Display khi thiếu use std::fmt;.
  • E0599: không tìm thấy phương thức, thường vì trait cung cấp phương thức đó chưa được import.
  • E0277: đã tìm thấy trait, nhưng kiểu của bạn không cài đặt nó.
  • E0404: một cái tên có tồn tại, nhưng không phải trait (như macro Debug), như ở trên.
  • Tất 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 viết impl Display for Invoice và gặp E0405. Còn thiếu gì?

2. Trait nào dưới đây dùng được mà không cần dòng use nào?

3. Trait Shape của bạn được định nghĩa trong mod shapes, còn impl Shape for Square nằm trong main.rs. Bạn cần gì?

Điểm: 0 / 3