1 poin oleh GN⁺ 3 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Peralihan dari Go ke Rust lebih mirip pilihan untuk memindahkan masalah nil, penanganan error, data race, dan masa hidup resource ke jaminan waktu kompilasi daripada sekadar mengejar peningkatan kecepatan
  • Go unggul dalam kompilasi cepat, goroutine yang sederhana, dan ekosistem backend yang kuat, tetapi Rust mencegah lebih banyak kesalahan lewat sistem tipe dengan Option, Result, dan Send/Sync
  • Borrow checker Rust serta async/await menghadirkan kurva belajar dan biaya kegunaan, dan waktu kompilasinya juga harus diterima sebagai kemunduran yang jelas dibanding Go
  • Untuk migrasi, strategi yang lebih cocok adalah memulai dari komponen dengan batas yang jelas seperti layanan hot path, worker, atau sebagian endpoint di balik gateway, alih-alih menulis ulang semuanya
  • Manfaat yang diharapkan dapat diringkas sebagai penurunan CPU 20–60%, penurunan memori 30–50%, latensi P99 yang lebih rata, serta berkurangnya gangguan akibat dereferensi nil dan race condition

Fokus transisi

  • Peralihan dari Go ke Rust lebih dekat pada pembahasan soal jaminan ketepatan, kompromi runtime, dan perbedaan pengalaman pengembang daripada sekadar “apakah Rust lebih cepat”
  • Pusat perbandingan adalah layanan backend, dengan acuan pada keunggulan Go dalam binary statis kecil, standard library yang berfokus pada jaringan, serta ekosistem HTTP server, gRPC, dan database
  • Sebagian isi juga bisa berlaku untuk alat CLI, firmware embedded, dan game engine, tetapi itu bukan target yang dioptimalkan
  • Sebagai materi latar terkait, disajikan “Go vs Rust? Choose Go.” dari 2017 dan “Rust vs Go: A Hands-On Comparison” dari tim Shuttle
  • Go adalah bahasa yang sukses, tetapi pilihan desain seperti penggunaan nil yang luas, penanganan error yang bergantung pada disiplin alih-alih tipe, serta generics yang lama tidak hadir menjadi titik isu utama saat dibandingkan dengan Rust
  • Dalam JetBrains Developer Ecosystem Survey, Go ditampilkan sebagai bahasa yang mempertahankan porsi 17–19% pengembang aktif, sementara Rust terus tumbuh tetapi masih berada di porsi yang lebih kecil

Rangkaian alat

  • Baik Go maupun Rust sama-sama memiliki rangkaian alat bawaan lengkap yang menyediakan build, test, format, lint, dan pengelolaan dependensi melalui antarmuka yang konsisten
  • cargo menyediakan padanan fungsi alat Go secara lebih luas sebagai alat utama
    • go.mod / go.sumCargo.toml / Cargo.lock: konfigurasi proyek dan manifes dependensi
    • go get / go mod tidycargo add / cargo update: penambahan dan resolusi dependensi
    • go buildcargo build: kompilasi
    • go run .cargo run: build lalu jalankan
    • go test ./...cargo test: pengujian
    • go vet ./...cargo clippy: linter, dan Clippy jauh lebih beropini daripada vet
    • gofmt / goimportscargo fmt: formatter otomatis tanpa konfigurasi
    • golangci-lint runcargo clippy -- -D warnings: mode lint yang ketat
    • go doccargo doc --open: membuat dan membuka dokumentasi API
    • pprofcargo flamegraph / samply: profiling CPU
    • govulncheckcargo audit: pemeriksaan kerentanan berbasis database advisory
  • Di Go, celah sering ditutup dengan alat pihak ketiga seperti golangci-lint, mockgen, air, dan goreleaser, tetapi di Rust, ekosistem utama mencakup lebih banyak fungsi sebagai bawaan
  • Bahkan saat memerlukan crate eksternal, alat seperti cargo watch dan cargo nextest dapat dipasang hanya dengan sekali cargo install cargo-nextest dan kemudian bekerja seperti alat native lewat cargo nextest
  • Keunggulan gofmt dan rustfmt lebih besar pada menghilangkan perdebatan gaya saat code review daripada soal preferensi detail gaya
    • Kutipan Rob Pike dari Go Proverbs: “Gofmt’s style is no one’s favorite, yet gofmt is everyone’s favorite.”

Perbedaan inti Go dan Rust

  • Kedua bahasa sama-sama menawarkan bahasa terkompilasi, tipe statis, distribusi single binary, dan model konkurensi yang kuat, tetapi perbedaannya terletak pada cakupan jaminan compiler dan tingkat kendali atas perilaku runtime
  • Poin perbandingan utamanya adalah sebagai berikut
    • Rilis stabil: Go pada 2012, Rust pada 2015
    • Sistem tipe: Go bertipe statis dan struktural serta mendukung generics sejak 1.18, Rust bertipe statis dan nominal serta mendukung generics, trait, dan lifetime
    • Manajemen memori: Go memakai garbage collection konkuren berlatensi rendah, Rust berbasis ownership dan borrowing tanpa GC
    • Keamanan null: Go memiliki nil yang tersebar luas, Rust tidak memiliki null dan Option<T> menjadi pengganti di level tipe
    • Penanganan error: Go memakai interface error dan if err != nil { ... }, Rust memakai Result<T, E>, operator ?, dan pattern matching penuh
    • Konkurensi: Go berbasis CSP dengan goroutine dan channel, Rust memakai async/await di atas tokio, channel, dan thread
    • Pembatalan: Go mengandalkan context.Context berbasis konvensi, Rust memakai penyampaian yang eksplisit dan diperiksa tipe seperti CancellationToken
    • Data race: Go mendeteksi secara probabilistik saat runtime dengan -race, Rust mendeteksi saat waktu kompilasi dengan Send/Sync
    • Waktu kompilasi: Go sangat cepat, Rust lambat terutama pada clean build
    • Runtime: Go memiliki runtime sekitar 2MB dan GC, Rust tidak memiliki runtime selain libc atau dapat dibangun sepenuhnya statis dengan MUSL
    • Skala ekosistem: Go sekitar 750 ribu+ modul, Rust 250 ribu+ crate
  • Pemeriksaan seperti penanganan nil, propagasi error, data race, masa hidup resource, pembatalan, dan generics yang di Go bergantung pada konvensi, alat, dan deteksi runtime, di Rust dipindahkan ke dalam sistem tipe
  • Mutex<T> di Rust membuat nilai internal hanya bisa diakses melalui guard yang diperoleh lewat .lock(), sehingga jalur “lupa melakukan lock” dihapus langsung dari level tipe
  • Pola yang sama berulang di Option, Result, &mut T, Send/Sync, dan guard RAII; setelah terbiasa, compiler akan menggantikan banyak pemeriksaan mental yang biasanya dilakukan sendiri

Keterbatasan Go yang Membuat Rust Layak Dipertimbangkan

  • Karena Go sudah cukup cepat untuk sebagian besar beban kerja backend, alasan utama meninjau Rust lebih dekat bukanlah kecepatan melainkan lebih kepada verbositas penanganan error, risiko pointer nil, dan kurangnya fitur sistem tipe yang canggih seperti enum dan trait
  • Interface Go bukan pengganti yang memadai untuk trait Rust, dan karena tidak ada tipe Set di standard library, diperlukan solusi idiomatik seperti map[T]struct{}
  • Panic nil di production

    • Layanan Go bisa berjalan normal selama berbulan-bulan lalu memunculkan panic goroutine pada jalur kode tertentu karena pemeriksaan pointer nil terlewat
    • Dalam contoh, Find mengembalikan (*User, error) dan pada kasus “not found” nilai error adalah nil, tetapi pemeriksaan user tetap menjadi tanggung jawab pemanggil
    • user.Account.Notify() dapat crash jika user atau Account bernilai nil
    • Linter dan pemeriksaan IDE seperti nilaway dan staticcheck memang menangkap sebagian kasus, tetapi bersifat opt-in, probabilistik, dan tidak andal melintasi batas package
    • Option<T> di Rust menghilangkan kategori gangguan ini karena dereferensi tidak bisa dilakukan tanpa menangani kasus None
  • Data race yang tidak tertangkap -race

    • go test -race adalah alat yang sangat bagus, tetapi karena ini detektor runtime, ia hanya menemukan race yang benar-benar terjadi saat pengujian
    • Di Go, kode dengan dua goroutine yang memodifikasi map tanpa lock tetap bisa dikompilasi, lalu meledak saat production berada di bawah beban
    • Di Rust, berbagi state yang bisa diubah antar-thread memerlukan tipe yang mengimplementasikan Send dan Sync, dan upaya membagikan HashMap biasa antar-thread tidak akan lolos kompilasi
    • Ini memaksa penggunaan salah satu dari Arc<Mutex<...>>, Arc<RwLock<...>>, atau channel, sehingga race condition menjadi error tipe
    • Paul Dix secara langsung menyebut eliminasi data race sebagai motivasi penulisan ulang InfluxDB 3.0
      • “[The main benefit is] fearless concurrency — eliminating data races essentially, which we had before. Really gnarly bugs in version 1 of Influx due to that.”
      • Sumber: Paul Dix, Founder & CTO, InfluxData, Rust in Production
  • Penanganan error yang dapat dikomposisikan

    • if err != nil { return err } di Go dapat mengaburkan logika utama fungsi, dan membungkus konteks dengan fmt.Errorf("doing X: %w", err) bergantung pada disiplin, bukan aturan yang ditegakkan compiler
    • Dalam thread Lobste.rs, para developer Go berpengalaman membantah bahwa errcheck dan golangci-lint sudah menangkap sebagian besar kelalaian penanganan error, dan bahwa if err != nil yang eksplisit lebih mudah dibaca daripada rantai ? yang padat
    • Peter Bourgon memposisikan penanganan error eksplisit di Go sebagai nilai budaya yang memang disengaja
      • “I think that error handling should be explicit, this should be a core value of the language.”
      • Sumber: Peter Bourgon, GoTime #91, dikutip dalam Zen of Go oleh Dave Cheney
    • Result<T, E> di Rust adalah bagian dari signature tipe itu sendiri sehingga tidak bisa terlupakan, dan melalui enum yang didefinisikan dengan thiserror::Error serta #[from], kita mendapatkan konversi error dan pemeriksaan kelengkapan
    • Saat menambahkan variant error baru, compiler akan memberi tahu lokasi match yang perlu diperbarui
  • Generic tanpa boxing

    • Generic Go 1.18 berguna, tetapi memiliki keterbatasan seperti tidak adanya method dengan type parameter, GC shape stenciling, dan kadang karakteristik performa yang mengejutkan
    • Generic Rust dimonomorfisasi sehingga setiap instansiasi menghasilkan kode yang terspesialisasi tanpa biaya runtime
    • Jika digabungkan dengan trait, ini memungkinkan abstraksi tanpa biaya
    • Ini lebih penting pada infrastruktur bersama seperti middleware, generic repository, decoder, dan parser daripada pada kode handler, dan di area seperti ini Go sering kembali ke interface{}/any dan type assertion
  • Latensi yang dapat diprediksi

    • GC Go sangat baik, konkuren, berlatensi rendah, dan dituning dengan baik untuk beban kerja layanan umum, tetapi “low-pause” bukan berarti “no-pause”
    • Dalam situasi dengan alokasi tinggi, ekor latensi P99 bisa lebih buruk dibanding implementasi Rust yang tidak melakukan alokasi di hot path
    • Pada sistem yang sensitif terhadap latensi seperti trading, real-time bidding, network proxy, dan ingestion berthroughput tinggi, tidak adanya GC pause adalah keunggulan nyata
    • Stephen Blum mengatakan Rust diperlukan untuk mendapatkan kapasitas performa per dolar yang dibutuhkan pada skala PubNub
      • “Go is great at our scale, but we really need something that is going to give us the price-per-dollar performance capacity that we need, and Rust is going to get us there. That’s why basically everything is heading towards Rust these days.”
      • Sumber: Stephen Blum, CTO, PubNub, Rust in Production

Padanan Rust untuk pola Go

  • Cara tercepat untuk terbiasa dengan Rust adalah memetakan pola Go yang sudah Anda kenal ke pola padanannya di Rust
  • Contoh yang lebih panjang yang mengimplementasikan layanan backend yang sama dalam kedua bahasa tersedia di Shuttle comparison
  • Penanganan error: if err != nil vs Result<T, E>

    • Go mengembalikan error yang dibungkus dengan konteks setelah os.ReadFile(path) dan json.Unmarshal dengan if err != nil
    • Rust disusun dengan fs::read_to_string(path)?, serde_json::from_str(&data)?, dan Ok(cfg)
    • Operator ? menggantikan pola if err != nil { return err }, dan bahkan menangani konversi tipe jika From<E1> for E2 diimplementasikan
    • #[from] dari thiserror mendukung konversi ini secara idiomatis
  • Null: nil vs Option<T>

    • GetUser(id string) *User di Go mengembalikan nil jika pengguna tidak ditemukan, dan jika pemanggil menjalankan fmt.Println(u.Name), akan terjadi panic saat nil
    • get_user(id: &str) -> Option<User> di Rust mengembalikan Some(User) atau None
    • let user = get_user("123"); println!("{}", user.name); akan menjadi error kompilasi karena user bukan User, melainkan Option<User>
    • Anda harus menangani Some(u) dan None sekaligus dengan match get_user("123")
    • Dalam Rust yang aman, tidak ada nil, dan referensi tidak bisa bernilai null
  • Interface vs trait

    • Interface Go bersifat struktural, dan tipe secara implisit memenuhi interface
    • Trait Rust bersifat nominal, dan harus diimplementasikan secara eksplisit
    • Pendekatan Go bagus untuk duck typing spontan, sedangkan pendekatan Rust bagus untuk refactoring dan discoverability, serta Anda bisa menemukan implementasi trait tertentu dengan grep
    • Generic function dengan trait bound seperti fn handle<R: Reader>(r: R) mencakup sebagian besar kasus, dan tidak ada dispatch runtime berkat monomorfisasi
    • Untuk menyimpan implementasi heterogen yang memerlukan dispatch runtime, gunakan Box<dyn Trait> atau Arc<dyn Trait>
  • Goroutine vs async task

    • Model konkurensi Go sederhana seperti go doWork(ctx, input), goroutine ringan, dan runtime menjadwalkannya di atas thread OS
    • Keunggulan besar Go adalah tidak ada perbedaan sintaks antara kode sekuensial dan kode paralel
    • Rust hampir selalu menggunakan async/await di atas executor tokio untuk layanan backend
    • Fungsi async mengembalikan Future, dan tidak berjalan sampai di-await atau di-spawn
    • Kompiler melacak Send/Sync sebelum dan sesudah titik .await, dan akan menghasilkan error kompilasi jika nilai non-Send dipertahankan melewati await
    • Karena tidak ada preemption bawaan ala goroutine, menjalankan pekerjaan CPU-bound terlalu lama di dalam async task bisa membuat executor kelaparan, sehingga perlu dipindahkan ke tokio::task::spawn_blocking atau rayon
  • context.Context vs CancellationToken

    • Di Go, context.Context diteruskan ke setiap blocking call
    • Rust tidak memiliki context.Context bawaan, dan padanan terdekat untuk pembatalan adalah tokio_util::sync::CancellationToken
    • Timeout membungkus future dengan tokio::time::timeout(dur, fut)
    • Deadline dan value sering diteruskan lewat argumen eksplisit atau span tracing, bukan satu objek context tunggal
    • Kutipan Dave Cheney dari The Zen of Go:
      • “Go doesn’t have a way to tell a goroutine to exit. There is no stop or kill function, for good reason. If we cannot command a goroutine to stop, we must instead ask it, politely.”
    • Di Go, “permintaan yang sopan” ini secara konvensional disampaikan melalui context.Context, sedangkan di Rust melalui CancellationToken atau kanal watch, tetapi kompiler dapat memberi tahu jika ada yang terlewat
  • String: string vs String dan &str

    • string di Go adalah byte slice UTF-8, dan saat di-assign, header disalin sementara byte dasarnya dibagikan dalam struktur immutable
    • Rust membaginya menjadi dua tipe
      • String: dimiliki, dialokasikan di heap, dan bisa bertambah besar
      • &str: borrowed view ke data string lain, dan dalam banyak kasus merupakan padanan untuk parameter string di Go
    • Aturan praktisnya adalah menerima &str untuk argumen, dan mengembalikan String saat membuat data baru
    • Pemisahan antara &str dan String menunjukkan secara ringkas model “borrow vs own” di Rust

Evaluasi terhadap generik di Go

  • Go memperkenalkan generik pada versi 1.18 pada Maret 2022, yaitu 13 tahun setelah bahasa ini dirilis
  • Generik memang berguna, tetapi dinilai tidak sepenuhnya memberikan keunggulan yang diharapkan dari Rust, Haskell, atau C++ modern, sambil tetap membawa cukup banyak kekurangan dari sistem tipe generik
  • Hampir tidak dipakai di pustaka standar

    • Bahkan 3 tahun setelah generik diperkenalkan, pustaka standar Go sebagian besar masih menghindari generik
    • sort.Slice masih menerima closure func(i, j int) bool alih-alih constraint cmp.Ordered
    • sync.Map masih diketik sebagai any/any
    • Helper generik yang ada hanya terdapat di sedikit paket, seperti beberapa item di bawah slices, maps, cmp, dan sync
    • Janji kompatibilitas Go 1 memang sedikit menjelaskan mengapa API non-generik lama sulit dirombak, tetapi Go tetap tidak memakai generik sebagai alat utama seperti Rust
    • Di Rust, sejak awal generik sudah meresap ke Option<T>, Result<T, E>, Vec<T>, HashMap<K, V>, Iterator, From/Into, serta semua koleksi dan smart pointer
  • Tidak ada sistem trait, hanya constraint struktural

    • Generik Rust terikat dengan trait yang menangani polimorfisme ad-hoc, supertrait, associated type, blanket impl, dan coherence
    • Constraint di Go lebih mirip interface dengan tambahan operator ~ untuk membership type-set
    • Go tidak memiliki hierarki supertrait seperti trait Ord: Eq + PartialOrd di Rust, associated type seperti type Item; pada Iterator, maupun blanket impl seperti impl<T: Display> ToString for T
    • Di Go, metode dengan parameter tipe tidak bisa digunakan, sehingga bentuk seperti func (s Set[T]) Map[U](<https://corrode.dev/learn/migration-guides/go-to-rust/f func(T>) U) Set[U] tidak memungkinkan
    • Saat abstraksi melampaui “fungsi yang bekerja pada sembarang T dengan beberapa operasi”, Go kembali ke any, type assertion, code generation, dan reflection runtime
  • Perbedaan inferensi tipe dan strategi implementasi

    • Rust menyebarkan informasi tipe ke seluruh expression, termasuk closure, rantai iterator, dan operator ?
    • Inferensi di Go lebih dangkal; biasanya ia menyimpulkan parameter tipe dari argumen fungsi, tetapi tidak bisa melakukan inferensi dari konteks posisi return, sehingga sering membutuhkan type argument eksplisit di titik pemanggilan
    • Go memilih jalan tengah berupa GCShape stenciling and dictionaries untuk menjaga waktu kompilasi tetap cepat, tetapi pemanggilan metode pada type parameter bisa menambahkan indirection
    • Sebagai rujukan untuk hal ini, diajukan artikel PlanetScale
    • Rust membuat kode mesin yang dispesialisasi untuk Vec<i32> dan Vec<String> masing-masing, tanpa dispatch runtime
    • Biaya dari monomorfisasi adalah waktu kompilasi; kedua bahasa ini mengoptimalkan tujuan yang berbeda
  • Tidak menutup lubang pada sistem tipe

    • Di Rust, generik dan trait menghilangkan sebagian besar situasi yang sebaliknya memerlukan Box<dyn Any> atau reflection runtime
    • Generik Go tidak menghilangkan any, reflect, maupun pola code generation yang dominan pada ORM, decoder, dan mock
    • encoding/json masih menggunakan reflection, database/sql masih menggunakan any, dan mockgen masih menghasilkan kode
    • Generik di Go terasa seperti alat baru yang berguna untuk kasus sempit, sedangkan generik di Rust berfungsi seperti fondasi yang jika dihilangkan akan membuat bahasanya runtuh

Ekosistem backend Rust

  • Ekosistem Rust juga sudah cukup terkonsolidasi pada “pilihan default” untuk layanan backend umum
  • Pemetaan yang mewakili:
    • HTTP server: Go net/http, chi, gin, echo, fiber → Rust axum on hyper
    • HTTP client: Go net/http, resty → Rust reqwest
    • gRPC: Go google.golang.org/grpc + protoc-gen-go → Rust tonic + prost
    • SQL: Go database/sql, sqlc, sqlx, gorm → Rust sqlx, sea-orm, diesel
    • Migrations: Go golang-migrate, goose → Rust sqlx migrate, refinery
    • JSON: Go encoding/json, sonic, goccy/go-json → Rust serde + serde_json
    • Logging: Go log/slog, zerolog, zap → Rust tracing + tracing-subscriber
    • Metrics: Go prometheus/client_golang → Rust metrics + metrics-exporter-prometheus
    • Config: Go viper, koanf → Rust config / config-rs, figment
    • CLI: Go cobra, urfave/cli → Rust clap derive
    • Errors: Go errors, pkg/errors → Rust thiserror untuk library, anyhow untuk biner
    • Testing: Go testing, testify, gomega → Rust bawaan #[test], rstest, assert_matches
    • Mocking: Go mockgen, moq → Di Rust, fake yang ditulis manual bersifat idiomatis, dan mockall juga digunakan
    • Background tasks: Go goroutine + errgroup → Rust tokio::spawn + JoinSet
  • Untuk layanan backend yang tipikal, kombinasi axum + sqlx + tokio + tracing + serde + clap disebut mencakup 90% dari kebutuhan

Borrow checker dan kurva belajar

  • Saat berpindah dari Go ke Rust, harus berasumsi bahwa Anda akan menabrak tembok
  • Runtime Go menangani memori dan aliasing, tetapi Rust memindahkan keputusan itu ke sistem tipe, sehingga pada beberapa minggu pertama kode yang “seharusnya jelas bisa jalan” bisa ditolak oleh compiler
  • Pola yang sering dihadapi developer Go:
    • Referensi berumur panjang: di Go, menyimpan *User yang diambil dari map untuk waktu lama terasa wajar, tetapi di Rust perubahan pada map akan diblokir selama borrow itu masih hidup
    • Struct self-referential: di Go, data dan iterator di atas data itu bisa disimpan dalam struct yang sama, tetapi di Rust diperlukan Pin, ouroboros, atau desain ulang
    • Berbagi state mutable antar goroutine: pola mu sync.Mutex; data map[K]V di Go menjadi bentuk Arc<Mutex<HashMap<K, V>>> di Rust
    • Mengembalikan referensi dari fungsi: anotasi lifetime mulai muncul, dan ini adalah konsep baru bagi developer Go
  • Borrow checker harus dipandang bukan sebagai “penjaga gerbang” yang menghalangi, melainkan sebagai mekanisme yang mengungkap bug yang benar-benar ada
  • Ia menyaring sejak waktu kompilasi kasus ketika nilai dipakai lagi setelah dipindahkan, beberapa thread menyentuh data yang sama secara bersamaan, pointer null atau dangling pointer didereferensikan, atau referensi hidup lebih lama daripada nilainya
  • Setelah konsep borrow terinternalisasi, ia berubah dari lawan menjadi rekan kerja, dan developer Rust yang sudah mahir biasanya mengatakan bahwa dalam 4–12 minggu borrow checker mulai terasa sebagai alat bantu
  • CTO PubNub Stephen Blum mengatakan di Rustacean Station bahwa bulan pertamanya “terasa seperti saat pertama kali belajar pemrograman”, karena ia dipaksa berurusan dengan borrow checker dan lifetime
  • Maintainer clap Ed Page mengatakan di Rustacean Station: clap with Ed Page bahwa borrow checker membantunya fokus pada masalah tingkat tinggi dan juga menangkap hal-hal yang luput dari analisisnya sendiri

Hambatan utama dalam transisi ke Rust

  • Waktu kompilasi

    • Waktu kompilasi Rust harus diterima sebagai kemunduran yang nyata dibanding Go; clean release build untuk layanan berukuran menengah bisa memakan waktu beberapa menit, berbeda dengan kompilasi Go yang nyaris seketika
    • Incremental build dan cargo check cukup masuk akal, dan waktu kompilasi juga membaik tiap tahun, tetapi selisihnya dengan Go tetap terasa
    • Dalam loop editing, gunakan cargo check, pisahkan ke workspace saat mulai memberi manfaat, dan simpan crate dengan banyak procedural macro sebagai crate terpisah agar hanya dikompilasi ulang saat berubah
    • Untuk detail lebih lanjut, lihat tips mengurangi waktu kompilasi Rust
  • Masalah async coloring

    • Pemisahan async fn / fn di Rust adalah salah satu kemunduran usability terbesar saat datang dari Go
    • async trait sudah stabil sejak Rust 1.75, tetapi masih ada sisi kasar saat dicampur dengan dynamic dispatch
    • Dalam beberapa situasi, crate async-trait akhirnya dipakai untuk menutupi bagian-bagian ini
  • Ekosistem yang lebih kecil

    • Ekosistem crate Rust terus tumbuh dan kualitas library secara umum tinggi, tetapi Go masih unggul di beberapa area yang dekat dengan backend
    • Area di mana Go lebih unggul mencakup operator Kubernetes, SDK cloud provider, dan driver database untuk storage niche tertentu
    • Sebelum memutuskan migrasi, luangkan sekitar satu hari untuk memeriksa apakah ada alternatif Rust yang layak untuk library yang Anda andalkan
    • Beberapa tim mungkin perlu memperbarui crate validasi skema XML yang terbengkalai atau menulis sendiri client untuk protokol yang kurang dikenal

Strategi integrasi

  • Transisi yang berhasil dari Go ke Rust lebih mirip pilihan taktis daripada rewrite total sekali jalan
  • Microsoft Principal Engineer Victor Ciura mengatakan di Rust in Production, “bukan menulis ulang semuanya ke Rust untuk bersenang-senang, tetapi membuat pilihan taktis untuk memakai Rust jika komponen baru lebih cocok dibangun dengan Rust”
  • 1. Memisahkan hot path menjadi layanan tersendiri

    • Jika layanan tertentu terus menimbulkan masalah, migrasi dengan risiko terendah adalah menulis ulang hanya layanan itu ke Rust sambil tetap menaruhnya di balik kontrak API yang sama
    • Targetnya bisa berupa layanan dengan penggunaan CPU tinggi, sensitif terhadap latensi, atau berulang kali menimbulkan masalah stabilitas
    • Layanan Go lainnya tetap berkomunikasi lewat HTTP/gRPC, jadi tidak perlu tahu bahasa implementasi internalnya
    • CTO Radar Jeff Kao mengatakan di Rust in Production bahwa tulisan Discord berpindah dari Go ke Rust membuat Radar memikirkan percobaan serupa
  • 2. Mengganti sidecar atau proses worker

    • Worker latar belakang, consumer queue, pipeline ingestion, dan pekerjaan batch yang CPU-bound adalah target awal yang bagus
    • Biasanya mereka punya batas input/output yang jelas seperti queue atau topic, dan tidak memiliki shared state in-process dengan bagian sistem lainnya
  • 3. cgo memungkinkan, tetapi menyakitkan

    • Anda bisa memanggil Rust dari Go lewat cgo, dan ada panduan yang bagus untuk menanganinya
    • Untuk layanan backend, ini umumnya tidak direkomendasikan
    • Kompleksitas build dan overhead FFI sering kali menghapus keuntungan dibanding pendekatan “mendirikan layanan Rust dan menaruhnya di balik network call”
    • Untuk library dan alat CLI, pendekatan ini bisa lebih praktis
  • 4. Menerapkan Strangler Pattern di balik gateway

    • Jika ada API gateway atau reverse proxy, Anda bisa merutekan hanya endpoint tertentu ke layanan Rust baru dan membiarkan sisanya tetap di Go
    • Ini sangat cocok ketika satu bounded context seperti autentikasi, pencarian, atau pembayaran pas dijadikan unit migrasi
    • Pola ini disebut “strangler fig” karena layanan baru tumbuh mengelilingi layanan lama dan pada akhirnya menggantikannya sepenuhnya

Tips migrasi praktis

  • Mulailah dari layanan dengan batas yang jelas, bukan layanan yang paling sentral dan paling sering dideploy
  • Pilih layanan yang kontraknya dengan sistem lain sudah terdefinisi dengan baik dan radius dampaknya kecil
  • Pertahankan kontrak API yang sama

    • Jika layanan Go mengekspos REST API, layanan Rust juga harus mempertahankan path yang sama, bentuk JSON yang sama, dan wrapper error yang sama
    • Bagi client, migrasi ini tidak terlihat, dan traffic bisa dialihkan secara bertahap melalui gateway
  • Jangan memindahkan idiom secara harfiah

    • if err != nil { return err } menjadi ?
    • Pola goroutine per request dipindahkan ke tokio::spawn hanya jika memang benar-benar perlu
    • axum sudah menangani request secara konkuren
    • Interface dengan satu metode biasanya menjadi trait bound pada generic, bukan Box<dyn Trait>
  • Gunakan compiler seperti pair programmer

    • Error compiler Rust umumnya berkualitas tinggi, dan jika dibaca pelan-pelan hampir selalu menunjukkan jawaban yang benar
    • Anggota tim yang paling lama kesulitan biasanya adalah mereka yang tidak melihat compiler sebagai kolaborator, melainkan lawan
  • Investasikan pada pelatihan di awal

    • Migrasi Rust sering tidak berakhir baik jika dikerjakan sambil “belajar di pinggir jalan”
    • Anda perlu benar-benar menyisihkan waktu belajar, seperti workshop, kursus online, dan sesi pair berdasarkan codebase nyata
    • Setelah tim menjadi mahir, investasi awal itu biasanya kembali berkali-kali lipat

Area yang Go tetap cocok

  • Tidak semua hal perlu dipindahkan ke Rust, dan ada area di mana Go sangat unggul
  • Alat native Kubernetes

    • Area operator, controller, dan CRD ekosistemnya sangat didominasi Go
  • Utilitas CLI dan alat pengembangan

    • Keunggulannya ada pada kompilasi cepat, cross-compilation yang mudah, dan deployment yang sederhana
  • Layanan glue

    • Pada lapisan API yang tipis, proxy, dan konverter format, rasio boilerplate Rust mungkin tidak sepadan dengan manfaatnya
  • Tempat ketika kecepatan tim lebih penting daripada jaminan akurasi absolut

    • Di area yang harus bergerak cepat, Go bisa tetap menjadi pilihan yang cocok
    • Canonical VP of Engineering Jon Seager mengatakan di Rust in Production bahwa Go adalah pilihan yang sangat baik untuk layanan jaringan, Canonical memiliki banyak Go, dan Juju juga merupakan codebase Go yang sangat besar
    • Strategi hibrida itu umum, dan banyak tim akhirnya menggunakan backend multibahasa: Go untuk layanan yang “membosankan”, dan Rust untuk layanan di mana stabilitas serta performa mampu menutup usaha tambahan yang dibutuhkan

Peningkatan yang bisa diharapkan

  • Angkanya sangat bervariasi tergantung workload, jadi ini sebaiknya dilihat sebagai panduan kasar, bukan janji
  • Kisaran peningkatan kasar yang diamati dalam migrasi dari Go ke Rust:
    • Penggunaan CPU: turun 20~60%
      • Karena Go sudah efisien, dampaknya tidak sedrastis saat berpindah dari Python ke Rust
      • Keuntungan datang dari tidak adanya GC dan loop yang lebih rapat
    • Memori: turun 30~50%
      • Terutama karena tidak ada overhead GC dan runtime yang lebih kecil
    • Latensi P99: jauh lebih konsisten
      • Layanan Rust cenderung lebih rata dengan jitter akibat GC yang lebih sedikit dibanding yang terlihat pada layanan Go
      • Setelah diperkenalkannya GC low-latency di Go, sisi Go juga banyak membaik, tetapi pada beban tinggi perbedaannya masih ada
    • Gangguan produksi: area peningkatan yang paling aktif dilaporkan tim
      • Jenis bug seperti data race, nil dereference, dan jalur error yang terlewat, yang bisa lolos go test -race lalu sampai ke produksi, tidak akan terkompilasi di Rust
      • Setelah migrasi ke Rust, giliran on-call biasanya menjadi sangat membosankan
  • InfluxData Staff Engineer Andrew Lamb mengatakan di Rustacean Station: Rebuilding InfluxDB with Rust bahwa setelah penulisan ulang InfluxDB, mereka tidak lagi perlu melacak crash, race condition multithread yang aneh, dan masalah yang sebelumnya sangat memakan waktu
  • Pindah dari Go ke Rust kecil kemungkinannya memberi peningkatan throughput 10x seperti saat berpindah dari Python ke Rust
  • Manfaat sebenarnya adalah berkurangnya “error konyol”, tail latensi yang lebih rata, dan kemampuan untuk meluas ke area lain seperti pengembangan embedded atau pemrograman sistem dengan bahasa yang sama

Catatan tambahan

  • Sistem tipe Rust tidak menghilangkan semua bug logika sinkronisasi, tetapi tipe yang tidak bisa dibagikan antarthread tanpa sinkronisasi tidak akan terkompilasi
  • Jenis masalah seperti “lupa memasang lock” yang bisa berujung pada korupsi data diam-diam dapat dicegah oleh sistem tipe Rust
  • Go string adalah urutan byte yang immutable dan secara konvensi UTF-8, tetapi itu tidak dijamin pada level tipe
  • Padanan terdekatnya adalah, dari sisi read-only view, Go string ↔ Rust &str, dan dari sisi buffer yang dapat diubah, Go []byte ↔ Rust Vec<u8>
  • Rust String adalah versi &str yang dimiliki dan dapat diperluas, dengan jaminan tambahan bahwa isinya adalah UTF-8 yang valid
  • Untuk detail lebih lanjut, lihat Strings, bytes, runes and characters in Go
  • Sejak Go 1.18, fungsi generik dan tipe generik dimungkinkan, tetapi parameter tipe pada method itu sendiri belum diperkenalkan
  • Rantai iterator seperti (0..100).filter(|n| ...).collect() di Rust mungkin terasa asing bagi pengembang Go, tetapi di Rust juga bisa memakai loop for, dan untuk kode sekali pakai itu sering kali merupakan pilihan yang tepat

Kesimpulan

  • Peralihan dari Go ke Rust berbeda dengan peralihan dari Python atau TypeScript ke Rust
  • Pengembang dengan latar belakang Go sudah memahami keunggulan static typing dan bahasa terkompilasi, jadi ini bukan peralihan yang mengorbankan dynamic typing atau runtime yang lambat
  • Pertukaran intinya adalah meninggalkan nil dan mendapatkan codebase yang lebih tangguh, lebih sedikit jebakan, serta compiler yang lebih ketat yang menangkap lebih banyak kesalahan saat waktu kompilasi
  • Sebagai gantinya, learning curve lebih curam
  • Pada layanan penting bagi bisnis yang menjadi sandaran organisasi, membutuhkan uptime tinggi, seperti layanan fondasional, pertukaran ini jelas bernilai
  • Untuk layanan lain, Go mungkin masih menjadi jawaban yang tepat
  • Tujuan migrasi adalah menempatkan setiap masalah pada bahasa yang paling baik menyelesaikannya

1 komentar

 
GN⁺ 3 jam lalu
Komentar Hacker News
  • Pindah dari C/C++ atau Python ke Rust bisa dipahami karena berbagai alasan, tetapi untuk backend web Go tampak sebagai pilihan yang sangat cocok
    Saya hampir hanya memakai Rust, tetapi terakhir kali mengerjakan sisi server web dengan Rust, saya merasa seharusnya memakai Go
    Tulisan aslinya menyoroti bahwa sintaks penanganan error di Go bertele-tele, dan itu benar. Rust juga dulu punya masalah yang sama lalu menambahkan sintaks ? untuk mengembalikan nilai error saat terjadi error. Penanganan error di Go pada dasarnya adalah bentuk yang ditulis panjang dari ini
    Di Rust tidak ada satu tipe error yang seragam; ada sistem error utama seperti io::Error, thiserror, dan anyhow, sehingga merepotkan saat meneruskannya ke atas sepanjang rantai pemanggilan
    Ada hal-hal yang jika tidak ada di bahasa baru akan sulit ditambahkan belakangan. Misalnya tipe konstanta, tipe boolean, tipe error, tipe array multidimensi, serta tipe vektor·matriks ukuran 2/3/4 dan operasi standarnya. Jika tidak distandardisasi sejak awal, akan banyak waktu terbuang untuk menyelaraskan berbagai representasi dari konsep yang sama
    Selain penanganan error, dampaknya mungkin lebih kecil untuk pengembangan web, tetapi pada komputasi numerik, grafis, dan pemodelan, kebutuhan menerapkan operasi standar pada array angka menjadi sumber penderitaan besar
    Keunggulan Go dalam layanan web ada dua. Pertama adalah goroutine seperti yang disebut tulisan asli, dan kedua adalah pustaka yang tidak terlalu banyak dibahas di tulisan itu. Go punya hampir semua pustaka yang dibutuhkan untuk layanan web, dan banyak di antaranya juga dipakai di internal Google sehingga sudah tahan terhadap kondisi yang sangat keras. Sebaliknya, crate Rust sering kurang matang dan sering tidak memiliki jaminan kualitas resmi

    • Menurut saya keunggulan terbesar Go dibanding Rust adalah kecepatan kompilasi
      Selain itu, Rust dibanding Go masih bergantung pada banyak pustaka C/C++, sehingga cross-compilation, build yang reproducible, dan pembuatan binary statis lebih mudah menjadi masalah
      Kekurangan Go adalah garbage collector-nya terlalu sederhana. Jika muncul lonjakan latensi, tidak banyak cara mengatasinya selain rewrite yang menyakitkan
    • Rust pada dasarnya punya satu jenis error, yaitu trait Error
      Yang disebut tadi hanyalah cara umum untuk memakainya, dan bahkan hanya memakai Box pun sama sekali tidak masalah. Itu pada dasarnya mirip dengan yang dilakukan anyhow::Error
    • Saya dulu cukup menyukai Go, tetapi setelah belakangan lebih sering memakai Swift dan Rust, compiler yang tidak mencegah dereferensi null pointer dan juga tidak menjamin keamanan konkurensi terasa agak prasejarah
      Meski begitu, di sisi standard library saya rasa Go jauh lebih baik daripada Rust
    • Setuju. Di awal saya langsung memperhatikan bagian yang mengatakan tulisan ini ditujukan untuk layanan backend
      Saya menyukai bahasa Rust dan memakainya untuk firmware embedded dan aplikasi PC, tetapi untuk backend web saya masih memakai Python. Alasannya, Rust tidak punya kumpulan alat setingkat Django atau Rails
      Ada yang mirip Flask, tetapi tidak ada ekosistem yang sekokoh Flask. Pengalaman saya dengan Go memang sedikit, tetapi untuk backend web saya kemungkinan akan memilih Go daripada Rust. Alasannya adalah ekosistem pustaka dan framework
      Selain itu, saya pada umumnya tidak terlalu suka Async Rust karena alasan-alasan yang biasa disebut. Ekosistem web Rust hampir semuanya nyaris mewajibkan penggunaan async
    • Rust bukan punya tiga sistem error, melainkan satu, yaitu trait Error
      io::Error hanya salah satu dari banyak tipe yang mengimplementasikannya dan tidak istimewa. Error yang didefinisikan dengan thiserror juga mengimplementasikan trait ini
      anyhow hanya memudahkan Anda mengatakan “semacam Error” ketika tidak ingin menuliskan secara rinci tipe error yang mungkin dikeluarkan fungsi sebagai kontrak API
  • Rust lebih mudah membuat kode yang deterministik daripada Go, jadi sangat berguna saat membutuhkan pengujian simulasi deterministik dan property-based testing
    Saya baru-baru ini menulis alat mirroring data Postgres-to-Iceberg dengan Go, https://github.com/polynya-dev/pg2iceberg, tetapi kemudian mem-porting-nya ke Rust karena saya ingin melakukan pengujian simulasi deterministik tanpa harus bertarung dengan runtime Go
    Namun, jika domain tersebut tidak cukup penting untuk membenarkan pengujian seperti itu, saya akan selalu memilih Go daripada Rust
    Tulisan terkait: https://www.polarsignals.com/blog/posts/2024/05/28/mostly-ds...

  • Ini mungkin terdengar klise dan berulang, tetapi keluhan terbesar saya tentang Rust adalah situasi manajemen paket, dan menurut saya itu sepenuhnya hasil dari pola pikir para pengembangnya
    Saya suka sisi usability Rust. Pendekatan fungsional terhadap tipe data itu indah. Namun sekarang saya sedang mengerjakan proyek Rust dan proyek Go berdampingan, dan pohon dependensinya benar-benar binatang yang berbeda
    Proyek Go sebagian besar selesai dengan standard library, tetapi proyek Rust tampaknya punya lebih dari 400 dependensi hanya karena meminta rusqlite (sqlite), clap (CLI), ratatui (TUI), dan tauri (GUI). Khususnya tauri adalah penyebab terbesar, tetapi bahkan tanpa itu pun jumlahnya hampir 100, dan rasanya gila
    Akan jauh lebih baik kalau ada alternatif crate Rust yang terkelola baik dan menangani dependensi secara masuk akal, tetapi saya belum menemukannya. Saya hanya tidak ingin memasukkan shai hulud ke dalam sistem, tetapi orang-orang Rust web tampaknya ingin menjadikan cargo seperti npm dalam hal itu

    • Perlu diingat bahwa banyak pustaka Rust dipecah menjadi beberapa crate, dan semuanya masuk ke grafik dependensi
      Karena itu jumlah dependensinya tampak lebih besar daripada kenyataannya. Walau terpisah sebagai crate, sering kali maintainer-nya sama dan semuanya bagian dari repositori Git upstream yang sama
      Meski begitu, saya setuju dengan kesan umumnya. Di Rust memang banyak crate versi 0.x yang setengah terbengkalai, dan sering tidak ada alternatif yang lebih baik
    • Menurut saya standard library adalah tempat ide bagus pergi untuk mati
      Lalu muncullah httplib3, lalu httplib4
      Dengan kata lain, saya jauh lebih suka pendekatan Rust. Bagi saya tidak ada banyak bedanya apakah bergantung pada standard library atau pada dependensi lain. Tetap saja itu dependensi
      Menganggap kualitasnya lebih baik atau pemeliharaannya lebih bagus hanya karena itu standard library adalah konsep yang terpisah
      Pada akhirnya semua bergantung pada sumber daya. Tentu standard library bisa mendapat lebih banyak sumber daya, tetapi sebaliknya juga bisa membengkak dan menjadi tak terpelihara
    • Saya ragu ada bahasa yang memiliki semua padanan rusqlite, clap, ratatui, dan tauri di standard library-nya, kecuali mungkin Java
      Selain itu, perlu dilihat juga bahwa Tauri sendiri terdiri dari 14 crate, dan masing-masing muncul di pohon build
      https://github.com/tauri-apps/tauri/blob/dev/Cargo.toml
      Ratatui juga 6
      https://github.com/ratatui/ratatui/blob/main/Cargo.toml
    • Manajemen paket adalah masalah hampir semua bahasa dan teknologi
      Tidak ada yang benar-benar “menyelesaikannya”, dan saya rasa juga sulit akan ada satu solusi tunggal di masa depan
      Di Go, Anda harus percaya bahwa pengembang pustaka akan benar-benar mematuhi semantic versioning, dan Anda tidak bisa pin versi. Ini secara pribadi juga cukup mengganggu saya
      Ada beberapa jalan memutar. Misalnya memakai SHA seperti hash commit Git untuk membuat versi semu, atau memakai vendoring yang merupakan cache dependensi yang sudah dikenal. Hanya saja vendoring membawa masalah pengelolaan cache
      Akhir pekan ini saya harus memakai virtual environment Python dan hasilnya tidak menyenangkan, dan itu mengingatkan lagi kenapa saya meninggalkan Python
      CPAN milik Perl, Maven/Gradle milik Java, gems milik Ruby, dep/glide/vgo/modules milik Go, Cargo milik Rust, npm/yarn milik Node, semuanya punya masalah yang serupa
      Sistem operasi juga sama, seperti yum/rpm milik Redhat, apt milik Debian, dan snap milik Ubuntu. Khususnya snap, saya benar-benar tidak mengerti kenapa bisa begitu
    • Saya tidak terlalu familiar dengan Go, jadi saya penasaran apa padanan Tauri di standard library Go
      Dari sudut use case, mungkin masuk akal juga tetap memakai Go untuk frontend dan hanya Rust untuk backend
  • Dokumen ini terasa aneh karena berusaha sekaligus menjadi panduan migrasi dan dokumen advokasi Rust
    Pada akhirnya, jika Anda sedang mempertimbangkan Rust vs Go, inti persoalannya hampir sepenuhnya bermuara pada “apakah Anda menginginkan managed runtime”. Satu generasi programmer Rust telah meyakinkan diri bahwa managed runtime itu buruk dan ketiadaannya adalah fitur penting
    Tetapi itu jelas salah. Ada lebih banyak area pemrograman yang memang menginginkan managed runtime daripada yang tidak
    Namun itu bukan berarti Go harus selalu menjadi pilihan default dalam kasus seperti itu. Ada juga banyak alasan subjektif untuk lebih menyukai Rust. Saat memakai Go saya merindukan match, tetapi saya tidak merindukan tokio dan Async Rust
    Keduanya adalah pilihan yang sah di hampir semua situasi yang tidak memaksa ruang masalah dipelintir secara tidak alami. Misalnya, menulis modul kernel Linux dengan Go tentu akan menjadi pilihan yang aneh
    Pertarungan Rust vs Go terasa seperti sudut aneh dan agak memalukan di bidang kita. Sebagian besar industri baik-baik saja membangun seluruh sistem dengan Python atau Node, dan menertawakan para geek yang bertengkar soal bahasa kompilasi bertipe statis mana yang harus dipakai. Pertanyaan yang sebenarnya adalah Python vs Rust/Go, bukan Rust vs Go

    • Menurut saya memakai Node bersama PureScript mungkin tidak masalah
      Tetapi secara umum kubu Rust dan Go seharusnya bersatu melawan keburukan dynamic typing. Jika type hint sekarang dianggap praktik terbaik, bukankah itu pada dasarnya pengakuan bahwa sebelumnya memang ada cacat
      Bahkan type hint yang bagus pun masih kalah dari type inference. Type inference memungkinkan banyak kode tetap tidak berubah saat tipe diubah, sambil tetap mencegah perubahan tipe yang tidak diinginkan
    • Kubu Node menerima TypeScript karena mereka menginginkan tipe kompilasi statis
      Saya berharap TS punya sedikit lebih banyak runtime. Satu-satunya hal dari Python yang saya iri adalah betapa alaminya validasi skema JSON di endpoint HTTP
      Alur yang harus dilewati dengan Zod terus menjadi sumber kejengkelan, dan menurut saya itu terjadi karena tim TS terlalu dogmatis
  • Jejak tulisan LLM makin lama makin halus, tetapi masih sangat mudah terlihat. Terutama kata genuine
    Contohnya seperti “This is the area where Go genuinely shines, and it’s worth being precise about why”, “the lack of GC pauses is a genuine selling point”, “Humans are genuinely bad at reasoning about memory”, “There are cases where the borrow checker is genuinely too strict”
    Saya tidak menganggap seluruh tulisan itu dihasilkan AI, tetapi tampaknya dibantu AI. Kalau begitu, penulisnya genuinely berhasil melakukannya dengan baik
    Karena orang lain tampaknya tidak terlalu menyinggung hal ini, sepertinya itu tidak terlalu merusak isi tulisannya, tetapi tetap terasa aneh bahwa hal seperti ini makin umum dan makin sulit dideteksi

    • Setuju, walau saya juga tidak begitu tahu kenapanya. Saya tidak tahu persis apa yang membuat sesuatu terdengar seperti buatan AI
      Saat membaca sampai “Go is clearly working for a lot of people,” saya mulai curiga itu dibantu AI. Tentu saja bisa jadi tidak, dan saya juga tidak pandai membedakannya
      Ironisnya, ini lebih mirip vibes daripada petunjuk konkret. Kalau sebuah tulisan “terdengar” seperti dibantu AI, saya langsung kehilangan minat walaupun tulisannya sendiri sebenarnya baik
      Saya berharap orang lebih nyaman menulis sendiri sesuai cara pikir mereka muncul di kepala
    • Ini benar-benar di luar topik, tetapi it's worth being precise about ... jauh lebih terasa seperti ungkapan AI daripada penggunaan genuine
    • Saya pikir seluruh tulisan itu dihasilkan AI. Mungkin penulis memberi draf sebagai input lalu mengubah sebagian outputnya
      Misalnya paragraf ini: “Go got generics in 1.18, and they’re useful, but the implementation has constraints (no methods with type parameters, GC shape stenciling, occasional surprising performance characteristics). Rust generics monomorphize, each instantiation produces specialized code with zero runtime cost. Combined with traits, this gives you real zero-cost abstractions.”
      Setiap kalimat mengatakan sesuatu, setiap kalimat penting, dan setiap kalimat menjalankan fungsinya. Gaya seperti ini lebih cocok diharapkan dari buku atau makalah yang sangat profesional daripada tulisan blog
      Justru karena itu, tulisan tersebut jadi lebih sulit dibaca dan lebih membosankan
    • Selama setahun terakhir saya merasa tulisan LLM punya kecenderungan yang sangat kuat untuk membicarakan permukaan dan terutama lapisan dasar
      Saya memang tidak berharap teks buatan LLM bebas dari ungkapan klise. Hanya saja saya berharap kita semua menunjukkan selera penyuntingan yang lebih baik agar tidak terus membaca suara yang sama berulang-ulang
  • Untuk proyek baru, silakan saja menulisnya dengan Rust
    Tetapi jika sudah ada kode yang berjalan dan menghasilkan uang, yang benar adalah terus lanjut sambil memperbaiki bagian yang memang perlu ditulis ulang dengan bahasa aslinya
    Perbaiki sistem secara kecil dan terukur dengan bahasa yang Anda kuasai dan tim yang bisa Anda percaya. Selain itu hanyalah perdebatan agama yang boros

    • Jika tim sudah berhasil meluncurkan produk dengan C#/Java/Go dan nyaman memakainya, saya tidak melihat alasan memakai Rust
  • Saya sudah menyukai Rust bahkan sebelum menjalankan benchmark, tetapi perbedaan efisiensi kebanyakan LLM dalam menulis Rust dan Go ternyata jauh lebih besar dari yang saya duga. Terutama pada harness agentic yang bisa memperbaiki masalah lingkungan awal
    Setelah melihat itu, saya menjadi penganjur Rust yang cukup kuat. Saya mendapat hasil bagus dengan menulis alat pemrosesan batch dalam Rust untuk dipanggil dari codebase yang sudah ada, tetapi saya belum mencoba migrasi produksi penuh
    Saya rasa masalah-masalah Go yang disebut di tulisan, terutama yang terkait penanganan nil, makin bisa diatasi lewat code review menyeluruh dengan Codex. Tentu lebih baik kalau masalahnya memang tidak ada sejak awal, tetapi bagi pengembang yang mencurahkan usaha pada review dan pemahaman sebanyak pada desain dan implementasi, bug keamanan seperti ini makin menjadi sesuatu yang opsional
    Data bahasanya ada di https://gertlabs.com/rankings?mode=agentic_coding

    • Berkat error compiler yang rinci dan sistem tipe yang kuat, agen lebih mudah menangani siklus perbaiki → kompilasi → perbaiki
      Rust menempatkan pengguna dengan tegas pada jalur yang sudah ditentukan. Codex selalu berhasil membuat sesuatu bisa dikompilasi
      Sisi buruknya, kadang ia seharusnya gagal ketika pendekatan idiomatis tidak memungkinkan, tetapi sebagai gantinya bisa menghasilkan implementasi bodoh yang tetap bisa dikompilasi dan memenuhi permintaan
    • Dari sudut pandang LLM, kelemahan Rust adalah waktu kompilasi
      LLM menulis kode lebih cepat daripada manusia, jadi secara relatif waktu menunggu kompilasi menjadi lebih besar. Pada proyek yang sudah cukup besar, misalnya lebih dari 100 ribu baris, kompilasi Rust yang sekitar 10 kali lebih lambat mulai menjadi bottleneck
      Jika Anda menulis infrastruktur inti, biaya itu mungkin layak dibayar, tetapi jika Anda membuat layanan internal yang tidak dibuka ke internet, kecepatan pengembangan bisa jadi perhatian yang lebih besar
      Saya juga merasa kompilasi lambat memengaruhi kecepatan pengembangan manusia, tetapi anehnya para developer sangat jarang mencoba mengukurnya secara kuantitatif
  • Jika kebertele-telan adalah hambatan utama, ini yang dijadwalkan masuk ke Go 1.28 bisa sangat menguranginya
    https://github.com/golang/go/issues/12854#issue-110104883

  • Ungkapan “layanan yang diandalkan organisasi, memerlukan uptime tinggi, dan kritis bagi bisnis” itu lucu
    Terutama saat layanan Rust itu berjalan di atas Kubernetes

  • Saya sudah memakai Rust dan tidak punya pengalaman dengan Go, jadi mungkin tulisan ini memang tidak terlalu ditujukan untuk saya
    Tetapi ada satu hal yang mengganjal. Mengatakan bahwa data race di Rust “tertangkap saat compile time” terasa setidaknya agak berlebihan
    Ungkapan ini bisa memberi kesan bahwa Rust juga bisa menangani hal-hal seperti lock starvation atau masalah konkurensi lain, padahal kenyataannya tidak
    Saya tahu data race adalah istilah formal dengan cakupan sempit, tetapi menurut saya ini tetap bisa ditulis dengan lebih jelas