Lewati ke konten

Error Rust E0308: mismatched types (tipe tidak cocok)

E0308 artinya Rust mengharapkan nilai dengan satu tipe, tapi kamu memberinya nilai dengan tipe lain. Kemungkinan besar inilah error yang paling sering kamu temui di Rust. Untungnya, pesannya hampir selalu menyebutkan kedua tipe itu, jadi biasanya cepat diperbaiki. (Mencari error lain? Lihat Penjelasan error compiler Rust.)

error[E0308]: mismatched types
--> src/main.rs:6:29
|
6 | let user = User { name: "Ferris" };
| ^^^^^^^^ expected `String`, found `&str`
|
help: try using a conversion method
|
6 | let user = User { name: "Ferris".to_string() };
| ++++++++++++

Compiler bilang di baris 6 ia mengharapkan String tapi menemukan &str, lalu menyarankan konversi dengan .to_string().

Ini program yang menghasilkan error tersebut:

struct User {
name: String,
}
fn main() {
let user = User { name: "Ferris" };
println!("{}", user.name);
}

Setiap pesan E0308 punya bentuk yang sama: expected A, found B (diharapkan A, ditemukan B).

  • Expected adalah tipe yang dibutuhkan Rust di posisi itu. Tipe ini datang dari tempat lain: anotasi tipe, tipe parameter atau tipe kembalian sebuah fungsi, field struct, atau cabang lain dari if/match.
  • Found adalah tipe dari nilai yang benar-benar kamu tulis di situ.

Di contoh tadi, field name dideklarasikan sebagai String, jadi itulah yang diharapkan Rust. Tapi "Ferris" adalah string literal, dan tipenya &str. Keduanya berhubungan, tapi bukan tipe yang sama.

Perhatikan juga baris help:. Untuk E0308, compiler sering menyarankan perbaikan yang persis, seperti .to_string() di atas.

Rust tidak pernah mengubah tipe diam-diam. Di beberapa bahasa, angka bisa berubah jadi string tanpa kamu sadari, atau angka besar dipotong supaya muat di tipe yang lebih kecil. Konversi tersembunyi seperti itu menimbulkan bug sungguhan: data hilang, hasil salah, atau crash yang terjadi jauh dari tempat kesalahan aslinya.

Karena itu Rust membuat setiap konversi terlihat di kode. Memang sedikit lebih banyak mengetik, tapi saat kamu membaca kodenya nanti, kamu bisa melihat persis di mana sebuah nilai berganti tipe dan apakah langkah itu bisa gagal. Tipe-tipenya sendiri dijelaskan di Tipe data.

Setiap perbaikan di bawah dimulai dengan bentuk error-nya, jadi kamu bisa langsung lompat ke yang cocok dengan error-mu.

error[E0308]: mismatched types
--> src/main.rs:1:46
|
1 | fn total_price(price: u32, quantity: u32) -> u32 {
| ----------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | price * quantity;
| - help: remove this semicolon to return this value

Kalau kamu melihat found (), periksa baris terakhir fungsinya. Di Rust, ekspresi terakhir sebuah blok yang tanpa titik koma adalah nilai kembaliannya. Menambahkan ; mengubahnya menjadi statement, sehingga fungsi itu mengembalikan () (tipe “kosong”). Compiler bahkan menunjuk titik komanya dan bilang “remove this semicolon” (hapus titik koma ini). Hapus saja:

fn total_price(price: u32, quantity: u32) -> u32 {
price * quantity // tanpa titik koma: nilai ini yang dikembalikan
}
fn main() {
println!("Total: {}", total_price(250, 3));
}

Output:

Total: 750
▶ Coba sendiri

Hal yang sama terjadi di dalam blok if dan cabang match. Selengkapnya di Fungsi.

Perbaikan 2: Konversi antara String dan &str

Section titled “Perbaikan 2: Konversi antara String dan &str”

String adalah string yang dimiliki (owned) dan bisa bertambah panjang. &str adalah tampilan pinjaman atas sebuah teks. Cerita lengkapnya ada di String. Konversinya pendek, ke dua arah:

  • &str ke String: .to_string() atau String::from(...).
  • String ke &str: pinjam dengan & (atau panggil .as_str()).

Kasus kedua muncul sebagai expected &str, found String, biasanya saat kamu memberikan String ke fungsi yang menerima &str.

struct User {
name: String,
}
fn greet(name: &str) {
println!("Halo, {name}!");
}
fn main() {
// &str -> String: .to_string() atau String::from()
let user = User { name: "Ferris".to_string() };
// String -> &str: pinjam dengan & (atau panggil .as_str())
greet(&user.name);
greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new();
tags.push(String::from("rust"));
println!("{tags:?}");
}

Output:

Halo, Ferris!
Halo, Ferris!
["rust"]
▶ Coba sendiri
error[E0308]: mismatched types
--> src/main.rs:3:22
|
3 | let count: i32 = names.len();
| --- ^^^^^^^^^^^ expected `i32`, found `usize`
| |
| expected due to this

Di sini anotasi i32 (ditunjuk dengan “expected due to this”) tidak cocok dengan usize yang dikembalikan .len(). i32, i64, u32, dan usize semuanya tipe yang berbeda, dan Rust tidak mau mencampurnya. Panjang dan indeks selalu bertipe usize. Pilih salah satu cara konversi:

  • .into() (atau i64::from(x)) kalau konversinya tidak mungkin kehilangan data, misalnya i32 ke i64.
  • i64::try_from(x) (atau .try_into()) kalau nilainya mungkin tidak muat. Hasilnya Result, jadi kamu yang menentukan apa yang terjadi kalau gagal.
  • as adalah cast cepat. Ia tidak pernah gagal, tapi diam-diam memotong nilai yang tidak muat, jadi pakai hanya kalau kamu yakin nilainya kecil.
fn average(total: i64, count: i64) -> i64 {
total / count
}
fn main() {
let scores: Vec<i32> = vec![80, 95, 70];
let total: i32 = scores.iter().sum();
// i32 -> i64 selalu muat, jadi .into() sudah cukup
let total: i64 = total.into();
// usize -> i64 mungkin tidak muat, jadi try_from bertanya dulu
let count = i64::try_from(scores.len()).unwrap();
println!("Rata-rata: {}", average(total, count));
// `as` juga bisa, tapi diam-diam memotong nilai yang tidak muat
let players = scores.len() as i32;
println!("{players} pemain");
}

Output:

Rata-rata: 81
3 pemain
▶ Coba sendiri

Sering kali perbaikan paling sederhana justru mengubah anotasinya: let count: usize = names.len();. Halaman Konversi tipe dan Konversi membahasnya lebih dalam.

Perbaikan 4: Bungkus dengan Some, atau buka Option-nya

Section titled “Perbaikan 4: Bungkus dengan Some, atau buka Option-nya”
error[E0308]: mismatched types
--> src/main.rs:3:27
|
3 | let position: usize = names.iter().position(|n| *n == "Ben");
| ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>`
| |
| expected due to this

Option<usize> bukan usize. Artinya “mungkin sebuah usize, mungkin tidak ada apa-apa”. Ketidakcocokan ini bisa terjadi ke dua arah:

  • Expected T, found Option<T>: kamu harus menangani kasus “tidak ada apa-apa”, dengan match, if let, atau unwrap_or(default).
  • Expected Option<T>, found T: fungsimu mengembalikan Option, jadi bungkus nilainya: Some(10), bukan 10.
fn find_discount(code: &str) -> Option<u32> {
if code == "SAVE10" {
return Some(10); // T -> Option<T>: bungkus dengan Some
}
None
}
fn main() {
let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: tangani kasus "tidak ditemukan"...
match names.iter().position(|n| *n == "Ben") {
Some(i) => println!("Ben ada di posisi {i}"),
None => println!("Ben tidak ada di sini"),
}
// ...atau pilih nilai default dengan unwrap_or
let discount: u32 = find_discount("HELLO").unwrap_or(0);
println!("Diskon: {discount}%");
}

Output:

Ben ada di posisi 1
Diskon: 0%
▶ Coba sendiri

Compiler mungkin menyarankan .expect("REASON"). Itu juga bisa, tapi program akan crash kalau nilainya None, jadi pakai hanya kalau None memang tidak mungkin terjadi. Lihat Option.

Perbaikan 5: Samakan tipe setiap cabang if/else dan match

Section titled “Perbaikan 5: Samakan tipe setiap cabang if/else dan match”
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:3:52
|
3 | let label = if stock > 0 { "in stock" } else { stock };
| ---------- ^^^^^ expected `&str`, found integer
| |
| expected because of this

Pesan ini berarti cabang if dan else menghasilkan tipe yang berbeda: yang satu &str, yang lain integer. Saat if atau match menghasilkan nilai, Rust perlu tahu satu tipe untuk nilai itu. Jadi setiap cabang harus menghasilkan tipe yang sama. Cabang pertama menentukan harapannya, dan error-nya menunjuk cabang yang tidak sepakat. Untuk match, pesannya berbunyi “match arms have incompatible types”. Samakan cabang yang berbeda itu:

fn status_text(code: u16) -> String {
match code {
200 => "OK".to_string(),
404 => "Not Found".to_string(),
_ => format!("Kode tidak dikenal {code}"), // setiap cabang adalah String
}
}
fn main() {
let stock = 0;
let label = if stock > 0 { "tersedia" } else { "habis" }; // keduanya &str
println!("{label}");
println!("{}", status_text(404));
println!("{}", status_text(418));
}

Output:

habis
Not Found
Kode tidak dikenal 418
▶ Coba sendiri

Di sini satu cabang butuh format!, yang menghasilkan String, jadi cabang lainnya juga dijadikan String. Lihat If…Else dan Match.

  • Membaca input pengguna. let age: u32 = input.trim(); menghasilkan “expected u32, found &str”. Teks bukan angka. Perbaikan: parse dulu: let age: u32 = input.trim().parse().unwrap(); (atau tangani error-nya, lihat Result).
  • Memasukkan literal ke Vec<String>. tags.push("rust") menghasilkan “expected String, found &str”. Perbaikan: tags.push("rust".to_string()), atau jadikan Vec<&str> kalau semua string-nya literal.
  • Hitungan dengan tipe angka campuran. total + n dengan total bertipe i64 dan n bertipe i32 menghasilkan E0308 (dan sering E0277 di sebelahnya). Perbaikan: konversi salah satu sisi dulu: total + i64::from(n).
  • E0277: the trait bound is not satisfied (trait tidak terpenuhi). Sering muncul bersama E0308, misalnya saat kamu menjumlahkan i64 dengan i32.
  • E0599: no method found (method tidak ditemukan). Kamu memanggil method yang tidak ada pada tipe ini, sering karena nilainya bertipe lain dari yang kamu kira.
  • E0425: cannot find value in this scope (nama tidak dikenal). Sebuah nama yang sama sekali tidak dikenal Rust.
  • Semua error compiler: daftar lengkap halaman error.

Kuis singkat

1. Sebuah fungsi ditulis -> i32, tapi error-nya bilang "expected i32, found ()". Apa penyebab yang paling mungkin?

2. Sebuah fungsi menerima &str dan kamu punya let name = String::from("Ana");. Bagaimana cara memberikannya?

3. Konversi mana yang tidak mungkin diam-diam kehilangan data?

Skor: 0 / 3