Sửa lỗi Rust 'type annotations needed': error[E0282] cannot infer type for type parameter `T`

beginner🦀 Rust2026-05-17| Rust (mọi phiên bản), rustc, Cargo — Linux, macOS, Windows

Error Message

error[E0282]: type annotations needed: cannot infer type for type parameter `T`
#rust#suy-luận-kiểu#generics#rustc#lỗi-compiler#collect#turbofish

Lỗi Gặp Phải

Bạn đang biên dịch một dự án Rust và quá trình build dừng đột ngột:

error[E0282]: type annotations needed
  --> src/main.rs:4:9
   |
4  |     let x = [].into_iter().collect();
   |         ^ cannot infer type for type parameter `T`
   |
   = note: cannot satisfy `_: Iterator`

rustc cố gắng tự suy luận kiểu dữ liệu. Khi không có gì ở phía sau để xác định T là gì, nó dừng lại và yêu cầu bạn khai báo tường minh.

Nguyên Nhân

Cơ chế suy luận kiểu của Rust hoạt động theo cả hai chiều trong code của bạn. Vấn đề xảy ra khi một hàm generic hoặc phương thức trait trả về T, nhưng không có gì bạn làm với giá trị đó giúp thu hẹp kiểu cụ thể. Trình biên dịch không thể đoán — nó yêu cầu một kiểu dữ liệu cụ thể.

Các trường hợp thường gặp:

  • Gọi .collect() mà không lưu vào biến có kiểu xác định
  • Gọi .parse() trên một chuỗi mà không có kiểu đích
  • Dùng Default::default() hoặc FromStr::from_str() mà không có gợi ý kiểu
  • Literal rỗng như vec![] hoặc [] — không có phần tử nghĩa là không suy luận được kiểu
  • Hàm generic mà kiểu trả về phụ thuộc hoàn toàn vào tham số kiểu

Cách Sửa Từng Bước

1. Khai báo kiểu trên biến

Thêm kiểu trực tiếp vào khai báo let. Chín trong mười trường hợp, đây là tất cả những gì bạn cần.

// Trước — trình biên dịch không thể suy luận kiểu được collect
let numbers = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();

// Sau — chỉ định kiểu collection
let numbers: Vec<i32> = "1 2 3".split_whitespace().map(|s| s.parse().unwrap()).collect();

2. Dùng cú pháp turbofish

Bỏ qua việc khai báo kiểu trên biến. Gắn kiểu ngay tại vị trí gọi hàm bằng turbofish (::<T>). Cách này tiện khi bạn truyền kết quả trực tiếp vào hàm khác mà không cần lưu vào biến trước.

// parse() cần biết phải parse thành kiểu gì
let n = "42".parse::<i32>().unwrap();

// collect() cần biết collection đích là gì
let v = (0..5).map(|x| x * 2).collect::<Vec<_>>();

// Parse địa chỉ IP
let addr = "127.0.0.1".parse::<std::net::IpAddr>().unwrap();

3. Sửa literal collection rỗng

Một Vec::new() rỗng không cho trình biên dịch bất kỳ gợi ý nào. Chọn một trong ba cách sau:

// Lỗi: không có kiểu phần tử để suy luận
let mut items = Vec::new();

// Cách 1: khai báo kiểu trên biến
let mut items: Vec<String> = Vec::new();

// Cách 2: turbofish trên constructor
let mut items = Vec::<String>::new();

// Cách 3: push một phần tử trước (trình biên dịch suy luận T = String)
let mut items = Vec::new();
items.push(String::from("hello"));

4. Sửa với Default::default()

// Lỗi — kiểu nào sẽ được dùng ở đây?
let config = Default::default();

// Sửa: khai báo kiểu trên biến
let config: MyConfig = Default::default();

// Cũng áp dụng được cho các kiểu chuẩn
let counts: HashMap<String, i32> = Default::default();

5. Sửa trong lời gọi hàm generic

Khi kiểu trả về của hàm generic không thể suy luận từ cách bạn dùng kết quả, hãy khai báo tường minh tại vị trí gọi:

fn make_pair<A, B>(a: A, b: B) -> (A, B) {
    (a, b)
}

// Thêm hậu tố kiểu vào literal
let pair = make_pair(1i32, 2i32);

// Hoặc dùng turbofish cho toàn bộ lời gọi
let pair = make_pair::<i32, f64>(1, 2.0);

6. Sửa chuỗi collect()

collect() là nguyên nhân số một gây ra E0282. Nó có thể tạo ra hàng chục kiểu collection — trình biên dịch sẽ không tự chọn cho bạn. Luôn xác định kiểu đầu ra ở đâu đó trong chuỗi:

use std::collections::{HashMap, HashSet};

// Collect thành HashSet
let unique: HashSet<i32> = vec![1, 2, 2, 3].into_iter().collect();

// Collect thành HashMap từ các cặp key-value
let map: HashMap<_, _> = vec![("a", 1), ("b", 2)].into_iter().collect();

// Hoặc dùng turbofish trực tiếp trên collect
let unique = vec![1, 2, 2, 3].into_iter().collect::<HashSet<_>>();

Ký tự giữ chỗ _ rất hữu ích — chỉ định kiểu ngoài cùng và để trình biên dịch tự điền phần còn lại.

Kiểm Tra Sau Khi Sửa

Chạy cargo check thay vì build đầy đủ — không tạo binary nên nhanh hơn:

cargo check

Kết quả sạch trông như thế này:

    Checking myproject v0.1.0 (/path/to/myproject)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s

Vẫn còn E0282? Dùng grep để tìm tất cả các lỗi còn sót lại:

cargo build 2>&1 | grep E0282

Tóm Tắt Nhanh

  • Khai báo kiểu trên biến: let x: Vec<i32> = ...
  • Turbofish: method::<Type>() hoặc Type::<Param>::method()
  • Suy luận một phần: dùng _ cho các phần trình biên dịch vẫn có thể tự suy luận — Vec<_>, HashMap<String, _>
  • Mã lỗi: luôn là error[E0282] — chạy rustc --explain E0282 để xem giải thích đầy đủ từ trình biên dịch

Khi Khai Báo Kiểu Trở Nên Dài Dòng

Khai báo kiểu dài rải rác khắp nơi trông rất lộn xộn. Một type alias sẽ giải quyết vấn đề này:

type WordCount = HashMap<String, usize>;

let counts: WordCount = text.split_whitespace()
    .map(|w| (w.to_string(), 1))
    .collect();

Một alias, một khai báo kiểu trên biến — các vị trí gọi giữ được sự gọn gàng và trình biên dịch có được gợi ý cần thiết.

Related Error Notes