Lewati ke isi
JRG++JRG++ v0.2.0

JRG 10 · JRG Lanjut

Kalau Jawabannya Mungkin Tidak Ada

Bagaimana JRG menangani "tidak ketemu" dan "gagal" — tanpa pernah menyerahkan nilai palsu yang kelihatan seperti jawaban.

keandalanMasalah yang diselesaikan

Apa yang harus dikembalikan sebuah fungsi pencari kalau yang dicari tidak ada? Banyak bahasa menjawab null — dan di situlah kesulitan dimulai, karena null terlihat seperti nilai biasa sampai program terlanjur memakainya.

JRG menjawab berbeda: fungsi semacam itu mengembalikan Option, yang isinya salah satu dari dua hal — Some(nilai) kalau ketemu, atau None kalau tidak.

option.jrg
pub fn main() {
    let a = [10, 20, 30]
    wok(a.first())
    wok(a.get(1))
    wok(a.get(99))     // di luar jangkauan -> None, bukan tebakan
}
Option::Some(10)
Option::Some(20)
Option::None

Mengambil isinya dengan aman

CaraKalau ada isinyaKalau kosong
x.unwrap_or(bawaan)IsinyaNilai bawaan yang kamu tentukan
x.is_some()truefalse
matchCabang Some(v)Cabang None
ambil.jrg
pub fn main() {
    let a = [10, 20, 30]

    wok(a.get(1).unwrap_or(0))
    wok(a.get(99).unwrap_or(0))

    match a.get(99) {
        Some(v) => wok("Ketemu:", v),
        None => wok("Tidak ada di posisi itu")
    }
}
20
0
Tidak ada di posisi itu
nullish.jrg
pub fn main() {
    let a = [10, 20]
    wok(a.get(0) ?? -1)
    wok(a.get(9) ?? -1)
}
10
-1

Result: berhasil, atau gagal dengan alasan

Kalau kegagalannya punya sebab yang perlu dijelaskan, JRG memakai Result: Ok(nilai) atau Err(alasan).

result.jrg
fn bagi(a: int, b: int) -> Result {
    if b == 0 {
        return Err("tidak bisa dibagi nol")
    }
    return Ok(a / b)
}

pub fn main() {
    match bagi(10, 2) {
        Ok(v) => wok("Hasil:", v),
        Err(e) => wok("Gagal:", e)
    }
    match bagi(10, 0) {
        Ok(v) => wok("Hasil:", v),
        Err(e) => wok("Gagal:", e)
    }
}
Hasil: 5
Gagal: tidak bisa dibagi nol

Meneruskan kegagalan dengan ?

Menulis match untuk setiap pemanggilan itu melelahkan. Tanda ? berarti: "kalau ini gagal, hentikan fungsi ini dan teruskan galatnya ke atas".

tanya.jrg
fn bagi(a: int, b: int) -> Result {
    if b == 0 { return Err("bagi nol") }
    return Ok(a / b)
}

fn hitung(x: int) -> Result {
    let a = bagi(100, x)?      // gagal di sini -> langsung keluar
    let b = bagi(a, 2)?
    return Ok(b)
}

pub fn main() {
    wok(hitung(5))
    wok(hitung(0))
}
Result::Ok(10)
Result::Err(bagi nol)

Kenapa bukan sekadar nil

Kalau sebuah fungsi boleh menjawab nil untuk "tidak ketemu", maka nil punya dua arti: “tidak ada” dan “ada, isinya memang kosong”. Dua keadaan berbeda dengan satu jawaban yang sama — dan program tidak bisa membedakannya.

Some(nil) ≠ None
Some(nil) → ada isinya, kebetulan kosong
None → memang tidak ada

Kuis 1

Apa yang dikembalikan [1,2].get(99)?

Kuis 2

Apa arti ? di akhir pemanggilan fungsi?

Kuis 3

Kenapa None lebih baik daripada nil untuk "tidak ketemu"?

Latihan mandiri

1. Tulis fungsi akar(n) yang menolak bilangan negatif memakai Result.

2. Ambil elemen ke-5 dari array berisi 3 elemen, dengan nilai cadangan 0.

Jawaban 1: if n < 0 { return Err("akar bilangan negatif") } lalu return Ok(math::sqrt(n)).
Jawaban 2: a.get(5).unwrap_or(0), atau a.get(5) ?? 0.