Result di Rust
Kalau Option mewakili nilai yang mungkin tidak ada, Result mewakili operasi yang mungkin gagal. Bedanya dengan nilai yang hilang, sebuah kegagalan biasanya datang bersama alasannya.
Bentuk Result
Section titled “Bentuk Result”Result adalah enum lain dari standard library, kali ini dengan dua varian yang sama-sama membawa data:
enum Result<T, E> { Ok(T), // berhasil, berisi sebuah nilai Err(E), // gagal, berisi sebuah error}Fungsi yang bisa gagal
Section titled “Fungsi yang bisa gagal”.parse(), yang sudah kamu temui di halaman Type Casting, adalah contoh nyata yang bagus: mengubah teks menjadi angka bisa gagal kalau teksnya ternyata bukan angka:
fn main() { let good: Result<i32, std::num::ParseIntError> = "42".parse(); let bad: Result<i32, std::num::ParseIntError> = "abc".parse();
println!("{:?}", good); println!("{:?}", bad);}Output:
Ok(42)Err(ParseIntError { kind: InvalidDigit })InvalidDigit adalah cara Rust bilang bahwa teksnya berisi karakter yang bukan digit.
Menangani Result dengan match
Section titled “Menangani Result dengan match”fn main() { let input = "42";
match input.parse::<i32>() { Ok(number) => println!("Hasil parse: {number}"), Err(e) => println!("Gagal parse: {e}"), }}Bentuknya sama dengan menangani Option memakai match. Bedanya, cabang Err memberimu nilai error sungguhan untuk diperiksa atau dilaporkan, bukan sekadar “tidak ada apa-apa”.
Menulis fungsi sendiri yang mengembalikan Result
Section titled “Menulis fungsi sendiri yang mengembalikan Result”fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("tidak bisa membagi dengan nol")) } else { Ok(a / b) }}
fn main() { match divide(10.0, 2.0) { Ok(result) => println!("Hasil: {result}"), Err(e) => println!("Error: {e}"), }
match divide(10.0, 0.0) { Ok(result) => println!("Hasil: {result}"), Err(e) => println!("Error: {e}"), }}Output:
Hasil: 5Error: tidak bisa membagi dengan nolMengembalikan Result<f64, String> memaksa setiap pemanggil untuk menghadapi kemungkinan gagal. Tidak ada cara untuk tidak sengaja memperlakukan pembagian dengan nol sebagai angka biasa.
Operator ?
Section titled “Operator ?”Merangkai beberapa operasi yang masing-masing bisa gagal akan menghasilkan blok match yang bersarang dalam. Operator ? meratakannya. Artinya: “kalau ini Err, langsung kembalikan error itu dari fungsi saat ini; kalau tidak, berikan nilai di dalam Ok.”
fn parse_and_double(input: &str) -> Result<i32, std::num::ParseIntError> { let number = input.parse::<i32>()?; // langsung kembali kalau parse gagal Ok(number * 2)}
fn main() { println!("{:?}", parse_and_double("21")); println!("{:?}", parse_and_double("ups"));}Output:
Ok(42)Err(ParseIntError { kind: InvalidDigit })Tanpa ?, baris itu butuh match sendiri hanya untuk mengambil number atau keluar lebih awal dengan error-nya. ? melakukan persis hal itu dalam satu karakter. Operator ini hanya bisa dipakai di dalam fungsi yang juga mengembalikan Result (atau Option), karena dari situlah ia punya tempat untuk mengembalikan error-nya.
Result vs panic!
Section titled “Result vs panic!”Rust juga punya panic!, yang langsung menghentikan program. Hal itu dibahas lebih dalam di halaman berikutnya. Sebagai pegangan: pakai Result untuk kegagalan yang wajar ingin ditangani oleh pemanggil (input yang salah, file yang tidak ada); simpan panic! untuk situasi yang menandakan ada bug sungguhan, di mana melanjutkan program justru lebih buruk daripada berhenti.
Begitu fungsimu bisa gagal dengan beberapa cara yang berbeda, error berupa String biasa tidak lagi cukup. Halaman Error kustom menunjukkan cara mendefinisikan tipe error-mu sendiri, dan anyhow dan thiserror menunjukkan crate yang dipakai sebagian besar proyek Rust untuk memangkas kode boilerplate.
Latihan
Section titled “Latihan”1. Periksa umur
Section titled “1. Periksa umur”Tulis fn parse_age(input: &str) -> Result<u32, String>. Fungsi ini harus mengembalikan Err dengan pesan yang jelas kalau teksnya bukan angka, atau kalau angkanya lebih dari 150. Uji dengan "34", "abc", dan "200". Petunjuk: lakukan match pada input.parse::<u32>(), dan di dalam cabang Ok pakai if untuk memeriksa besarnya.
Lihat solusi
fn parse_age(input: &str) -> Result<u32, String> { match input.parse::<u32>() { Ok(age) => { if age > 150 { Err(format!("{age} terlalu tua untuk jadi umur sungguhan")) } else { Ok(age) } } Err(_) => Err(format!("'{input}' bukan angka")), }}
fn main() { for input in ["34", "abc", "200"] { match parse_age(input) { Ok(age) => println!("Umur: {age}"), Err(e) => println!("Error: {e}"), } }}Output:
Umur: 34Error: 'abc' bukan angkaError: 200 terlalu tua untuk jadi umur sungguhan2. Menjumlahkan dua angka dengan ?
Section titled “2. Menjumlahkan dua angka dengan ?”Tulis fn add_strings(a: &str, b: &str) -> Result<i32, std::num::ParseIntError> yang mem-parse kedua string dan mengembalikan jumlahnya. Pakai ?, bukan match. Cetak hasilnya untuk ("10", "20") dan ("10", "dua puluh"). Petunjuk: kamu butuh satu ? untuk setiap parse, dan baris terakhir membungkus hasil penjumlahannya dengan Ok(...).
Lihat solusi
fn add_strings(a: &str, b: &str) -> Result<i32, std::num::ParseIntError> { let x = a.parse::<i32>()?; let y = b.parse::<i32>()?; Ok(x + y)}
fn main() { println!("{:?}", add_strings("10", "20")); println!("{:?}", add_strings("10", "dua puluh"));}Output:
Ok(30)Err(ParseIntError { kind: InvalidDigit })Ringkasan
Section titled “Ringkasan”Result<T, E>mewakili operasi yang bisa berhasil (Ok) atau gagal (Err) dengan sebuah nilai error.matchmenangani kedua kasus secara eksplisit, polanya sama sepertiOption.- Operator
?otomatis mengembalikanErrlebih awal, jadi kamu tidak butuhmatchsetelah setiap pemanggilan yang bisa gagal. - Utamakan
Resultdaripadapanic!untuk error yang wajar bisa dipulihkan oleh pemanggil.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Apa dua varian dari Result<T, E>?
2. Apa yang dilakukan operator ? di dalam fungsi yang mengembalikan Result?
3. Menurut halaman ini, kapan sebaiknya kamu memilih Result daripada panic!?
4. Di contoh divide pada halaman ini, apa yang dikembalikan divide(10.0, 0.0)?
5. Kenapa operator ? hanya bisa dipakai di dalam fungsi yang juga mengembalikan Result (atau Option)?
Skor: 0 / 5
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- Recoverable Errors with Result — The Rust Book (bahasa Inggris): bab resmi tentang
Result,unwrap,expect, dan operator?, dengan contoh membuka file. std::result— dokumentasi standard library (bahasa Inggris): gambaran cara bekerja denganResult, termasuk method sepertimap_err,unwrap_or, danok.- Result — Rust by Example (bahasa Inggris): contoh yang bisa dijalankan tentang menangani
Resultdan mengembalikannya darimain. - The ? operator — Rust by Example (bahasa Inggris): penjelasan singkat dan fokus tentang bagaimana
?menggantikan blokmatchyang bersarang.