Lewati ke konten

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.

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 */>().

Tanda-tandanya adalah bagian yang mudah:

  1. ^^^ di bawah age: variabel yang tipenya tidak diketahui.
  2. ----- di bawah parse: “type must be known at this point” (tipenya harus sudah diketahui di titik ini). Di sinilah Rust macet.
  3. Bagian help: “consider giving age an explicit type” (coba beri age tipe yang eksplisit). Ganti /* Type */ dengan tipe sungguhan, seperti u32.

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.

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.

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 43
Dua kopi: 9.98
Mendengarkan di port 8080
▶ Coba sendiri

Pilih 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.

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: 15
Dikali dua: 42
▶ Coba sendiri

Taruh 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 42
300: number too large to fit in target type
abc: invalid digit found in string
Ok(0.25)
▶ Coba sendiri

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 worker
Selamat datang! Umurmu 42.
▶ Coba sendiri

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.

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()?;.

  • 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") atau Default::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 variabel u32 (konversikan dengan u8::try_from(n) saja).
  • Semua error compiler: daftar lengkap halaman error.

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