Inti dari Rust
(jyn.dev)- Rust adalah bahasa dengan berbagai konsep yang saling terkait erat, sehingga untuk memahami program dasar sekalipun, banyak elemen harus dipelajari secara bersamaan
- Fungsi, generik, enum, pattern matching, trait, referensi, ownership,
Send/Sync,Iterator, dan lainnya semuanya merupakan elemen inti yang dirancang untuk saling berinteraksi - Dibandingkan dengan JavaScript, di JS kita masih bisa menulis kode hanya dengan memahami beberapa konsep, tetapi di Rust kita baru bisa menulis kode yang bermakna setelah memahami konteks bahasa secara keseluruhan
- Kompleksitas Rust seperti ini memang meningkatkan hambatan belajar, tetapi sekaligus memberikan keamanan dan konsistensi, serta sangat memengaruhi cara perancangan kode
- Kerapian struktur bahasa seperti ini membuat Rust istimewa, dan visi tentang “Rust yang lebih kecil” mengajak kita kembali melihat filosofi bahasa yang dipadukan secara presisi
Sulitnya mempelajari Rust
- Meski Rust memiliki hambatan masuk yang tinggi, banyak orang telah berkontribusi pada dokumentasi, API, dan peningkatan diagnostik
- Konsep dasarnya mencakup fungsi sebagai first-class object, enum, pattern matching, generik, trait, referensi, borrow checker, keamanan konkurensi, dan iterator
- Konsep-konsep ini saling bergantung dan saling terkait, sehingga sulit dipelajari satu per satu secara terpisah, dan sebagian besar pustaka standar juga memanfaatkan fitur-fitur ini
- Untuk memahami bahkan hanya sekitar 20 baris kode Rust, kita perlu menangkap banyak elemen sekaligus seperti paradigma fungsional,
Resultdan penanganan error, tipe generik, enum, iterator, dan lainnya
Perbandingan Rust dan JavaScript
- Saat program pendeteksi perubahan file yang sama ditulis dalam Rust dan JS, Rust memperlihatkan banyak konsep bahasa yang saling terjalin
- Di JS, pada dasarnya cukup memahami fungsi dan penanganan null untuk menulis kode yang bisa berjalan
- Ini bukan berarti Rust sekadar lebih sulit, melainkan menunjukkan bahwa Rust adalah desain yang menuntut pemahaman struktural atas keseluruhan bahasa
Desain Rust yang saling terhubung
- Inti Rust adalah gabungan fitur-fitur yang dirancang secara organik
- Enum terasa tidak praktis tanpa pattern matching, dan pattern matching juga terbatas tanpa enum
ResultdanIteratortidak mungkin diimplementasikan tanpa generik- Konsep
Send/Syncdan batasanprintlnhanya bisa diekspresikan dengan aman jika ada trait - Borrow checker menjamin keamanan
Send/Syncmelalui analisis capture pada closure
- Keterkaitan seperti ini membuat Rust bukan sekadar kumpulan fitur, melainkan sebuah sistem bahasa yang terintegrasi
Visi Rust yang lebih kecil
- Pada 2019, without.boats menyebut “Smaller Rust” saat membahas kemungkinan Rust yang lebih kecil dan lebih halus
- Kini Rust sudah jauh lebih besar, tetapi gagasan tentang Rust yang lebih kecil mengingatkan kita pada hakikat desain bahasa yang saling bertaut secara presisi
- Daya tarik Rust terletak pada fakta bahwa elemen-elemennya, saat berdiri sendiri maupun saat digabungkan, memberikan daya ekspresi dan keamanan yang kuat
Kesimpulan
- Rust memang sulit dipelajari, tetapi konsistensi dan integrasi dari konsep-konsep yang saling terkait menjadi kekuatan besarnya
- Berkat struktur ini, Rust membuat pengembang bukan sekadar menulis kode, tetapi juga membangun cara berpikir yang mempertimbangkan keamanan dan performa sekaligus
- Hakikat Rust ada pada “bahasa inti yang kecil dan dirancang dengan presisi”, dan ini tetap menjadi filosofi penting bahkan pada Rust yang telah berkembang saat ini
1 komentar
Opini Hacker News
fs.watchsecara eksplisit menyebutkan bahwafilenamedi callback bisa bernilainulldan wajib diperiksa. Di Rust, fakta ini akan tercermin di sistem tipe sehingga pasti dipaksa untuk ditangani, tetapi di JS orang mudah menulis kode secara serampangan. Dokumentasi terkaitnullakan dipaksakan. Jadi ini menurut saya contoh yang bagus bahwa TS adalah langkah yang relatif ringan di JS untuk lebih mendekati correctness ala Rustfor path in pathsseharusnyafor (const path of paths). Memang JS akan langsung error kalau tanpa tanda kurung, tetapi perbedaanindanofadalah bahwainmengiterasi indeks iterable, bukan nilainya, sehingga indeks itu akhirnya dikonversi menjadi string dan masuk sebagai argumen pertamafs.watch. Bahkan TypeScript pun mungkin tidak menangkap kesalahan inikinditu datang dari mana. Diconsole.log("${kind} ${filename}"), seharusnyaeventTypeyang berupa string, bukankindprintlndi Rust hanya bisa mencetak tipe yang mengimplementasikan traitDisplayatauDebug. JadiPathtidak bisa dicetak secara langsung. Tidak semua OS menyimpan path yang sesuai UTF-8, sedangkan semua tipe string Rust berbasis UTF-8. Artinya, menampilkanPathbisa menimbulkan kehilangan informasi.Pathmengembalikan tipe yang mengimplementasikanDisplaymelalui metodedisplay. Rust memasukkan ini ke sistem tipe, tetapi di JS/TS sulit mengekspresikan bahwa string internalnya UTF-16, dan path non-Unicode harus ditangani langsung denganTextEncoder/TextDecoderagar benar. Dari pengalaman saya dulu, ketika server mengirim teks dalam Shift_JIS lalu dibaca denganresponse.text(), runtime hanya menghasilkan string kosong. Jika tidak terbiasa dengan isu encoding, Anda bisa menghabiskan berhari-hari untuk debugging situasi seperti ini. Selain itu, contoh JS tersebut punya bug dan kesalahan sintaks yang tidak ada di kode Rust (di loop harusfor-of, bukanfor-in). Saya juga tidak merasa contoh ini sekadar memakai "fungsi kelas satu"; Anda tetap perlu memahami iterator seperti di Rust, dan ia memakai CommonJS. Selain itu adaasync/await, Promises, dan top-level await yang juga harus dipelajari, dan top-level await sendiri baru belakangan didukung di sebagian runtime termasuk node. Masih ada engine JS tertentu (misalnya Hermes milik React Native) yang belum mendukungnyaItulah alasan saya terus memakai Rust. Contoh ini hanya satu kasus, tetapi masalah-masalah kecil dan jebakan seperti ini selalu tersebar di bahasa lain. Mungkin tiap masalah tidak selalu muncul sendiri-sendiri, tetapi jika dikumpulkan sepanjang siklus hidup program, bug aneh akan terus muncul entah dari mana dan harus terus dicari. Di Rust hal seperti ini tidak terjadi. Sistem tipenya memblokir sangat banyak kasus absurd sejak awal. Dalam praktiknya, setelah saya merilis software yang fiturnya sudah lengkap dengan Rust, saya hanya sesekali menambah fitur, dan pekerjaan debugging bug umum nyaris hilang. Tentu bug logika tetap bisa terjadi di mana saja, tetapi Rust menutup sumber masalah dari ketidakcocokan tipe/struktur yang konyol seperti di bahasa lain, sehingga produktivitas dan maintainability terasa sangat berbeda
Secara pribadi saya merasa banyak developer JS/TS yang tidak benar-benar paham thenable/Promise dan async-await. Saya juga pernah melihat ini:
Wrapper berbentuk callback dibungkus lagi sebagai Promise lalu dipakai kembali di dalam fungsi async. Setiap kali melihat ini rasanya pedih sekali. Dan saya benar-benar melihat kode seperti ini di banyak tempat. Belum lagi kalau memikirkan import modul dan
async import(), transpile, code splitting, dan seterusnya, semuanya benar-benar kompleksrustfmt,rust-analyzer, perbaikan bug dirustc, serta peningkatan pelaporan error di Cargo. Saya sendiri setiap hari menulis skrip reproduksi issue dengan cargo script-Zscriptlalu terdistraksi saat riset. Ini sudah berjalan sejak 2023, dan ada issue terbuka yang tampak hampir selesai. Saya juga melihat di repositori ZomboDB bahwa mereka menangani pipeline build dengan rust, meski saya tidak sepenuhnya memahami konteks keseluruhannya. Saya juga ingin menekankan betapa bergunanya frontmatter cargo untuk portabilitas skrip. Cukup berbagi satu file saja, lalu dependensi bisa langsung diambil dan dipakai tanpa instalasi/inisialisasi tambahan seperti pada Python atau Node.js#!/some/pathtinggal dijalankan oleh shell dengan menyerahkan seluruh file ke perintah yang ditentukan lewat stdinasyncdanconst. Kalau begitu, mestinya bisa langsung dikatakan bahwa "Rust sebelumasyncdanconstmasuk itu lebih kecil dan lebih bersih", tetapi tulisan aslinya tidak menjelaskannya secara seterang itu, yang menurut saya agak disayangkanCopytrait, reborrowing, deref coercion,into_iterotomatis di loop, pemanggilandropotomatis saat scope berakhir (ini pun bisa dipanggil langsung atau dijadikan error oleh compiler), default:Sizeddi trait bound, lifetime elision, match ergonomic, dan berbagai otomatisasi/kemudahan lain, kita bisa memperoleh Rust yang benar-benar lebih sederhana secara mekanis. Tetapi bahasa seperti itu akan sangat tidak nyaman dipakai sehari-hari. Ironisnya, elemen-elemen tadi justru dirancang untuk pemulaasyncdanconstitu lebih kecil dan lebih bersih. Alasan saya tidak mengatakannya secara terlalu gamblang adalah karena saya punya banyak teman yang mengembangkan fitur-fitur tersebut. Matklad mengungkapkannya dengan sangat baik di lobste.rs. Rust 2015 lebih terasa selesai dan lebih konsisten, tetapi visi Rust bukanlah koherensi sempurna, melainkan menjadi bahasa yang berguna di industri.into()dan traitFrommenangani konversi tipe dengan cara yang terlalu tersembunyi. Bahkan di standard library pun ada banyak fungsi "kenyamanan" semacam ini. Akibatnya tipe objek jadi ambigu, dan sulit melacak hubungan antara pemanggilan fungsi dan implementasinya (meski IDE memang sedikit membantu)pgrxbenar-benar luar biasa). Tetap saja, apa pun masih lebih baik daripada bekerja dengan Cconst, yang dipelajari di Rust, mungkin justru mengurangi beban untuk menghapus kebiasaan keliru yang dipelajari dari bahasa-bahasa lama di kemudian harimem, jadi untuk benar-benar memahami struktur interface, sebaiknya mulai dari std::mem