Error Rust E0284: type annotations needed (tipe untuk parse())
E0284 hampir selalu berarti satu hal: kamu memanggil .parse() dan Rust tidak tahu teksnya harus diubah menjadi apa. Catatan cannot satisfy `<_ as FromStr>::Err == _` memang terlihat menakutkan, tapi perbaikannya biasanya cuma beberapa karakter, seperti : u32 atau ::<u32>. Halaman ini menunjukkan perbaikannya dulu, lalu membongkar catatan itu supaya tidak menakutkan lagi. Untuk error lain di seri ini, lihat Penjelasan error compiler Rust.
Pesan error-nya
Section titled “Pesan error-nya”error[E0284]: type annotations needed --> src/main.rs:2:9 |2 | let age = "42".parse().unwrap(); | ^^^ ----- type must be known at this point | = note: cannot satisfy `<_ as FromStr>::Err == _`help: consider giving `age` an explicit type |2 | let age: /* Type */ = "42".parse().unwrap(); | ++++++++++++Artinya: anotasi tipe diperlukan. Compiler menyarankan kamu menulis tipe age secara eksplisit.
Ini program yang menghasilkan error tersebut:
fn main() { let age = "42".parse().unwrap(); println!("Next year you'll be {}", age + 1);}Kamu akan melihat catatan yang sama dengan .expect(...) sebagai ganti .unwrap(), dengan match "42".parse() { ... }, dan dengan .parse() di dalam closure. Kalau hasil .parse() tidak disimpan di variabel, rustc menunjuk langsung ke parse dan menyarankan parse::</* Type */>().
Apa artinya
Section titled “Apa artinya”Tanda-tandanya adalah bagian yang mudah:
^^^di bawahage: variabel yang tipenya tidak diketahui.-----di bawahparse: “type must be known at this point” (tipenya harus sudah diketahui di titik ini). Di sinilah Rust macet.- Bagian help: “consider giving
agean explicit type” (coba beriagetipe yang eksplisit). Ganti/* Type */dengan tipe sungguhan, sepertiu32.
Sekarang catatannya, sepotong demi sepotong. Di pesan compiler, _ berarti “tipe yang belum aku ketahui”:
| Potongan | Dalam bahasa sederhana |
|---|---|
<_ as FromStr> |
“tipe tujuan parse-mu (aku belum tahu tipenya)” |
::Err |
“…dan tipe error yang menyertainya” |
== _ |
“harus sama dengan tipe lain yang juga belum aku ketahui” |
Jadi seluruh catatan itu bilang: “Aku bahkan tidak bisa mencari tahu jenis error apa yang mungkin dikembalikan parse, karena aku tidak tahu kamu mau mengubah teksnya menjadi apa.”
Bayangkan kamu menyerahkan sepucuk surat ke penerjemah dan bilang “tolong terjemahkan”. Ke bahasa Prancis? Jepang? Sebelum kamu bilang, ia tidak bisa mulai, dan bahkan tidak bisa memberi tahu kesalahan apa saja yang mungkin ia temui.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Ini signature parse, sedikit disederhanakan:
fn parse<F: FromStr>(&self) -> Result<F, F::Err>F adalah tipe yang kamu inginkan sebagai hasil, dan F::Err adalah tipe error-nya. Setiap tipe tujuan membawa error-nya sendiri: parse ke i32 bisa gagal dengan ParseIntError, ke f64 dengan ParseFloatError, ke bool dengan ParseBoolError. Rust butuh F untuk tahu apa yang dikembalikan parse sama sekali.
Pilihan tipe ini juga mengubah apa yang terjadi saat program berjalan. "42" berhasil di-parse sebagai u8, i32, maupun f64. Tapi "300" gagal sebagai u8 (terlalu besar), dan "4.5" gagal sebagai i32 (bukan bilangan bulat). Rust tidak mau menebak, karena tebakan yang salah akan menjadi bug yang hanya muncul dengan input tertentu. Pelajaran Konversi menunjukkan cara kerja FromStr, dan cara menambahkannya ke tipe buatanmu sendiri.
Cara Rust menyimpulkan tipe sendiri dijelaskan di halaman E0282. Kenapa ada tiga kode berbeda (E0282, E0283, E0284) untuk “type annotations needed” dijelaskan di halaman E0283. Versi singkatnya: E0284 adalah varian di mana aturan yang macet menyangkut tipe yang menjadi bagian dari tipe yang belum diketahui (di sini F::Err). Buatmu, perbaikannya sama saja: sebutkan tipenya.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Beri anotasi pada variabelnya
Section titled “Perbaikan 1: Beri anotasi pada variabelnya”Pilih ini di kebanyakan kasus. Ini gaya yang dipakai di Rust Book, dan sekaligus mendokumentasikan isi teks yang diharapkan:
fn main() { let age: u32 = "42".parse().unwrap(); let price: f64 = "4.99".parse().unwrap(); let port: u16 = "8080".parse().expect("port harus berupa angka");
println!("Tahun depan umurmu {}", age + 1); println!("Dua kopi: {:.2}", price * 2.0); println!("Mendengarkan di port {port}");}Output:
Tahun depan umurmu 43Dua kopi: 9.98Mendengarkan di port 8080Pilih tipe yang sesuai dengan arti teksnya: u16 untuk port, u8 untuk persentase, f64 untuk harga. Tipe yang terlalu kecil akan membuat input yang valid menjadi error, jadi kalau ragu, pilih tipe yang lebih lega seperti i64 atau u32.
Perbaikan 2: Pakai turbofish parse::<T>()
Section titled “Perbaikan 2: Pakai turbofish parse::<T>()”Pilih ini kalau tidak ada variabel yang bisa diberi anotasi: di dalam closure, di tengah rantai iterator, atau di ekspresi seperti ... * 2:
fn main() { let line = "3 7 5"; let total = line .split(' ') .map(|s| s.parse::<u32>().unwrap()) .sum::<u32>(); let doubled = "21".parse::<i32>().unwrap() * 2;
println!("Total: {total}"); println!("Dikali dua: {doubled}");}Output:
Total: 15Dikali dua: 42Taruh turbofish-nya di parse itu sendiri. Di dalam sebuah rantai, memberi anotasi hanya pada hasil akhirnya sering tidak cukup; lihat poin pertama di bagian “Di mana biasanya kamu melihatnya” di bawah.
Perbaikan 3: Sebutkan tipenya dan tangani error-nya dengan benar
Section titled “Perbaikan 3: Sebutkan tipenya dan tangani error-nya dengan benar”Pilih ini untuk input sungguhan (file, argumen command line, formulir web), di mana .unwrap() akan membuat programmu crash kalau teksnya tidak valid. Sebutkan tipenya di parse dan lakukan match pada Result-nya. Perhatikan bagaimana tipe menentukan input mana yang berhasil:
fn main() { for input in ["42", "300", "abc"] { match input.parse::<u8>() { Ok(n) => println!("{input}: dapat {n}"), Err(e) => println!("{input}: {e}"), } }
let rate: Result<f64, _> = "0.25".parse(); println!("{:?}", rate);}Output:
42: dapat 42300: number too large to fit in target typeabc: invalid digit found in stringOk(0.25)Pesan error dari standard library tetap dalam bahasa Inggris: “number too large to fit in target type” artinya angkanya terlalu besar untuk tipe tujuan, dan “invalid digit found in string” artinya ada karakter yang bukan angka.
Di Result<f64, _>, kamu hanya menyebutkan tipe nilainya. _ membiarkan Rust mengisi tipe error-nya sendiri (ParseFloatError), dan itulah persis bagian yang ditanyakan catatan tadi. Selengkapnya di Result.
Perbaikan 4: Pakai nilainya di tempat yang tipenya sudah pasti
Section titled “Perbaikan 4: Pakai nilainya di tempat yang tipenya sudah pasti”Pilih ini kalau nilai hasil parse masuk ke field struct atau parameter fungsi yang tipenya sudah diketahui. Rust membaca seluruh fungsi, jadi pemakaian belakangan itu menjawab pertanyaannya:
struct Config { port: u16, workers: u8,}
fn greet(age: u32) { println!("Selamat datang! Umurmu {age}.");}
fn main() { let port = "8080".parse().unwrap(); // u16, dari Config let workers = "4".parse().unwrap(); // u8, dari Config let config = Config { port, workers }; println!("Port {} dengan {} worker", config.port, config.workers);
let age = "42".parse().unwrap(); // u32, dari greet greet(age);}Output:
Port 8080 dengan 4 workerSelamat datang! Umurmu 42.Tidak semua pemakaian dihitung, makanya ini Perbaikan 4. Operasi hitung seperti age + 1 dan perbandingan seperti age >= limit tetap menghasilkan E0284, bahkan kalau limit bertipe u32: operator bisa bekerja di antara banyak pasangan tipe, jadi tidak menyebutkan satu tipe yang pasti. Dan memanggil method, seperti age.pow(2), mengubah error-nya menjadi E0282. Kalau ragu, pakai Perbaikan 1.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Parse di dalam map, lalu sum(). let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); tetap E0284, meskipun total sudah punya tipe. Sebuah u32 bisa merupakan jumlah dari nilai u32 atau nilai &u32, jadi anotasi itu tidak memberi tahu Rust apa tipe setiap item. Perbaikannya: s.parse::<u32>(). (Kalau di-collect ke Vec<i32>, malah berhasil, karena Vec<i32> hanya bisa berisi i32.)
Mengumpulkan hasil parse ke dalam Result. let nums: Result<Vec<_>, _> = text.split(' ').map(|x| x.parse()).collect(); adalah E0284: _ di Vec<_> membiarkan tipe item-nya terbuka. Perbaikannya: Result<Vec<i32>, _>. Pola praktis ini berhenti di item pertama yang tidak valid dan memberimu error-nya.
Membaca input pengguna. let guess = line.trim().parse().unwrap(); di game tebak angka atau tool command line. Perbaikannya: let guess: u32 = ..., persis seperti di Rust Book.
Dengan ?, kamu mendapat error yang lebih aneh. let port = text.parse()?; tidak menghasilkan E0284. Di edition 2021, Rust jatuh ke tipe () dan melaporkan E0277: the trait bound `(): FromStr` is not satisfied (edition 2024 menyebut !, bukan ()). Perbaikannya sama: let port: u16 = text.parse()?;.
Error terkait
Section titled “Error terkait”- E0282: type annotations needed (anotasi tipe diperlukan), saat sama sekali tidak ada petunjuk (atau tipenya dibutuhkan untuk memanggil method seperti
age.pow(2)). - E0283: type annotations needed setelah
.sum(),.collect(), atau.into(), saat banyak tipe bisa cocok. Halaman itu juga menjelaskan kenapa ketiganya punya kode yang berbeda. - E0790:
FromStr::from_str("42")atauDefault::default()dipanggil tanpa menyebutkan tipenya. - E0277: yang kamu dapat dari
.parse()?tanpa tipe, dan yang kamu dapat kalau tipe pilihanmu tidak bisa di-parse dari teks. - E0599: method tidak ditemukan, misalnya memanggil
.parse()pada sesuatu yang bukan teks, seperti variabelu32(konversikan denganu8::try_from(n)saja). - Semua error compiler: daftar lengkap halaman error.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Apa arti cannot satisfy <_ as FromStr>::Err == _ dalam bahasa sederhana?
2. Baris mana yang memperbaiki let port = "8080".parse().unwrap();?
3. let total: u32 = line.split(' ').map(|s| s.parse().unwrap()).sum(); tetap menghasilkan E0284. Di mana tipenya harus ditulis?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0284 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
str::parse— dokumentasi standard library (bahasa Inggris): signature method-nya, dengan gaya anotasi maupun turbofish.FromStr— dokumentasi standard library (bahasa Inggris): trait di balikparse, termasuk tipeErr-nya dan cara mengimplementasikannya.- Programming a Guessing Game — The Rust Book (bahasa Inggris): tempat
let guess: u32 = guess.trim().parse()pertama kali muncul, dijelaskan langkah demi langkah. - Data Types — The Rust Book (bahasa Inggris): kenapa
"42".parse()butuh anotasi tipe, lengkap dengan error compiler aslinya.