assert harus benar-benar diperbaiki
(kristoff.it)- assert adalah mekanisme untuk menyatakan prasyarat, pascakondisi, dan invarian di dalam kode, dan jika sebuah batasan bisa dipaksakan oleh sistem tipe, lebih baik mengekspresikannya dengan fitur bahasa
std.debug.assertdi Zig adalah fungsi biasa, bukan makro; ia menandai jalur yang mustahil dicapai melaluiunreachabledan juga dapat dimanfaatkan untuk optimisasi- Dalam Debug dan ReleaseSafe, assert yang gagal akan menyebabkan panic dan crash, tetapi dalam ReleaseFast dan ReleaseSmall, hal itu dapat menjadi unchecked illegal behavior yang membuat program berperilaku salah
- Mematikan assert di production membuat kita kehilangan kesempatan menemukan asumsi yang salah lebih cepat, dan setelah itu kode dapat bergantung pada assert yang keliru hingga berujung pada kerentanan
- Pilihan antara ReleaseSafe dan ReleaseFast bergantung pada prioritas program, tetapi intinya adalah jangan menonaktifkan assert untuk menutupinya — assert yang salah harus diperbaiki
Peran assert dan perilaku dasar Zig
- assert adalah mekanisme untuk mengekspresikan dalam kode bahwa kondisi seperti “argumen ini tidak mungkin null” atau “bilangan bulat ini tidak mungkin genap” harus selalu benar
- Contoh:
assert(my_arg != null);,assert(my_num % 2 != 0); - Jika sebuah batasan dapat dipaksakan lewat sistem tipe, lebih baik memakai fitur bahasa daripada assert
- Di Zig, pointer biasa
*Footidak bisa bernilai null, sedangkan pointer opsional?*Foobisa null tetapi mewajibkan pengecekan sebelum nilainya diakses
- Contoh:
- assert cocok untuk menyatakan prasyarat, pascakondisi, dan invarian
- assert yang baik bisa lebih kuat daripada unit test dalam menangkap kesalahan pemrograman
- Jika dipakai bersama fuzzing, efek assert bisa menjadi lebih besar
unreachable di Zig dan assert
- assert di Zig dibangun di atas
unreachable, fitur bahasa untuk menandai jalur kode yang salah- Dalam
switch, cabang yang tidak mungkin tercapai dapat ditandai seperti.a => unreachable unreachabledapat digunakan sebagai statement maupun di tempat yang memerlukan ekspresi dari tipe apa pun- Tidak perlu memaksakan pembuatan nilai sementara untuk kasus yang mustahil dicapai
- Dalam
std.debug.assertdi pustaka standar Zig diimplementasikan seperti inipub fn assert(ok: bool) void { if (!ok) unreachable; // assertion failure }- Informasi
unreachabledapat dimanfaatkan untuk optimisasi- Kompiler dapat menghapus jalur yang tak mungkin dicapai, dan informasi ini bisa dipropagasikan sehingga memungkinkan optimisasi nonlokal
- Tidak semua assert akan menghasilkan peningkatan performa, tetapi optimisasi yang sulit diprediksi programmer pun bisa terjadi
Mode build dan keamanan runtime
- Zig memiliki mode build Debug, ReleaseSafe, ReleaseFast, dan ReleaseSmall
- Pengaturan ini tidak harus selalu diterapkan secara global untuk seluruh program
- Tiap dependensi dapat dibangun dengan mode berbeda, dan dengan
@setRuntimeSafety, keamanan runtime juga bisa diatur per blok di dalam fungsi
- Kegagalan assert di Zig dianggap sebagai “illegal behavior”
- Dalam mode checked seperti Debug, ReleaseSafe, dan
@setRuntimeSafety(true), program akan crash dengan panic - Dalam mode unchecked seperti ReleaseFast, ReleaseSmall, dan
@setRuntimeSafety(false), akan terjadi “unchecked illegal behavior” sehingga program berperilaku salah
- Dalam mode checked seperti Debug, ReleaseSafe, dan
- Hasil dari unchecked illegal behavior tidak dijamin
- Dalam contoh
switch, berdasarkan karakteristik machine code yang saat ini dihasilkan, perilakunya bisa tampak seperti lompat ke cabang lain - Di versi kompiler lain, bisa muncul perilaku salah yang sama sekali berbeda
- Perilaku terkait bisa dilihat di contoh godbolt
- Dalam contoh
- Perbedaan perilaku assert dan
switchsesudahnya antara ReleaseSafe dan ReleaseFast dapat dilihat di contoh godbolt lain- Dalam ReleaseFast, fungsi tampak melewati semua perbandingan dan langsung mengembalikan
true - Optimisasi semacam ini adalah jenis perilaku yang sangat diandalkan oleh video game dan aplikasi media real-time lainnya
- Dalam ReleaseFast, fungsi tampak melewati semua perbandingan dan langsung mengembalikan
Zig assert bukan makro
std.debug.assertdi Zig adalah fungsi biasa, bukan makro- Zig tidak memiliki makro
- Ini sering menjadi hal yang mengejutkan terutama bagi developer C/C++ yang baru mendekati Zig
- Dalam C/C++, jika assert dinonaktifkan, lazimnya seluruh pemanggilan assert dan ekspresi yang diberikan akan diperlakukan seolah-olah ikut dikomentari
- Karena itu, di C/C++ tidak boleh memasukkan ekspresi yang punya efek samping ke dalam assert
- Saat assert dinonaktifkan, operasi itu sendiri bisa hilang
- Di Zig, sesuai aturan pemanggilan fungsi, argumen dievaluasi sebelum fungsi dipanggil
- Terlepas dari logika internal
std.debug.assert, ekspresi argumennya tetap dievaluasi - Karena itu, ekspresi dengan efek samping seperti berikut pun bisa dimasukkan ke assert
// assert that the remove operation is not a noop: assert(my_map.remove("expected-to-exist")); - Terlepas dari logika internal
- Sebaliknya, jika menghitung kondisi assert memerlukan operasi yang kompleks, perhitungan itu mungkin tidak selalu dieliminasi dalam mode unchecked
- Dalam kasus seperti ini, kode harus dijaga dengan
comptime if
const builtin = @import("builtin"); if (builtin.mode == .Debug) { var condition = ...; // whatever bookkeeping is necessary // to compute the condition assert(condition == .ok); } - Dalam kasus seperti ini, kode harus dijaga dengan
- Jika terbiasa dengan semantik C/C++, ini mungkin terasa asing, tetapi di Zig terdapat asumsi bahwa assert pada umumnya tidak dinonaktifkan
Masalah mematikan assert di production
- Secara garis besar, ada tiga pilihan untuk assert
- Mempertahankannya sebagai runtime check dan membiarkan proses crash dengan panic saat gagal
- Memakai assert untuk optimisasi performa sambil menerima risiko program salah berperilaku ketika assert tersebut keliru
- Menonaktifkan assert sepenuhnya
std.debug.asserttidak menyediakan dukungan bawaan untuk menonaktifkan assert sepenuhnya- Jika membuat assert sendiri yang memeriksa flag build pada saat kompilasi, kita bisa membuat perilaku yang lebih mirip gaya C/C++
- Alasan orang ingin mematikan assert biasanya merupakan gabungan dari dua hal
- Mereka tidak ingin mempertahankan runtime check karena tak suka biaya performa atau crash aplikasi
- Mereka juga sulit percaya bahwa assert selalu benar, sehingga takut pada perilaku salah yang bisa terjadi saat assert dipakai untuk optimisasi
- Seperti diingatkan matklad dalam diskusi terkait, memang ada situasi dengan alasan engineering yang sah untuk menghindari crash
- Tetapi dalam software umum, menjadikan penghindaran crash sebagai default dinilai sebagai pilihan yang buruk
- Jika assert dinonaktifkan, kondisi yang diasumsikan mustahil tetap dapat terjadi dan program akan terus berjalan
- Program terus berjalan di bawah asumsi yang salah, dan itu sendiri merupakan bentuk salah perilaku meskipun bukan unchecked illegal behavior
- Alasan unchecked illegal behavior atau undefined behavior di C berbahaya adalah karena hal itu bisa menjadi jalan yang mengubah program menjadi weird machine
- Dalam software yang cukup kompleks, bahkan tanpa UIB pun program bisa terpelintir dengan cara yang tidak dimaksudkan
- Assert yang bernilai false saat runtime berarti keluar dari spesifikasi, dan itu sendiri dapat membuat program melakukan tindakan yang tak dimaksudkan
- SQL injection adalah contoh konkret dan sangat umum dari salah perilaku setingkat weird machine tanpa melibatkan UIB
- Jika biaya salah perilaku program terlalu tinggi, maka assert sebaiknya tetap dinyalakan
- Jika performa sangat penting sehingga risiko salah perilaku bisa diterima, maka assert sebaiknya dipakai sebagai peluang optimisasi
- Menonaktifkan assert membuat kita kehilangan performa sekaligus mudah salah mengira sistem lebih aman daripada kenyataannya
Cara assert yang salah menipu codebase
- Risiko utamanya adalah assert yang keliru bisa tidak terlihat saat testing dan baru gagal di production
- Jika dapat dijamin bahwa semua assert selalu benar, pemakaian assert untuk optimisasi tidak akan menjadi kontroversi
- Jika testing dapat dijamin menangkap semua assert yang salah, optimisasi di production juga menjadi aman
- Pada kenyataannya, kita bisa menulis assert yang salah, dan testing pun tidak selalu menangkapnya
- Jika assert dimatikan di production, kita kehilangan kesempatan menemukan assert yang salah secepat mungkin
- Masalah yang lebih serius adalah kode berikutnya terus ditulis dengan bergantung pada assert yang salah tersebut
- Dalam contoh kode, diasumsikan
processThinghanya boleh dipanggil padathingyang sudah dimulai, dan asumsi itu ditaruh sebagai assertfn processThing(thing: Thing) void { // this function must always be invoked on // a thing that has already been started assert(thing.is_started); // ... } - Bisa saja assert ini tidak pernah gagal saat testing, lalu tanpa disadari dinonaktifkan di production padahal dalam kenyataan bisa bernilai false
- Jika tidak ada salah perilaku yang terlihat oleh pengguna, semuanya bisa tampak baik-baik saja dan pengembangan terus berlanjut
- Setelah itu, seseorang bisa menambahkan kode dengan asumsi bahwa
thingsudah dimulai, sehinggabazaman dipanggil tanpa persiapan tambahanfn processThing(thing: Thing) void { // this function must always be invoked on // a thing that has already been started assert(thing.is_started); // ... // Since thing is already started, we don't // need to foo the bar before bazzing the qux. // It would be really bad to baz the qux otherwise, // so we add an assert for good measure. assert(thing.is_fooed); thing.baz(qux); } - Meskipun assert kedua secara logika benar, tetap muncul bahaya jika assert pertama sebenarnya dapat bernilai false
- Dalam testing, assert pertama tidak gagal, sehingga assert kedua pun tidak gagal
- Di production, karena assert dinonaktifkan, kita mungkin tidak menyadari saat kerentanan masuk ke codebase
- Jika assert di dalam kode sudah menipu developer, maka menulis kode yang benar menjadi jauh lebih sulit daripada seharusnya
Pilihan bergantung pada prioritas program
- Tiap program memiliki prioritas yang berbeda, dan untuk sebagian program, memprioritaskan performa daripada meminimalkan risiko salah perilaku bisa saja masuk akal
- Dalam kasus ini, mengubah assert menjadi peluang optimisasi adalah pilihan yang wajar
- Menonaktifkan assert di production secara kebiasaan dinilai sebagai pilihan yang lebih buruk daripada membiarkannya tetap aktif, bahkan juga lebih buruk daripada secara aktif memanfaatkan optimisasi performa
- Zine adalah static site generator, dan saat ini terutama dipakai untuk membangun blog pribadi
- Threat model-nya belum terdefinisi dan itu juga bukan prioritas utamanya
- Karena disukai bisa berjalan satu orde magnitudo lebih cepat daripada Hugo, build ReleaseFast yang didistribusikan
- Awebo adalah alternatif Discord self-hosted yang masih berada di tahap pre-alpha
- Sudah jelas bahwa ini software yang akan menangani data pribadi dan terekspos ke internet
- Saat distribusi nanti, rencananya build ReleaseSafe yang akan disediakan
- Namun, beberapa dependensi inti seperti FFmpeg, Xiph Opus, dan SQLite tetap akan dibangun dengan ReleaseFast
- Pada dependensi tersebut, peningkatan performa dinilai jelas lebih penting daripada mengurangi risiko salah perilaku program lebih jauh
Pilihan proyek nyata dan kasus keamanan
- TigerBeetle adalah database finansial dan selalu membiarkan assert tetap aktif
- Ghostty adalah emulator terminal dan mendistribusikan build ReleaseFast untuk macOS
- Pendekatan yang sama juga direkomendasikan kepada konsumen downstream, misalnya pengelola distribusi Linux
- Dua CVE yang dipublikasikan untuk Ghostty dan tergolong cukup serius sama-sama merupakan kasus eksekusi perintah arbitrer tanpa kerusakan memori
- Kerusakan memori atau UIB bukan satu-satunya sumber risiko
Assert implisit yang tidak benar-benar hilang di Zig
- Sekalipun assert buatan sendiri dapat dinonaktifkan, assert implisit yang ditambahkan bahasa Zig sendiri ke dalam kode tidak bisa dinonaktifkan
- Integer overflow, pembagian dengan 0, dan akses array di luar batas termasuk di dalamnya
- Kondisi-kondisi ini akan menimbulkan panic runtime atau dipakai untuk tujuan optimisasi
- Kebiasaan menonaktifkan assert di production dapat membuat assert yang salah membusuk dan bertambah di dalam codebase
- Akibatnya, paranoia terhadap UIB bisa makin besar, dan developer bisa secara bawah sadar takut menyalakan kembali assert lalu menghadapi hasilnya
- Kesimpulan yang tak terhindarkan adalah bahwa kita tidak boleh menutupi masalah dengan menonaktifkan assert, melainkan harus memperbaiki assert yang salah
- Kebenaran program harus diupayakan untuk keseluruhan, bukan hanya untuk sebagian subset
1 komentar
Opini Lobste.rs
Saya setuju bahwa pada
assert, sekadar crash, atau perilaku seperti panic di Rust yang hanya membuat tugasnya crash, umumnya adalah pilihan terbaik. Namun, saya sulit setuju bahwa memakaiassertsebagai petunjuk optimisasi selalu lebih baik daripada sekadar menghapusnyaPertama,
assertyang arbitrer sering kali tidak banyak membantu optimisasi, dan banyak kondisi juga tidak bisa langsung dimanfaatkan oleh pengoptimal. Kecuali Anda memasukkan asumsi yang langsung seperti “cabang ini tidak akan pernah dilewati”, keuntungan performa dari menaburkan asumsi acak di seluruh kode kemungkinan tidak besarKedua, mengubah
assertmenjadi asumsi sangat memperluas radius dampak dari sebuah kesalahan. Misalnya, dalam sistem yang memproses data yang dipisahkan per proyek atau per pengguna, bayangkan adaassertdi tengah fungsi perhitungan yang menangkap keadaan yang semestinya mustahil terjadi. Jika dalam build rilis ia dimatikan karena mahal, maka jika hanya dinonaktifkan, kerusakannya bisa terbatas pada satu proyek atau pengguna dan mungkin tertangkap pada pemeriksaan berikutnya. Sebaliknya, jika itu dijadikan perilaku tak terdefinisi, perhitungan bisa lompat ke kode yang tidak semestinya, merusak memori secara sewenang-wenang, dan merusak data semua proyekPada akhirnya, memilih
assertyang tidak aman sebagai default untuk build rilis berarti mengoptimalkan secara prematur di titik-titik acak dalam kode, dengan konsekuensi berkurangnya peluang untuk melokalisasi dampak saat masalah terjadi. Menurut saya Rust dirancang dengan baik karenaassert!()selalu panic,debug_assert!()hanya panic di mode debug, danassert_unchecked()panic di debug lalu menjadi petunjuk optimisasi di rilisReleaseSafealih-alihReleaseFastasserttertentu, melainkan menentang mematikannya secara massal seolah itu praktik umum yang disarankanMenilai bahwa dampak performanya terlalu besar sehingga tidak bisa dipertahankan di build rilis adalah keputusan yang sepenuhnya masuk akal. Lagipula,
assertyang biaya komputasinya besar juga, seperti disebut sebelumnya, hampir tidak mungkin menghasilkan peningkatan performaAda beberapa contoh seperti itu di Zine:
https://github.com/kristoff-it/zine/…
https://github.com/kristoff-it/zine/…
Zig tidak memiliki “mode rilis default”. Anda selalu harus memilih sendiri bagaimana
assertdiperlakukan, dan opsi globalnya adalah crash atau optimisasi, tanpa salah satunya bisa dibilang lebih default daripada yang lainFakta bahwa dua CVE yang relatif serius dan sejauh ini dipublikasikan di Ghostty sama-sama berujung pada eksekusi perintah arbitrer tanpa kerusakan memori terasa sangat aneh. Fakta bahwa itu terjadi meski didistribusikan dengan ReleaseFast benar-benar bertentangan dengan pemahaman saya tentang cara dunia bekerja
Dari pengalaman menangani terminal emulator, kerentanan ini adalah jenis masalah menyebalkan yang persis bisa diduga. Bukan untuk merendahkan pengembang atau peneliti, tetapi injeksi perintah di tempat yang tidak terduga seperti ini nyaris merupakan masalah bawaan di bidang ini, mirip dengan bagaimana kerentanan injeksi lain ikut muncul di bidang lain
Menarik bahwa saya sudah hampir 40 tahun mendengar argumen untuk mematikan
assertdan pemeriksaan batas di produksi “demi performa”. Selama itu, komputer sudah menjadi beberapa orde magnitudo lebih cepat, dan perangkat lunak sudah jauh lebih masuk ke kehidupan semua orang, jadi ketepatan runtime lebih penting daripada sebelumnyaAgar pembahasannya lebih produktif, dulu di Microsoft ada sesuatu yang, selain
assert,check, dan sejenisnya yang umum, berupa assert untuk pelaporan yang jarang saya lihat di tempat lain. Ini dipakai saat ada kondisi yang tidak sepenuhnya saya kendalikan, saya mengasumsikan itu benar tetapi tetap menangani secara defensif jika ternyata salah, dan saya ingin tahu lewat log atau telemetri apakah itu benar-benar pernah salah di lapangan. Misalnya, saya mengasumsikan pengguna tidak akan memasukkan lebih dari 1000 item ke daftar tertentu sehingga saya memakai algoritme kuadratik, atau saya menganggap latensi jaringan akan di bawah 200 ms sehingga saya memakai protokol dengan banyak round-tripcheck?Sebagai salah satu orang yang ditautkan di sini, ini menjadikan pandangan saya tentang
assertsebagai dikotomi palsu dan karikatur yang konyol. Seperti yang saya tulis di komentar lain, saya lebih memilih memutuskan perassertapakah akan diubah menjadi perilaku tak terdefinisi. Kritik saya terhadap ReleaseFast adalah bahwa pilihan itu dibundel bukan hanya untuk semuaassertdalam cakupan tertentu, tetapi juga untuk semua pemeriksaan keamananSaya setuju dengan kristoff bahwa mematikan
assertyang belum diperbaiki hanya karena ia menyebabkan crash adalah tindakan bodoh. Namun, saya tidak setuju bahwa satu-satunya alternatif yang masuk akal hanyalah “crash atau perilaku tak terdefinisi”. Pandangan goldstein di komentar saudara lebih dekat dengan posisi sayaMenjadikan perilaku
assert_unchecked()sebagai default global memang sulit dibela, tetapi itu bisa masuk akal sebagai teknik optimisasi performa. Jika mengubah semuaassertmenjadi asumsi membuat build produksi jauh lebih cepat, mungkin ada sejumlah kecil asumsi, semoga hanya satu, yang menghasilkan sebagian besar peningkatan performa itu, dan itu bisa ditemukan dengan metode seperti pencarian binerReleaseSafedanReleaseFast/ReleaseSmallDalam literatur analisis program, ada dualitas yang membagi pernyataan atau
assertdi dalam kode menjadi dua bentuk. Yang satu adalah konteks di sekitar kode—untuk fungsi, ini adalah kondisi yang harus dipenuhi pemanggil—dan yang lain adalah kode itu sendiri—untuk fungsi, ini adalah kondisi yang harus dipenuhi fungsi tersebutPembedaan ini menjadi jelas jika dilihat melalui konsep akademis standar “blame” dalam literatur kontrak dan tipe gradual. Jika pernyataan tentang konteks gagal, itu bukan kesalahan kita melainkan tanggung jawab konteks atau pemanggil, meski bisa juga pemanggil benar dan justru pernyataannya yang bug. Jika pernyataan tentang kode itu sendiri gagal, itu tanggung jawab kita, meski bisa juga kodenya benar dan justru pernyataannya yang bug
Pada tingkat fungsi, prasyarat adalah pernyataan tentang konteks, dan pascakondisi adalah pernyataan tentang kode itu sendiri. Namun, keduanya bisa ditempatkan juga di tengah-tengah kode. Beberapa framework verifikasi memakai
assertuntuk pernyataan tentang kode, danassumeuntuk pernyataan tentang konteks. Ini juga terkait dengan cara beberapa framework pengujian—terutama framework pengujian acak—menafsirkannya. Jikaassertgagal, itu ditandai sebagai kegagalan tes; jikaassumegagal, tes dilewatiIni tampaknya menyindir Bun, jadi saya ingin membuat kaitannya sedikit lebih formal. Ada issue Zig pada 2024 di mana pembuat Bun, Jarred Sumner, mengusulkan agar
unreachablemelakukan panic di ReleaseFast. Komentar Andrew Kelley dan Matthew Lugg di thread itu relevan dengan pembahasan ini=> https://github.com/ziglang/zig/issues/19664
Bun memakai fungsi
assertbuatannya sendiri, dan dalam mode rilis itu akan panic atau dihapus, tetapi tidak memperkenalkan undefined behavior. Namun, catatan kaki dari Loris juga perlu diingat: “Sebagai bahasa, Zig secara implisit menambahkan banyakassertke dalam kode yang tidak bisa dinonaktifkan”Saya tidak ingin terlalu panjang membahas Bun, karena itu proyek tunggal dari tim kecil. Intinya, jika ada sedikit saja kekhawatiran, gunakan ReleaseSafe. ReleaseSafe punya reputasi lambat, tetapi pada proyek-proyek Zig kecil saya, saya tidak bisa mengukur perbedaan benchmark antara ReleaseSafe dan ReleaseFast. Meski begitu, kemungkinan besar tetap lebih cepat daripada banyak bahasa lain
Atau, jika masuk akal dalam konteksnya, Anda bisa mendistribusikan biner ReleaseFast lalu, ketika mulai muncul laporan bug non-deterministik akibat undefined behavior, kembali ke ReleaseSafe. Dengan begitu Anda bisa mengumpulkan laporan bug yang bisa ditindaklanjuti tentang
assertmana yang gagal, termasuk akses di luar batas atau overflow, lalu memperbaiki kodenya. Saya bahkan akan merekomendasikan pendekatan ini meskipun sejak awal Anda membuat keputusan untuk mendistribusikan ReleaseFast dalam konteks yang sebenarnya tidak seharusnya :^)Anda juga bisa menyesuaikan dependensi dan memakai
@setRuntimeSafetyuntuk menerapkan pendekatan yang sama hanya pada sebagian proyek. Pada akhirnya, jika ada kemauan untuk melakukannya dengan cerdas, semua alat yang dibutuhkan sudah tersediaJangan menulis seolah-olah boleh memasukkan ekspresi yang punya efek samping ke dalam pemanggilan
assert. Itu praktik buruk. Memakaiassertuntuk pengecekan error juga sebaiknya dihindari. Supaya adil, penulis tampaknya tidak sedang menganjurkan ituSebaliknya, juga disebutkan bahwa jika
assertbergantung pada perhitungan yang kompleks, perhitungan itu belum tentu dihapus dalam mode unchecked, sehingga perlu dijaga dengancomptime ifSaya harap penulis tidak melewatkan ironi dari kalimat “kesempatan yang bagus untuk membuang trauma yang ditinggalkan makro dan menerima kesederhanaan”. Maksudnya, kita diminta menerima “kesederhanaan” berupa harus mempertimbangkan mode build program dan menaburkan
comptime ifdefensif di mana-manaSaya menulis sedikit kode komputasi numerik dalam C#, dan banyak memakai
assertyang dimatikan di mode rilis. Menjalankannya di setiap loop yang padat terlalu mahal, tetapi dalam unit test sangat berguna jika rutin langsung meledak pada saat pertama kali melihat input NaNNaN seperti ini sering bukan berasal dari input pengguna, melainkan dari bug kode—misalnya optimizer masuk ke jalur yang seharusnya tidak boleh—dan berarti dibutuhkan pembatasan batas yang lebih baik. Tentu saja input pengguna mungkin juga perlu disanitasi, tetapi itu harus dilakukan di batas paling luar, bukan jauh di dalam algoritme. Akan bagus jika ada sistem pembuktian yang bisa membuktikan invarian internal algoritme tanpa
assertsebagai hasil dari sanitasi input pengguna, tetapi ini proyek sampingan, dan kalau sampai meledak tidak ada yang matiSebanyak 90% perbedaan pendapat tentang
assertmuncul karena definisi istilah itu buruk dan bermacam-macam, sehingga mengaburkan pemikiran dan komunikasi. Karena itu, konsepnya perlu dipecah menjadi tiga nama berikut dan dipakai secara ketatassert(bool)atau, dalam Rust,assert_unchecked()adalah sesuatu yang diyakini programmer selalu benar, dan compiler juga menganggapnya selalu benar untuk keperluan optimisasi. Untuk menghindari asosiasi dengan assert pemeriksaan dari bahasa lama, mungkin lebih baik menyebutnyaassume()check(bool)akan panic jika kondisi salah dan lanjut jika benar, dan selalu berperilaku demikiandebug_check(bool)sama dengancheck()di mode debug, dan di mode rilis selalu lanjut. Dalam praktiknya, ini dikendalikan oleh flag--debug_checksyang aktif secara default di mode debugSelain itu juga dibutuhkan flag compiler
--check_assertsyang mengubahassert()menjadicheck(). Ini dipakai saat ingin memverifikasiassertmilik sendiri karena terasa mencurigakan, dan di mode debug aktif secara default. Jika saat mengatakan “assert” tidak benar-benar jelas yang dimaksud adalah yang mana, diskusi yang matang tidak mungkin terjadi dan yang terbuang hanya kata-kata