Lỗi Rust E0411: cannot find type Self in this scope (Self nằm ngoài impl)
E0411 nghĩa là bạn đã viết Self ở một chỗ mà Rust không biết nó đại diện cho cái gì. Self là cách viết tắt cho “kiểu mà khối impl (hoặc trait) này nói tới”, nên nó chỉ dùng được bên trong một impl, một trait, hoặc một định nghĩa kiểu. Nguyên nhân thường gặp là một hàm tạo như fn new() -> Self được viết ở cấp cao nhất của file, hoặc một phương thức bạn chép ra khỏi khối impl của nó. Lỗi này phổ biến và sửa nhanh. Khác với E0412, 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 E0411. Trang này thuộc loạt bài về lỗi trình biên dịch.
Thông báo lỗi
Phần tiêu đề “Thông báo lỗi”error[E0411]: cannot find type `Self` in this scope --> src/main.rs:5:21 |5 | fn new_counter() -> Self { // ❌ `Self` outside an `impl` | ----------- ^^^^ `Self` is only available in impls, traits, and type definitions | | | `Self` not allowed in a functionThông báo của trình biên dịch luôn bằng tiếng Anh: “cannot find type Self in this scope” nghĩa là “không tìm thấy kiểu Self 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 Counter { count: u32,}
fn new_counter() -> Self { // ❌ `Self` outside an `impl` Counter { count: 0 }}
fn main() { let c = new_counter(); println!("{}", c.count);}Ý nghĩa
Phần tiêu đề “Ý nghĩa”rustc chỉ ra hai thứ trên dòng 5:
- Dấu
^^^^dướiSelf: “Selfis only available in impls, traits, and type definitions” (Selfchỉ có trong impl, trait và định nghĩa kiểu). Đó là quy tắc, gói gọn trong một dòng. - Dấu
-----dướinew_counter: “Selfnot allowed in a function” (không được dùngSelftrong một hàm). Đây là lý do trong trường hợp này:new_counterlà một hàm bình thường, không thuộcimplnào.
Bạn có thể biết new_counter trả về một Counter, vì bạn vừa viết Counter { count: 0 }. Nhưng Rust không đọc phần thân để đoán Self nghĩa là gì. Self chỉ có nghĩa nhờ một impl Counter { ... } hoặc trait ... { ... } bao quanh nó, và ở đây không có cái nào.
Giống như chữ “ở đây” trên một tờ giấy nhớ. Trên bản đồ, “bạn đang ở đây” thì có nghĩa. Trên một tờ giấy nằm trên bàn bếp, chẳng ai biết “ở đây” là ở đâu.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Self không phải là một kiểu riêng. Nó là một chỗ trống mà mỗi impl hoặc trait tự điền vào: bên trong impl Counter, Self nghĩa là Counter; bên trong impl Timer, nó nghĩa là Timer; bên trong một trait, nó nghĩa là “bất kỳ kiểu nào cài đặt trait này”. Chính điều đó làm nó hữu ích: bạn có thể đổi tên struct, hoặc chép một phương thức sang impl khác, và Self tự đi theo.
Một hàm tự do không thuộc về kiểu nào, nên chẳng có gì để điền vào. Rust có thể thử đoán từ phần thân, nhưng khi đó chữ ký hàm sẽ phụ thuộc vào code bên trong, mà Rust thì cố ý kiểm tra chữ ký một cách độc lập. Xem Struct để biết chỗ ở quen thuộc của fn new() -> Self.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Chuyển hàm vào một khối impl
Phần tiêu đề “Cách sửa 1: Chuyển hàm vào một khối impl”Phần lớn trường hợp, hãy chọn cách này. Một hàm tạo ra hoặc làm việc với một kiểu nên nằm trong khối impl của kiểu đó, và ở đó Self hoạt động đúng như bạn mong đợi. Gọi nó bằng Counter::new():
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Self { count: 0 } }
fn increment(&mut self) { self.count += 1; }}
fn main() { let mut c = Counter::new(); c.increment(); c.increment(); println!("{}", c.count);}Kết quả:
2Bên trong impl, bạn có thể dùng Self vừa làm kiểu (-> Self) vừa để tạo giá trị (Self { count: 0 }). Xem Struct.
Cách sửa 2: Viết tên kiểu thay cho Self
Phần tiêu đề “Cách sửa 2: Viết tên kiểu thay cho Self”Chọn cách này khi hàm nên tiếp tục là một hàm tự do, ví dụ một hàm hỗ trợ tạo ra nhiều kiểu, hoặc một hàm nằm ở module khác. Chỉ cần viết tên kiểu thật, Counter, ở chỗ bạn đã viết Self:
struct Counter { count: u32,}
fn new_counter(start: u32) -> Counter { Counter { count: start }}
fn reset(c: &mut Counter) { c.count = 0;}
fn main() { let mut c = new_counter(10); println!("{}", c.count); reset(&mut c); println!("{}", c.count);}Kết quả:
100Đây cũng là cách sửa cho fn reset(c: &mut Self) bị chép ra khỏi một impl: mọi Self trong chữ ký và phần thân đều đổi thành Counter.
Cách sửa 3: Dùng kiểu generic nếu ý bạn là “kiểu bất kỳ”
Phần tiêu đề “Cách sửa 3: Dùng kiểu generic nếu ý bạn là “kiểu bất kỳ””Chọn cách này khi bạn dùng Self với nghĩa “kiểu nào mà bên gọi muốn”, đó là việc của một generic T, không phải Self. Khai báo <T> cùng các trait mà hàm cần. Ở đây, T: Default cho phép hàm tạo ra giá trị khởi đầu của bất kỳ kiểu nào:
fn fresh_pair<T: Default>() -> (T, T) { (T::default(), T::default())}
fn main() { let numbers: (u32, u32) = fresh_pair(); let words: (String, String) = fresh_pair(); println!("{:?}", numbers); println!("{:?}", words);}Kết quả:
(0, 0)("", "")Nếu bạn muốn mỗi kiểu tự quyết định cách được tạo ra, hãy đặt hàm vào một trait: trait Reset { fn fresh() -> Self; }. Bên trong một trait, Self nghĩa là “kiểu cài đặt trait này”. Xem Generic và Trait.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”Self { ... } trong main hoặc một hàm tự do. let c = Self { count: 0 }; gây ra E0411 với dòng tiêu đề hơi khác: cannot find struct, variant or union type `Self` in this scope. Cách sửa: viết Counter { count: 0 }, hoặc gọi Counter::new().
Một hàm hỗ trợ lồng bên trong một phương thức. Bên trong impl Counter, một phương thức chứa fn helper() -> Self của riêng nó sẽ nhận mã lỗi khác, E0401: can't use `Self` from outer item (không thể dùng Self của mục bên ngoài). Một hàm nằm trong hàm khác không thừa hưởng impl bên ngoài. Cách sửa: viết Counter trong hàm bên trong, hoặc dùng closure, hoặc chuyển helper lên thành một phương thức riêng trong impl.
Nhầm Self với self. Self viết hoa là kiểu; self viết thường là giá trị mà phương thức được gọi trên đó. Dùng self bên ngoài phương thức là một lỗi khác, E0424. Cách sửa cho cả hai: đảm bảo code nằm bên trong đúng khối impl.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0424:
self(viết thường, là giá trị) bị dùng bên ngoài phương thức. - E0425: không tìm thấy một giá trị, hàm hoặc (từ Rust 1.93) kiểu có tên bình thường.
- E0412: mã cũ của “cannot find type”, giờ được báo là E0425.
- E0405: không tìm thấy một trait, thường do thiếu dòng
use. - E0401:
Selfcủa mộtimplbên ngoài bị dùng trong hàm lồng bên trong, 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
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. Bạn có thể dùng Self làm kiểu ở đâu?
2. fn make_user() -> Self ở cấp cao nhất gây ra E0411. Cách sửa thường dùng là gì?
3. Bên trong impl Counter, Self { count: 0 } tạo ra cái gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0411 — Rust error codes index (tiếng Anh): phần giải thích chính thức về ý nghĩa của
Self, và vì sao nó chỉ dùng được trong impl, trait hoặc định nghĩa kiểu. - Method Syntax — The Rust Book (tiếng Anh): các hàm liên kết như
fn square(size: u32) -> Self, và cáchSelfhoạt động trong mộtimpl. - The
Selfkeyword — tài liệu thư viện chuẩn (tiếng Anh):Selfnghĩa là gì trong impl, trait và định nghĩa kiểu. Selfin paths — The Rust Reference (tiếng Anh): các quy tắc chính xác, kể cả lý do các mục lồng bên trong không thể dùngSelfcủa bên ngoài.