Sistem sering mengambil keputusan berlapis: pesanan boleh diproses jika pembayaran lunas dan alamat terverifikasi; lampu menyala jika salah satu sensor mendeteksi gerakan. Keputusan berlapis seperti itu diekspresikan dengan operator logika &&, ||, dan !. Di sisi lain, sebagian domain bekerja pada aras yang lebih dalam, misalnya hak akses berkas dan pengolahan data sensor yang diwakili bit demi bit; untuk itu Rust menyediakan operator bitwise &, |, ^, <<, dan >>. Kedua keluarga operator ini memakai simbol yang mirip sehingga pemula sering tertukar, padahal mekanisme kerjanya sangat berbeda. Pada Chapter ini kita akan memakai keduanya dan membuktikan perbedaannya lewat perilaku short-circuit.

Operator Logika #

Tiga operator logika bekerja pada nilai bool dan menghasilkan bool baru:

OperatorNamaHasil true ketika
&&AND logikakedua operan true
||OR logikasalah satu operan true
!NOT (pembalik)operan bernilai false

Kita uji ketiganya pada syarat masuk kelas:

rust
fn main() {
    let sudah_daftar = true;
    let sudah_bayar = false;

    println!("Boleh masuk kelas: {}", sudah_daftar && sudah_bayar);
    println!("Punya akses terbatas: {}", sudah_daftar || sudah_bayar);
    println!("Belum daftar: {}", !sudah_daftar);
}

Output yang Dihasilkan:

text
Boleh masuk kelas: false
Punya akses terbatas: true
Belum daftar: false

Mari kita bedah hasil tiap barisnya:

  1. Baris 5 (sudah_daftar && sudah_bayar): operator && menuntut kedua syarat terpenuhi; karena sudah_bayar bernilai false, hasilnya false.
  2. Baris 6 (sudah_daftar || sudah_bayar): operator || cukup dengan satu syarat; sudah_daftar yang bernilai true membuat hasilnya true.
  3. Baris 7 (!sudah_daftar): operator ! membalik nilai, sehingga true menjadi false.

Operator logika adalah bahan utama kondisi if, dan sebelum kita sampai ke sana, ada satu perilakunya yang wajib dipahami lebih dalam.

Short-Circuit Evaluation #

Operator && dan || milik Rust mengevaluasi operan kiri terlebih dahulu, lalu memutuskan apakah operan kanan masih perlu diperiksa. Jika operan kiri && sudah false, hasil pasti false dan operan kanan tidak dievaluasi sama sekali; sebaliknya jika operan kiri || sudah true, hasil pasti true dan operan kanan dilewati. Perilaku ini bernama short-circuit evaluation, dan justru karena sifat inilah kode pelindung bisa ditulis aman:

rust
fn main() {
    let pembagi = 0;
    let nilai = 100;

    if pembagi != 0 && nilai / pembagi > 10 {
        println!("Hasil pembagian lebih besar dari 10");
    } else {
        println!("Pembagian tidak dilakukan");
    }
}

Output yang Dihasilkan:

text
Pembagian tidak dilakukan

Mari kita amati urutan kejadian di dalam kondisi:

  1. Baris 5 (if pembagi != 0 && nilai / pembagi > 10): operan kiri pembagi != 0 dievaluasi lebih dulu dan menghasilkan false karena pembagi memang 0.
  2. Sisi kanan (nilai / pembagi > 10): ruas ini tidak pernah dievaluasi; short-circuit pada && menyelamatkan program dari pembagian nol yang berujung panic.
  3. Blok else: karena kondisi utama bernilai false, blok else yang dieksekusi dan mencetak pesan aman.

Operator Bitwise #

Operator bitwise memproses representasi biner dari integer, bit demi bit, dan hanya berlaku pada tipe bilangan bulat:

OperatorNamaPerilaku per bit
&ANDhasil 1 jika kedua bit 1
|ORhasil 1 jika salah satu bit 1
^XORhasil 1 jika kedua bit berbeda
!NOTmembalik setiap bit
<<geser kirimenggeser bit ke kiri
>>geser kananmenggeser bit ke kanan

Penggunaan klasiknya adalah mask izin: satu bilangan menyimpan banyak tanda izin, satu bit mewakili satu izin. Kita siapkan izin baca (0100 = 4) dan izin tulis (0010 = 2), lalu gabungkan keduanya dengan OR:

rust
fn main() {
    let izin_baca: u8 = 0b0100;
    let izin_tulis: u8 = 0b0010;

    let izin_gabungan = izin_baca | izin_tulis;

    println!("Izin baca: {:b}", izin_baca);
    println!("Izin tulis: {:b}", izin_tulis);
    println!("Gabungan: {:b} ({})", izin_gabungan, izin_gabungan);
    println!("Punya izin baca: {}", (izin_gabungan & izin_baca) != 0);
    println!("Punya izin hapus: {}", (izin_gabungan & 0b1000) != 0);
}

Output yang Dihasilkan:

text
Izin baca: 100
Izin tulis: 10
Gabungan: 110 (6)
Punya izin baca: true
Punya izin hapus: false

Mari kita bedah operasi bit pada tiap barisnya:

  1. Baris 5 (let izin_gabungan = izin_baca | izin_tulis;): operator | menyatukan kedua mask menjadi 0b0110 alias 6; bit ke-2 menyala dari izin baca dan bit ke-1 menyala dari izin tulis.
  2. Baris 7-9 (println!("...", {:b})): placeholder {:b} mencetak nilai dalam bentuk biner, sehingga 6 tampil sebagai 110.
  3. Baris 10 ((izin_gabungan & izin_baca) != 0): operator & menyaring bit milik izin_baca dari izin_gabungan; hasil 4 (bukan nol) membuktikan izin baca melekat, sehingga perbandingan menghasilkan true.
  4. Baris 11 ((izin_gabungan & 0b1000) != 0): mask 0b1000 menguji bit izin hapus yang memang tidak pernah dinyalakan, sehingga hasil AND-nya 0 dan perbandingan bernilai false.

Operator ^ (XOR) punya sifat khusus yang berguna: meng-XOR-kan sebuah bit dengan 1 akan membalik status bit itu. Teknik ini dipakai untuk menyalakan-mematikan sakelar:

rust
fn main() {
    let mode_dasar: u8 = 0b0100;
    let sakelar: u8 = 0b0100;
    let mode_baru = mode_dasar ^ sakelar;

    println!("Mode dasar: {:b}", mode_dasar);
    println!("Mode baru : {:b} ({})", mode_baru, mode_baru);

    let satu_bit: u8 = 1;
    let nilai_besar: u8 = 200;

    println!("1 << 3 = {}", satu_bit << 3);
    println!("200 >> 4 = {}", nilai_besar >> 4);
}

Output yang Dihasilkan:

text
Mode dasar: 100
Mode baru : 0 (0)
1 << 3 = 8
200 >> 4 = 12

Mari kita telaah hasilnya:

  1. Baris 4 (let mode_baru = mode_dasar ^ sakelar;): kedua nilai memiliki bit yang sama-sama menyala di posisi yang sama, sehingga XOR menghasilkan 0; sakelar berhasil mematikan mode.
  2. Baris 12 (satu_bit << 3): geser kiri memindahkan bit 1 tiga posisi menjadi 0b1000 alias 8; setiap geser satu langkah sama dengan mengalikan dua.
  3. Baris 13 (nilai_besar >> 4): geser kanan membuang empat bit terbawah dari 200 sehingga tersisa 12; setiap geser satu langkah sama dengan membagi dua dan memotong sisanya.

Ringkasan #

Operator logika &&, ||, dan ! bekerja pada bool dengan evaluasi short-circuit yang menyelamatkan ekspresi seperti pemeriksaan pembagi nol. Operator bitwise &, |, ^, !, <<, dan >> mengoperasikan integer bit demi bit tanpa short-circuit, dan cocok untuk mask izin, sakelar bit, serta pergeseran yang setara perkalian atau pembagian dua. Simbol && dan & terlihat serupa, tetapi pilihan yang salah di antara keduanya mengubah hasil dan keselamatan program. Selanjutnya kita belajar mengonversi tipe data secara eksplisit dengan kata kunci as.