Lỗi Rust E0107: wrong number of generic arguments (sai số đối số generic)
E0107 nghĩa là số lượng kiểu bên trong <...> không khớp với số mà kiểu hoặc hàm đó cần. Result cần hai (Result<T, E>), HashMap cần hai (HashMap<K, V>), Option và Vec cần một. Đưa thiếu một hoặc thừa một là rustc sẽ nói kiểu như enum takes 2 generic arguments but 1 generic argument was supplied (enum nhận 2 đối số generic nhưng chỉ được đưa 1), với “struct”, “enum”, “type alias”, “function” hoặc “method” ở đầu câu. Đây là một trong những lỗi dễ chịu nhất: rustc cho bạn biết cả hai con số, và cách sửa là thêm hoặc bớt một kiểu. 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[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied --> src/main.rs:1:29 |1 | fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types | ^^^^^^ --- supplied 1 generic argument | | | expected 2 generic arguments |help: add missing generic argument |1 | fn parse_age(text: &str) -> Result<u32, E> { // ❌ `Result` needs two types | +++Thông báo của trình biên dịch luôn bằng tiếng Anh. Đây là chương trình gây ra lỗi đó (chuỗi và chú thích được giữ bằng tiếng Anh vì code ví dụ lỗi được giữ nguyên như bản gốc):
fn parse_age(text: &str) -> Result<u32> { // ❌ `Result` needs two types text.trim().parse()}
fn main() { for input in ["42", "forty"] { match parse_age(input) { Ok(age) => println!("age: {}", age), Err(e) => println!("error: {}", e), } }}Ý nghĩa
Phần tiêu đề “Ý nghĩa”rustc chỉ vào dòng 1 hai lần:
- Dấu
^^^^^^dướiResult: “expected 2 generic arguments” (cần 2 đối số generic).Resultlà một enum có hai tham số kiểu:Tcho giá trị khi thành công vàEcho lỗi. - Dấu
---dướiu32: “supplied 1 generic argument” (được đưa 1 đối số generic). Bạn chỉ đưaT. - Dòng
help:thêm một chữEtạm thời. Đó là gợi ý về chỗ đặt kiểu, không phải bản thân kiểu: bạn thayEbằng kiểu lỗi thật.
Đối số generic giống như các ô trống trên một tờ khai. Tờ khai Result có hai ô, “nếu thành công:” và “nếu thất bại:”. Chỉ điền một ô thì nhân viên sẽ trả lại cho bạn.
Lỗi này rất hay gặp nếu bạn từng đọc code viết Result<u32> với chỉ một kiểu. Đoạn code đó không sai: nó dùng một alias kiểu (type alias) như io::Result<T> hoặc anyhow::Result<T>, thứ tự điền sẵn kiểu lỗi cho bạn. Result thông thường từ prelude thì không có lối tắt đó.
Vì sao Rust làm vậy
Phần tiêu đề “Vì sao Rust làm vậy”Một kiểu generic chưa phải kiểu hoàn chỉnh cho tới khi mọi tham số đều có giá trị. Result<u32, E> với E chưa biết không cho biết giá trị lớn cỡ nào hay lỗi trông ra sao, nên Rust không thể biên dịch code dùng nó. Rust có thể thử đoán kiểu còn thiếu từ phần thân hàm, nhưng nó cố ý không bao giờ suy luận kiểu trong chữ ký hàm. Chữ ký là bản hợp đồng mà code khác dựa vào, nên phải được viết ra đầy đủ.
Một số tham số có giá trị mặc định. HashMap<K, V, S = RandomState> có tham số thứ ba cho bộ băm (hasher), đó là lý do rustc nói “at least 2” (ít nhất 2) với HashMap. Giá trị mặc định chỉ có thể điền từ cuối danh sách, và chỉ ở chỗ tác giả của kiểu đã thêm vào. Xem Generic và Result.
Cách sửa
Phần tiêu đề “Cách sửa”Cách sửa 1: Viết kiểu còn thiếu
Phần tiêu đề “Cách sửa 1: Viết kiểu còn thiếu”Phần lớn trường hợp, hãy chọn cách này. Tìm xem tham số còn thiếu nên là gì rồi viết nó ra. Với parse(), kiểu lỗi khi một số không phân tích được là std::num::ParseIntError:
use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u32, ParseIntError> { text.trim().parse()}
fn main() { for input in ["42", "bốn mươi"] { match parse_age(input) { Ok(age) => println!("tuổi: {}", age), Err(e) => println!("lỗi: {}", e), } }}Kết quả:
tuổi: 42lỗi: invalid digit found in string“invalid digit found in string” (có chữ số không hợp lệ trong chuỗi) là thông báo của chính thư viện chuẩn, nên nó luôn bằng tiếng Anh. Không chắc đó là kiểu lỗi nào? Hãy xem tài liệu của hàm mà bạn trả về lỗi của nó. Cách sửa tương tự dùng được cho HashMap<String>: nói rõ giá trị là gì, như HashMap<String, u32>. Xem HashMap và Lỗi tùy chỉnh.
Cách sửa 2: Dùng alias một kiểu mà bạn định viết, như io::Result
Phần tiêu đề “Cách sửa 2: Dùng alias một kiểu mà bạn định viết, như io::Result”Chọn cách này khi bạn làm việc với file, mạng hoặc stdin, hoặc khi bạn thấy Result<T> trong code của người khác. std::io::Result<T> là cách viết tắt của Result<T, std::io::Error>. Import module và viết io::Result:
use std::fs;use std::io;
fn read_config(path: &str) -> io::Result<String> { fs::read_to_string(path)}
fn main() { match read_config("missing-config.toml") { Ok(text) => println!("{}", text), Err(e) => println!("không đọc được cấu hình: {}", e), }}Kết quả:
không đọc được cấu hình: No such file or directory (os error 2)Phần “No such file or directory” (không có file hoặc thư mục đó) do hệ điều hành trả về, nên vẫn bằng tiếng Anh. Các ứng dụng dùng crate anyhow cũng viết anyhow::Result<T> theo cách tương tự. Sai sót ngược lại cũng là E0107: io::Result<String, io::Error> cho ra type alias takes 1 generic argument but 2 generic arguments were supplied, vì alias đã chứa sẵn kiểu lỗi. Hãy bỏ kiểu thứ hai. Xem Đọc ghi file và anyhow và thiserror.
Cách sửa 3: Cho hàm là generic, hoặc ghi rõ các kiểu
Phần tiêu đề “Cách sửa 3: Cho hàm là generic, hoặc ghi rõ các kiểu”Chọn cách này khi kiểu đó là struct generic của chính bạn và bạn chỉ viết mỗi tên của nó. rustc nói missing generics for struct `Pair` (thiếu generic cho struct Pair):
error[E0107]: missing generics for struct `Pair` --> src/main.rs:6:13 |6 | fn show(p: &Pair) { // ❌ which `K` and `V`? | ^^^^ expected 2 generic arguments |note: struct defined here, with 2 generic parameters: `K`, `V` --> src/main.rs:1:8 |1 | struct Pair<K, V> { | ^^^^ - -help: add missing generic arguments |6 | fn show(p: &Pair<K, V>) { // ❌ which `K` and `V`? | ++++++Chương trình bị lỗi:
struct Pair<K, V> { key: K, value: V,}
fn show(p: &Pair) { // ❌ which `K` and `V`? println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "volume", value: 7 }); show(&Pair { key: "theme", value: "dark" });}Phần help: viết Pair<K, V>, nhưng K và V chưa tồn tại trong show, nên bạn còn phải khai báo chúng. Vì show được gọi với hai loại Pair khác nhau, hãy cho nó là generic, kèm các ràng buộc cần để in ra:
use std::fmt::Display;
struct Pair<K, V> { key: K, value: V,}
fn show<K: Display, V: Display>(p: &Pair<K, V>) { println!("{} = {}", p.key, p.value);}
fn main() { show(&Pair { key: "âm lượng", value: 7 }); show(&Pair { key: "giao diện", value: "tối" });}Kết quả:
âm lượng = 7giao diện = tốiNếu hàm chỉ bao giờ xử lý một loại, hãy ghi rõ các kiểu: fn show(p: &Pair<&str, u32>). impl Pair { ... } cũng vậy, nó cần được viết là impl<K, V> Pair<K, V> { ... }. Xem Generic.
Cách sửa 4: Bỏ đối số thừa
Phần tiêu đề “Cách sửa 4: Bỏ đối số thừa”Chọn cách này khi rustc nói “but 2 generic arguments were supplied” và muốn ít hơn. Nó hay xảy ra với turbofish ::<> khi gọi hàm:
error[E0107]: function takes 1 generic argument but 2 generic arguments were supplied --> src/main.rs:13:20 |13 | println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many | ^^^^^^^ ----- help: remove the unnecessary generic argument | | | expected 1 generic argument |note: function defined here, with 1 generic parameter: `T` --> src/main.rs:1:4 | 1 | fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { | ^^^^^^^ -Chương trình bị lỗi:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest::<i32, i32>(&scores)); // ❌ one type too many}largest có một tham số, T, nên nó nhận một kiểu. Hãy viết largest::<i32>(...), hoặc bỏ hẳn turbofish, vì Rust tự suy ra T từ đối số:
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T { let mut best = items[0]; for &item in items { if item > best { best = item; } } best}
fn main() { let scores = [72, 95, 88]; println!("{}", largest(&scores)); println!("{}", largest::<f64>(&[1.5, 0.5]));}Kết quả:
951.5Dòng note: là dòng hữu ích nhất ở đây: nó cho thấy định nghĩa và liệt kê các tham số, nên bạn có thể tự đếm.
Thường gặp ở đâu
Phần tiêu đề “Thường gặp ở đâu”HashMap<String> chỉ có khóa. rustc nói struct takes at least 2 generic arguments but 1 generic argument was supplied. Cách sửa: HashMap<String, u32>, hoặc viết HashMap::new() và để Rust suy ra cả hai từ lần insert đầu tiên. Xem HashMap.
Struct của bạn có hai tham số kiểu, nhưng chỉ được đưa một. let p: Pair<&str> = ... cho ra struct takes 2 generic arguments but 1 generic argument was supplied. Cách sửa: Pair<&str, i32>, hoặc Pair<_, _> để Rust tự điền cả hai. Option<i32, i32> thì ngược lại: enum takes 1 generic argument but 2 generic arguments were supplied. Nếu bạn muốn có hai giá trị, hãy dùng tuple, Option<(i32, i32)>.
Lifetime cũng được tính. Một struct Excerpt<'a> bị viết thành Excerpt<'a, 'a> cho ra struct takes 1 lifetime argument but 2 lifetime arguments were supplied. Cách sửa: mỗi tham số trong định nghĩa nhận một lifetime. Xem Lifetime. Một phương thức hoàn toàn không generic, như .into::<String>(), cho ra method takes 0 generic arguments but 1 generic argument was supplied; hãy viết let s: String = x.into(); thay vào đó (xem E0283).
Một điều bất ngờ: Vec<i32, i32> không phải E0107. Vec có tham số thứ hai, chưa ổn định, dành cho bộ cấp phát bộ nhớ (allocator), nên bạn nhận được E0658 (allocator_api) cùng vài lỗi khác. Cách sửa vẫn vậy: Vec<i32>, hoặc Vec<(i32, i32)> cho các cặp.
Lỗi liên quan
Phần tiêu đề “Lỗi liên quan”- E0061: cùng sai sót đó với đối số thường, một hàm nhận N đối số nhưng được đưa M.
- E0106: thiếu chỉ định lifetime, khi một tham chiếu trong kiểu cần
'a. - E0252: hai dòng
useimport cùng một tên; một E0107 thường theo sau khiResultmang nghĩafmt::Result. - E0283: cần chú thích kiểu, cách sửa đúng khi
.into::<T>()gây ra E0107. - E0401: một hàm bên trong dùng
Tgeneric của hàm bên ngoài. - 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. fn load() -> Result<String> gây ra E0107. Kiểu trả về nào sửa được lỗi cho một hàm đọc file?
2. Vì sao rustc nói at least 2 generic arguments với HashMap?
3. fn show(p: &Pair) báo `missing generics for struct Pair , và show được gọi với nhiều loại Pair` khác nhau. Cách sửa là gì?
Điểm: 0 / 3
Đọc thêm
Phần tiêu đề “Đọc thêm”- E0107 — Rust error codes index (tiếng Anh): phần giải thích chính thức, với các ví dụ thừa và thiếu đối số generic và lifetime.
- Generic Data Types — The Rust Book (tiếng Anh):
<T>hoạt động thế nào trong struct nhưPoint<T, U>, enum nhưResult<T, E>, và phương thức. - Recoverable Errors with
Result— The Rust Book (tiếng Anh): các hàm trả vềResult<String, io::Error>, và toán tử?. std::io::Result— tài liệu thư viện chuẩn (tiếng Anh): alias một tham số, và vì sao nhiều code viếtio::Result<T>đến vậy.- Generic parameters — The Rust Reference (tiếng Anh): các quy tắc chính xác, kể cả tham số kiểu mặc định như bộ băm của
HashMap.