Tipe integer menyimpan nilai dalam jumlah bit yang tetap, seperti u8 yang hanya punya delapan bit untuk angka 0 s.d. 255. Batas ini biasanya tidak terasa, sampai suatu ketika counter lampu di angka 255 ditambah satu, atau suhu -128 derajat dikurangi satu lagi. Kejadian tersebut bernama integer overflow: hasil perhitungan keluar dari jangkauan tipe sehingga representasi binernya tidak lagi muat. Insiden teknik nyata berakar pada masalah sederhana ini, mulai dari roket Ariane 5 yang menghancurkan dirinya sendiri karena angka navigasi tidak muat saat dikonversi ke integer sempit, hingga YouTube yang terpaksa memperluas penghitung tayangannya setelah satu video melampaui batas bilangan 32-bit. Rust mengambil pendekatan yang berbeda per mode kompilasi: mode debug menghentikan program saat overflow terjadi, sementara mode release membiarkan nilai berputar. Pada Chapter ini kita akan membuktikan kedua perilaku itu secara nyata, lalu mempelajari metode checked, wrapping, saturating, dan overflowing untuk mengendalikan overflow secara eksplisit.

Mode Debug: Program Berhenti dengan panic #

Kompilasi tanpa optimasi apa pun adalah mode debug, dan mode ini memasang pemeriksaan overflow pada setiap operasi aritmetika integer. Ketika hasil perhitungan melampaui jangkauan tipe, program berhenti total melalui mekanisme panic. Kode berikut menambah nilai u8 yang sudah berada di batas maksimum:

rust
fn main() {
    let mut lampu: u8 = 255;

    lampu = lampu + 1;

    println!("Nilai lampu: {}", lampu);
}

Simpan kode tersebut sebagai main.rs, lalu kompilasi dan jalankan tanpa optimasi:

bash
rustc --edition 2021 main.rs -o program_debug
./program_debug

Program tidak pernah mencetak apa pun; ia berhenti dengan keluaran berikut:

text
thread 'main' (6823) panicked at main.rs:4:13:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Mari kita bedah kejadiannya:

  1. Baris 2 (let mut lampu: u8 = 255;): variable lampu menyimpan 255, nilai terbesar yang mampu ditampung delapan bit tanpa bertanda.
  2. Baris 4 (lampu = lampu + 1;): pemeriksaan debug mendeteksi hasil 256 melebihi jangkauan u8 sebelum nilai disimpan; program langsung panic di titik ini dan baris setelahnya tidak pernah tereksekusi.
  3. Lokasi main.rs:4:13: pasangan angka menunjuk baris dan kolom operasi penjumlahan; angka dalam tanda kurung setelah kata main adalah nomor thread sistem operasi yang berbeda pada setiap eksekusi.

Pesan attempt to add with overflow menyebut operasi yang gagal secara spesifik, sehingga kita langsung tahu harus memeriksa penjumlahan mana. Perilaku menghentikan program tampak keras, tetapi inilah tujuannya: data korup yang berjalan diam-diam jauh lebih mahal daripada program yang berhenti dengan pesan jelas.

Mode Release: Nilai Berputar dengan Wrapping #

Saat mengompilasi dengan optimasi (-O atau cargo build --release), pemeriksaan overflow dilepas demi kecepatan eksekusi, dan operasi integer mengikuti perilaku wrapping dua komplemen (two’s complement): bit yang meluber terbuang dan nilai melanjutkan dari ujung rentang yang lain. Kita buktikan dengan mengompilasi ulang berkas yang sama:

bash
rustc --edition 2021 -O main.rs -o program_release
./program_release

Output yang Dihasilkan:

text
Nilai lampu: 0

Mari kita amati prosesnya bit demi bit:

  1. Nilai 255 dalam biner delapan bit adalah 11111111.
  2. Penambahan 1 menghasilkan 100000000 dengan sembilan bit, sementara u8 hanya menyimpan delapan bit.
  3. Bit kesembilan terbuang, menyisakan 00000000 yang dibaca sebagai 0; nilai berputar dari batas atas ke batas bawah seolah mengitari jam.

Untuk tipe bertanda seperti i8, putaran terjadi pada dua ujungnya: batas bawah -128 dikurangi satu menghasilkan 127, karena pola bit 10000000 bergeser menjadi 01111111. Jika kita memakai Cargo dalam proyek nyata, cargo run memakai mode debug, sedangkan cargo run --release memakai mode release; beda perilaku inilah alasan mengapa bug overflow kerap baru muncul di lingkungan produksi.

Metode checked: Menolak Hasil yang Meluap #

Metode checked_add, checked_sub, dan checked_mul mengembalikan hasil pembungkus: Some(hasil) ketika perhitungan aman, atau None ketika terjadi overflow. Dengan bentuk hasil tersebut, kode kita dipaksa menghadapi kedua kemungkinan secara eksplisit:

rust
fn main() {
    let lampu: u8 = 255;

    match lampu.checked_add(1) {
        Some(hasil) => println!("255 + 1 = {}", hasil),
        None => println!("255 + 1 overflow, penjumlahan dibatalkan"),
    }

    match lampu.checked_sub(10) {
        Some(hasil) => println!("255 - 10 = {}", hasil),
        None => println!("255 - 10 overflow, pengurangan dibatalkan"),
    }
}

Output yang Dihasilkan:

text
255 + 1 overflow, penjumlahan dibatalkan
255 - 10 = 245

Mari kita bedah mekanismenya:

  1. Baris 4 (lampu.checked_add(1)): karena 255 + 1 melampaui 255, method mengembalikan None; cabang None pada match yang terpilih dan penjumlahan dibatalkan tanpa panic.
  2. Baris 9 (lampu.checked_sub(10)): 255 - 10 = 245 masih di dalam jangkauan, sehingga method mengembalikan Some(245) dan cabang pertama mencetak hasilnya.

Metode wrapping dan saturating #

Ketika nilai berputar justru diinginkan, metode wrapping_ menyatakannya secara eksplisit dan berperilaku sama di semua mode kompilasi. Sebaliknya, metode saturating_ menahan nilai di batas terdekat alih-alih berputar: penjumlahan yang melampaui maksimum berhenti tepat di maksimum:

rust
fn main() {
    let lampu: u8 = 255;
    let titik_beku: i8 = -128;

    println!("u8 255 wrapping_add(1)  : {}", lampu.wrapping_add(1));
    println!("i8 -128 wrapping_sub(1) : {}", titik_beku.wrapping_sub(1));
    println!("u8 255 saturating_add(1): {}", lampu.saturating_add(1));
    println!("i8 -128 saturating_sub(1): {}", titik_beku.saturating_sub(1));
}

Output yang Dihasilkan:

text
u8 255 wrapping_add(1)  : 0
i8 -128 wrapping_sub(1) : 127
u8 255 saturating_add(1): 255
i8 -128 saturating_sub(1): -128

Mari kita bedah keempat hasilnya:

  1. Baris 5 (lampu.wrapping_add(1)): 255 + 1 berputar ke 0, sama seperti perilaku mode release, tetapi kini dinyatakan gamblang di dalam kode.
  2. Baris 6 (titik_beku.wrapping_sub(1)): -128 - 1 berputar ke batas atas i8, yaitu 127, sesuai hukum dua komplemen.
  3. Baris 7 (lampu.saturating_add(1)): nilai menepi di 255 alih-alih berputar; saturating cocok untuk data seperti persentase atau volume yang tidak boleh melompat ke nol.
  4. Baris 8 (titik_beku.saturating_sub(1)): pada i8 bawah, saturating_sub menahan nilai di -128 karena sudah berada di batas minimum.

Pilihan antara keduanya adalah keputusan domain: counter jam berputar masuk akal, sedangkan stok barang atau skor kelayakan kredit harus memakai saturating agar tidak tiba-tiba nol.

Metode overflowing: Hasil Sekaligus Tanda Meluap #

Metode overflowing_ mengembalikan tuple berisi hasil perhitungan dan penanda boolean apakah overflow terjadi. Bentuk ini menghubungkan langsung materi tuple pada Chapter Tipe Data Komposit:

rust
fn main() {
    let lampu: u8 = 255;

    let (hasil, meluap) = lampu.overflowing_add(1);
    println!("255 + 1 = {}, meluap: {}", hasil, meluap);

    let (hasil_aman, meluap_aman) = lampu.overflowing_add(0);
    println!("255 + 0 = {}, meluap: {}", hasil_aman, meluap_aman);
}

Output yang Dihasilkan:

text
255 + 1 = 0, meluap: true
255 + 0 = 255, meluap: false

Mari kita telaah bentuk keluarannya:

  1. Baris 4 (let (hasil, meluap) = lampu.overflowing_add(1);): destructuring tuple memisahkan dua anggota; hasil berisi 0 karena wrapping terjadi, dan meluap bernilai true.
  2. Baris 7 (lampu.overflowing_add(0)): penjumlahan dengan nol tidak pernah meluap, sehingga tuple berisi 255 dan false.

Metode ini pas ketika kita butuh hasil wrapping sekaligus catatan diagnostik, misalnya mencatat kejadian overflow ke log tanpa menghentikan program.

Ringkasan #

Integer overflow terjadi ketika hasil perhitungan keluar dari jangkauan tipe. Mode debug menghentikan program dengan panic attempt to add with overflow lengkap dengan lokasi baris, sedangkan mode release membiarkan nilai berputar mengikuti dua komplemen. Metode checked_ mengembalikan Some/None agar overflow ditangani eksplisit, wrapping_ menyatakan putaran secara sah lintas mode, saturating_ menahan nilai di batas terdekat, dan overflowing_ mengembalikan hasil beserta tanda meluap dalam satu tuple. Pemilihan metode yang tepat bergantung pada makna data: putaran untuk siklus, saturasi untuk batas, dan checked untuk penolakan. Section Dasar Sintaks telah lengkap; selanjutnya kita masuk ke Kontrol Alur untuk mengatur jalur eksekusi program.