Lỗi Rust E0599: no method named X found (không tìm thấy phương thức)
Lỗi E0599 nghĩa là bạn gọi một phương thức (method) mà Rust không tìm thấy trên kiểu đó. Lỗi này rất hay gặp, và nguyên nhân thường chỉ là một trong vài thứ: một trait bạn chưa import, một Option bạn chưa lấy giá trị ra, một Vec bạn dùng như iterator, hoặc đơn giản là gõ nhầm tên. Tin vui là các dòng help: của rustc thường chỉ thẳng cho bạn cách sửa. Trang này thuộc hướng dẫn 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[E0599]: no method named `write_all` found for struct `Stdout` in the current scope --> src/main.rs:5:9 |5 | out.write_all(b"Server started\n").unwrap(); | ^^^^^^^^^ | = note: the method is available for `Stdout` here | = help: items from traits can only be used if the trait is in scopehelp: trait `Write` which provides `write_all` is implemented but not in scope; perhaps you want to import it |1 + use std::io::Write; |Trình biên dịch nói rằng Stdout thật ra có write_all, nhưng phương thức đó đến từ trait Write chưa được import, và gợi ý bạn thêm dòng use std::io::Write;.
Đây là chương trình gây ra lỗi:
use std::io;
fn main() { let mut out = io::stdout(); out.write_all(b"Server started\n").unwrap();}Ý nghĩa
Phần tiêu đề “Ý nghĩa”Dấu ^^^^ đánh dấu tên phương thức, write_all, còn dòng đầu tiên cho biết kiểu mà Rust đã tìm trên đó: Stdout. Vậy thông báo có nghĩa là: “Tôi đã xem Stdout và không thấy phương thức nào tên write_all mà bạn được phép dùng ở đây.”
Kiểu trên dòng đầu tiên là manh mối hữu ích nhất. Nếu nó là Option<T>, Result<T, E>, Vec<_> hay một tham số kiểu T trong khi bạn chờ đợi kiểu khác, bạn đã biết mình cần cách sửa nào bên dưới.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Trong Rust, nhiều phương thức không thuộc về chính kiểu đó. Chúng đến từ một trait. write_all đến từ trait Write, và Stdout có cài đặt (implement) trait này. Nhưng phương thức của một trait chỉ dùng được trong file có trait đó trong phạm vi (scope), thường là nhờ một dòng use. Đó chính là ý của câu “items from traits can only be used if the trait is in scope” (phần tử của trait chỉ dùng được khi trait nằm trong phạm vi).
Quy tắc này giúp code dễ đoán. Hai crate khác nhau có thể cùng thêm một phương thức tên write_all cho cùng một kiểu qua hai trait khác nhau. Nếu mọi trait đều luôn có hiệu lực, bạn sẽ không biết lời gọi đó là của trait nào. Nhờ quy tắc này, chỉ những trait bạn đã import mới được tính.
Các nguyên nhân còn lại đến từ hệ thống kiểu chặt chẽ của Rust. Một Option<&str> không phải là &str, nên nó không có các phương thức của &str. Một T generic có thể là bất kỳ kiểu nào, nên Rust chỉ cho bạn gọi những phương thức mà các ràng buộc (bound) trên T hứa hẹn.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Import trait
Phần tiêu đề “Cách sửa 1: Import trait”Chọn cách này khi dòng help nói “items from traits can only be used if the trait is in scope”. Thêm dòng use mà rustc gợi ý.
use std::io::{self, Write};
fn main() { let mut out = io::stdout(); out.write_all(b"Server started\n").unwrap();}Kết quả:
Server startedChuỗi b"..." là chuỗi byte và chỉ chứa được ký tự ASCII, nên ở đây nó vẫn giữ tiếng Anh.
Điều tương tự xảy ra với crate rand. Không có dòng import, rng.random_range(1..=6) sẽ báo lỗi, và rustc cho bạn biết cần đưa trait nào vào:
help: trait `RngExt` which provides `random_range` is implemented but not in scope; perhaps you want to import it |1 + use rand::RngExt;Lưu ý: trong rand 0.10, trait này tên là RngExt. Các bài hướng dẫn cũ viết use rand::Rng, và với rand 0.10 dòng import đó sẽ cho bạn đúng lỗi này. Hãy làm theo dòng help:, đừng làm theo bài hướng dẫn cũ.
Cách sửa 2: Lấy giá trị ra khỏi Option hoặc Result trước
Phần tiêu đề “Cách sửa 2: Lấy giá trị ra khỏi Option hoặc Result trước”Chọn cách này khi kiểu trong thông báo lỗi là Option<T> hoặc Result<T, E>. Giá trị bạn cần nằm bên trong, và bạn phải lấy nó ra trước khi gọi phương thức của nó.
error[E0599]: no method named `to_uppercase` found for enum `Option<T>` in the current scope --> src/main.rs:4:39 |4 | println!("First guest: {}", first.to_uppercase()); | ^^^^^^^^^^^^ method not found in `Option<&&str>`first() trả về một Option, vì danh sách có thể rỗng. rustc gợi ý .expect(...), cách này chạy được nhưng sẽ crash khi gặp None. Xử lý cả hai trường hợp thường tốt hơn:
fn main() { let guests = vec!["ana", "bo", "chen"];
match guests.first() { Some(name) => println!("Khách đầu tiên: {}", name.to_uppercase()), None => println!("Chưa có khách nào"), }
let shout = guests.last().map(|name| name.to_uppercase()); println!("Khách cuối cùng: {:?}", shout);}Kết quả:
Khách đầu tiên: ANAKhách cuối cùng: Some("CHEN").map() chạy code của bạn trên giá trị bên trong và giữ nguyên lớp vỏ Option. Với Result (ví dụ từ .parse()), hãy dùng match, .unwrap_or(...), hoặc ? bên trong một hàm trả về Result. Xem Option và Result.
Cách sửa 3: Gọi .iter() trên Vec
Phần tiêu đề “Cách sửa 3: Gọi .iter() trên Vec”Chọn cách này khi bạn gọi .map(), .filter() hay một phương thức iterator khác trực tiếp trên Vec. Lúc này thông báo lỗi trông khác:
error[E0599]: the method `map` exists for struct `Vec<{integer}>`, but its trait bounds were not satisfied --> src/main.rs:3:35 |3 | let curved: Vec<i32> = scores.map(|s| s + 5).collect(); | ^^^ | = note: the following trait bounds were not satisfied: `Vec<{integer}>: Iterator`Với một số phương thức, như filter, thông báo dễ hiểu hơn. rustc nói Vec<i32> “is not an iterator” (không phải iterator) và thêm help: call `.into_iter()` first. Cùng nguyên nhân, cùng cách sửa.
Vec<{integer}>: Iterator “not satisfied” (không thỏa mãn) vì Vec là một collection, không phải một iterator. Hãy xin nó một iterator bằng .iter() (mượn các phần tử) hoặc .into_iter() (lấy luôn các phần tử):
fn main() { let scores = vec![70, 85, 92]; let curved: Vec<i32> = scores.iter().map(|s| s + 5).collect(); println!("{:?}", curved);}Kết quả:
[75, 90, 97]Cách sửa 4: Thêm ràng buộc trait cho T generic
Phần tiêu đề “Cách sửa 4: Thêm ràng buộc trait cho T generic”Chọn cách này khi thông báo nói phương thức không tìm thấy “for type parameter T” (cho tham số kiểu T). Bên trong một hàm generic, Rust chỉ biết những gì ràng buộc trên T hứa hẹn. T trơn không hứa gì cả, nên nó không có phương thức nào.
error[E0599]: no method named `to_string` found for type parameter `T` in the current scope --> src/main.rs:2:26 |1 | fn describe<T>(item: T) -> String { | - method `to_string` not found for this type parameter2 | format!("[{}]", item.to_string()) | ^^^^^^^^^ method cannot be called on `T` due to unsatisfied trait bounds |help: the following trait defines an item `to_string`, perhaps you need to restrict type parameter `T` with it: |1 | fn describe<T: ToString>(item: T) -> String { | ++++++++++Thêm ràng buộc mà rustc gợi ý. Giờ chỉ những kiểu chuyển được thành chuỗi mới được chấp nhận:
fn describe<T: ToString>(item: T) -> String { format!("[{}]", item.to_string())}
fn main() { println!("{}", describe(42)); println!("{}", describe("sẵn sàng"));}Kết quả:
[42][sẵn sàng]Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”- Gõ nhầm, hoặc dùng tên phương thức của ngôn ngữ khác.
tasks.push_back(...)trênVeccho rahelp: you might have meant to use `push`, còntasks.lenght()cho rahelp: there is a method `len` with a similar name. Cách sửa: dùng tên mà rustc gợi ý. - Gọi
Type::new()mà bạn chưa bao giờ viết. Rust không tự tạo constructor cho bạn:Account::new("Ana", 100)báo lỗino associated function or constant named `new` found for struct `Account`. Cách sửa: thêmimpl Account { fn new(owner: &str, balance: i64) -> Self { ... } }, hoặc tạo trực tiếp bằngAccount { owner: ..., balance: ... }. Xem Struct. - Gọi hàm liên kết (associated function) bằng dấu chấm. Nếu
fn new() -> Selfkhông có tham sốself,acc.new()sẽ lỗi với “this is an associated function, not a method” (đây là hàm liên kết, không phải phương thức). Cách sửa: gọi nó trên kiểu,Account::new().
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0425: cannot find value in this scope: chính cái tên không nhìn thấy được, chứ không phải phương thức trên một kiểu.
- E0277: trait bound not satisfied: một kiểu thiếu trait mà hàm hoặc toán tử cần. Thường xuất hiện cạnh E0599 trong code generic.
- E0308: mismatched types: bạn có
Option<T>ở chỗ cầnT, hoặc ngược lại. - E0382: use of moved value: lỗi hay gặp tiếp theo sau khi đổi từ
.iter()sang.into_iter(). - Tất cả lỗi trình biên dịch: danh sách đầy đủ, kèm tóm tắt ngắn cho từng lỗi.
Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Kiểm tra nhanh
1. rustc nói "items from traits can only be used if the trait is in scope". Bạn nên làm gì?
2. names.first().to_uppercase() báo lỗi E0599 cho Option<&&str>. Vì sao?
3. Bạn gọi scores.filter(...) trên một Vec<i32> và rustc nói "Vec<i32> is not an iterator". Cách sửa là gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0599 — Rust error codes index (tiếng Anh): giải thích chính thức của lỗi này.
- Traits: Defining Shared Behavior — The Rust Book (tiếng Anh): trait thêm phương thức cho kiểu như thế nào, và ràng buộc trait hoạt động ra sao.
- Processing a Series of Items with Iterators — The Rust Book (tiếng Anh):
iter(),into_iter()và các phương thức iterator nhưmap. Option— thư viện chuẩn Rust (tiếng Anh): mọi phương thức để lấy giá trị ra khỏiOption.- The Rust prelude — thư viện chuẩn Rust (tiếng Anh): các trait luôn nằm trong phạm vi, và vì sao
Writekhông nằm trong số đó.