Go’dan Rust’a Geçiş
(corrode.dev)- Go’dan Rust’a geçiş, hız artışından çok
nil, hata işleme, veri yarışı ve kaynak ömrü sorunlarını derleme zamanı güvencelerine taşıma tercihi olarak görülebilir - Go; hızlı derleme, basit goroutine yapısı ve güçlü backend ekosistemiyle öne çıkarken, Rust
Option,Result,Send/Syncile daha fazla hatayı tür sistemi düzeyinde engeller - Rust’ın borrow checker’ı ile
async/await, öğrenme eğrisi ve kullanılabilirlik maliyeti yaratır; derleme süreleri de Go’ya kıyasla açık bir gerileme olarak kabul edilmelidir - Geçişte, tüm sistemi baştan yazmak yerine hot path servisleri, worker’lar ve gateway arkasındaki belirli endpoint’ler gibi sınırları net bileşenlerden başlamak daha uygun bir stratejidir
- Beklenen kazanımlar; CPU kullanımında %20–60 düşüş, bellekte %30–50 azalma, daha düz bir P99 gecikme eğrisi ve
nildereference ile race kaynaklı arızalarda azalma olarak özetlenebilir
Geçişin odağı
- Go’dan Rust’a geçiş, “Rust daha mı hızlı?” sorusundan çok doğruluk garantileri, çalışma zamanı tavizleri ve geliştirici deneyimi farklarını değerlendirme meselesidir
- Karşılaştırmanın odağında backend servisleri yer alır; temel referans olarak da Go’nun güçlü olduğu küçük statik binary’ler, ağ odaklı standart kütüphane ve HTTP sunucusu, gRPC, veritabanı ekosistemi alınır
- Bazı noktalar CLI araçları, gömülü firmware ve oyun motorları için de geçerli olabilir, ancak bunlar optimize edilmiş hedefler değildir
- İlgili arka plan kaynakları olarak 2017 tarihli “Go vs Rust? Choose Go.” ve Shuttle ekibinin “Rust vs Go: A Hands-On Comparison” yazıları sunuluyor
- Go başarılı bir dil olsa da,
nil’in yaygın kullanımı, türler yerine disipline dayanan hata işleme yaklaşımı ve uzun süre generics içermemesi gibi tasarım tercihleri, Rust ile karşılaştırmada başlıca tartışma noktalarıdır - JetBrains Developer Ecosystem Survey’e göre Go, çalışan geliştiriciler arasında %17–19 bandını koruyan bir dil olarak öne çıkarken, Rust istikrarlı biçimde büyüse de daha küçük bir paya sahiptir
Araç zinciri
- Hem Go hem de Rust; build, test, formatlama, lint ve bağımlılık yönetimini tutarlı bir arayüzle sunan batteries-included araç zincirlerine sahiptir
cargo, Go araçlarıyla eşleşen işlevleri birinci sınıf araç olarak daha geniş kapsamda sunargo.mod/go.sum→Cargo.toml/Cargo.lock: proje yapılandırması ve bağımlılık manifestosugo get/go mod tidy→cargo add/cargo update: bağımlılık ekleme ve çözümlemego build→cargo build: derlemego run .→cargo run: derleyip çalıştırmago test ./...→cargo test: testgo vet ./...→cargo clippy: linter;Clippy,vet’e göre çok daha güçlü görüşlere sahiptirgofmt/goimports→cargo fmt: yapılandırmasız otomatik biçimlendiricigolangci-lint run→cargo clippy -- -D warnings: katı lint modugo doc→cargo doc --open: API dokümantasyonu üretme ve görüntülemepprof→cargo flamegraph/samply: CPU profillemegovulncheck→cargo audit: tavsiye veritabanı tabanlı güvenlik açığı taraması
- Go tarafında boşluklar çoğu zaman
golangci-lint,mockgen,air,goreleasergibi üçüncü taraf araçlarla kapatılırken, Rust birinci taraf ekosisteminde daha fazla işlevi varsayılan olarak kapsar - Harici crate gerektiğinde bile,
cargo watchvecargo nextestgibi araçlarcargo install cargo-nextestile tek seferde kurulupcargo nextestgibi yerel araç hissiyle çalışır gofmtilerustfmt’in en büyük avantajı, ince stil tercihlerinden çok kod incelemelerinde stil tartışmalarını ortadan kaldırmalarıdır- Rob Pike’ın Go Proverbs alıntısı: “Gofmt’s style is no one’s favorite, yet gofmt is everyone’s favorite.”
Go ve Rust arasındaki temel farklar
- Her iki dil de derlenen dil, statik türler, tek binary olarak dağıtım ve güçlü eşzamanlılık modelleri sunar; ancak fark, derleyicinin garanti ettiği kapsam ve çalışma zamanı davranışı üzerindeki kontrol düzeyindedir
- Başlıca karşılaştırma başlıkları şunlardır
- Kararlı sürüm: Go 2012, Rust 2015
- Tür sistemi: Go statik ve yapısal türlemeye sahiptir, 1.18’den beri generics destekler; Rust statik ve nominal türlemeye sahiptir, generics, trait ve lifetime desteği sunar
- Bellek yönetimi: Go eşzamanlı ve düşük gecikmeli garbage collector kullanır; Rust sahiplik ve borrowing temellidir, GC yoktur
- Null güvenliği: Go’da
nilyaygındır; Rust’ta null yoktur ve tür düzeyindeki karşılığıOption<T>’dir - Hata işleme: Go
errorarayüzü veif err != nil { ... }kullanır; RustResult<T, E>,?operatörü ve tam desen eşleme sunar - Eşzamanlılık: Go goroutine ve kanal tabanlı CSP yaklaşımını kullanır; Rust
tokioüzerindeasync/await, kanallar ve thread’ler kullanır - İptal: Go’da gelenek temelli
context.Contextkullanılır; Rust’taCancellationTokengibi açık ve tür denetimli aktarım yöntemleri vardır - Veri yarışı: Go
-raceile çalışma zamanında olasılıksal tespit yapar; RustSend/Syncile derleme zamanında tespit eder - Derleme süresi: Go çok hızlıdır; Rust ise özellikle temiz build’lerde yavaştır
- Çalışma zamanı: Go yaklaşık 2MB’lik runtime ve GC içerir; Rust’ta
libcdışında runtime olmayabilir veya MUSL ile tamamen statik build mümkündür - Ekosistem ölçeği: Go yaklaşık 750 bin+ modül, Rust ise 250 bin+ crate düzeyindedir
- Go’da geleneklere, araçlara ve çalışma zamanı tespitine bırakılan
nilişleme, hata yayılımı, veri yarışı, kaynak ömrü, iptal ve generics gibi denetimler, Rust’ta tür sisteminin içine alınır - Rust’ın
Mutex<T>yapısı, iç değere yalnızca.lock()ile alınan guard üzerinden erişilmesini sağlayarak “kilidin unutulduğu yol” ihtimalini tür düzeyinde ortadan kaldırır - Aynı yaklaşım
Option,Result,&mut T,Send/Syncve RAII guard kalıplarında da tekrar eder; buna alışıldığında derleyici, zihinsel kontrol listesinin bir kısmını geliştiricinin yerine üstlenir
Rust’ı değerlendirmeye yönelten Go sınırlamaları
- Go, çoğu backend iş yükü için yeterince hızlı olduğu için, Rust’ı değerlendirmenin başlıca nedeni hızdan çok hata işlemenin ayrıntılı olması,
nilpointer riski ve enum·trait gibi gelişmiş tür sistemi özelliklerinin eksikliğine daha yakındır - Go interface’leri, Rust trait’lerinin yeterli bir karşılığı değildir ve standart kütüphanede
Settipi olmadığındanmap[T]struct{}gibi deyimsel dolaylı çözümler gerekir -
Prodüksiyonda
nilpanic’i- Go servisleri aylarca normal çalıştıktan sonra belirli bir kod yolunda pointer için
nilkontrolü atlandığı için goroutine panic’i üretebilir - Örnekte
Find,(*User, error)döndürür ve “not found” durumundaerrornilolsa dauserkontrolü çağırana bırakılır user.Account.Notify(),userveyaAccountnilolduğunda çökebilirnilaway,staticcheckgibi linter’lar ve IDE kontrolleri bazılarını yakalasa da, bunlar opt-in’dir, olasılıksaldır ve paket sınırlarını güvenilir biçimde aşamaz- Rust’taki
Option<T>,Nonedurumunu ele almadan dereference etmeyi engelleyerek bu arıza kategorisini ortadan kaldırır
- Go servisleri aylarca normal çalıştıktan sonra belirli bir kod yolunda pointer için
-
-race’in yakalayamadığı data racego test -raceharika bir araçtır, ancak bir runtime dedektörü olduğu için yalnızca test sırasında gerçekten yürütülen race’leri bulur- Go’da iki goroutine’in lock olmadan bir map’i değiştirdiği kod da derlenir ve yük altındaki prodüksiyonda patlayabilir
- Rust’ta thread’ler arasında mutable state paylaşımı için
SendveSyncimplement eden tiplere ihtiyaç vardır; sıradan birHashMap’i thread’ler arasında paylaşmaya çalışırsanız kod derlenmez Arc<Mutex<...>>,Arc<RwLock<...>>veya channel kullanmanız zorunlu hale gelir ve race condition bir tür hatasına dönüşür- Paul Dix, InfluxDB 3.0 yeniden yazımının gerekçesi olarak data race’lerin ortadan kaldırılmasını doğrudan anıyor
- “[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.”
- Kaynak: Paul Dix, Founder & CTO, InfluxData, Rust in Production
-
Birleştirilebilir hata işleme
- Go’daki
if err != nil { return err }, fonksiyonun gerçek mantığını sulandırabilir ve bağlamıfmt.Errorf("doing X: %w", err)ile sarmak, derleyici kuralından çok disipline dayanır - Lobste.rs başlığında deneyimli Go geliştiricileri,
errcheckvegolangci-lint’in eksik hata işlemenin büyük kısmını yakaladığını ve açıkif err != nilyapısının yoğun?zincirlerinden daha okunabilir olduğunu söyleyerek karşı çıkıyor - Peter Bourgon, Go’nun açık hata işlemesini dilin kasıtlı bir kültürel değeri olarak sunuyor
- “I think that error handling should be explicit, this should be a core value of the language.”
- Kaynak: Peter Bourgon, GoTime #91, Dave Cheney’nin Zen of Go yazısında alıntılanıyor
- Rust’ın
Result<T, E>yapısı tür imzasının kendisidir, bu yüzden gözden kaçırılamaz;thiserror::Errorile tanımlanan enum ve#[from]sayesinde hata dönüşümü ve tamlık kontrolü sağlanır - Yeni bir hata variant’ı eklediğinizde derleyici, güncellenmesi gereken
matchkonumlarını size bildirir
- Go’daki
-
Box’lama yapmayan generics
- Go 1.18 generics yararlıdır, ancak tür parametreli method eksikliği, GC shape stenciling ve zaman zaman şaşırtıcı performans özellikleri gibi kısıtları vardır
- Rust generics monomorphized edilir; böylece her somutlama özelleştirilmiş kod üretir ve runtime maliyeti oluşmaz
- Trait’lerle birleştiğinde sıfır maliyetli soyutlama mümkün olur
- Bu, handler kodundan çok middleware, generic repository, decoder, parser gibi paylaşılan altyapıda daha önemlidir; Go ise bu alanlarda sık sık
interface{}/anyve type assertion’a geri döner
-
Öngörülebilir gecikme süresi
- Go’nun GC’si mükemmeldir; eşzamanlıdır, düşük gecikmelidir ve genel servis iş yükleri için iyi ayarlanmıştır, ancak “low-pause”, “no-pause” anlamına gelmez
- Tahsisin yoğun olduğu durumlarda, sıcak yolda tahsis yapmayan Rust implementasyonuna kıyasla P99 gecikme kuyruğu daha kötü olabilir
- Trading, gerçek zamanlı teklif verme, ağ proxy’leri ve yüksek throughput’lu veri toplama gibi gecikmeye duyarlı sistemlerde GC pause olmaması gerçek bir avantajdır
- Stephen Blum, PubNub ölçeğinde ihtiyaç duydukları fiyat/performans kapasitesini elde etmek için Rust’a ihtiyaçları olduğunu söylüyor
- “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.”
- Kaynak: Stephen Blum, CTO, PubNub, Rust in Production
Rust’ta Go kalıplarının karşılıkları
- Rust’a alışmanın en hızlı yolu, zaten bildiğiniz Go kalıplarını Rust’taki karşılık gelen kalıplarla eşleştirmektir
- Aynı backend servisini iki dilde uygulayan daha uzun bir örnek için Shuttle comparison yazısına bakabilirsiniz
-
Hata işleme:
if err != nilvsResult<T, E>- Go,
os.ReadFile(path)vejson.Unmarshalsonrasındaif err != nilile bağlam eklenmiş hatayı döndürür - Rust tarafı
fs::read_to_string(path)?,serde_json::from_str(&data)?,Ok(cfg)yapısından oluşur ?operatörüif err != nil { return err }kalıbının yerini alır veFrom<E1> for E2uygulanmışsa tip dönüşümünü de yaparthiserroriçindeki#[from], bu dönüşümü idiomatik şekilde destekler
- Go,
-
Null:
nilvsOption<T>- Go’daki
GetUser(id string) *User, kullanıcı bulunamazsanildöndürür; çağıran taraffmt.Println(u.Name)yaparsanildurumunda panic oluşur - Rust’taki
get_user(id: &str) -> Option<User>,Some(User)veyaNonedöndürür let user = get_user("123"); println!("{}", user.name);ifadesi,userbirUserdeğilOption<User>olduğu için derleme hatası verirmatch get_user("123")ile hemSome(u)hem deNonedurumlarını ele almanız gerekir- Güvenli Rust’ta
nilyoktur ve referanslar null olamaz
- Go’daki
-
Arayüzler vs trait’ler
- Go arayüzleri yapısaldır; bir tip, arayüzü örtük olarak karşılar
- Rust trait’leri nominaldir; açıkça implemente edilmeleri gerekir
- Go yaklaşımı anlık duck typing için iyidir; Rust yaklaşımı ise refactoring ve discoverability için daha iyidir, ayrıca belirli bir trait implementasyonunu grep ile bulabilirsiniz
fn handle<R: Reader>(r: R)gibi trait bound eklenmiş generic function’lar çoğu durumu kapsar ve monomorphization sayesinde runtime dispatch yoktur- Runtime dispatch gerektiren heterojen implementasyonları saklamak için
Box<dyn Trait>veyaArc<dyn Trait>kullanılır
-
Goroutine vs async task
- Go’nun eşzamanlılık modeli
go doWork(ctx, input)örneğinde olduğu gibi basittir; goroutine’ler hafiftir ve runtime bunları OS thread’leri üzerinde zamanlar - Go’nun büyük avantajlarından biri, sıralı kod ile paralel kod arasında sözdizimsel bir ayrım olmamasıdır
- Rust, backend servislerinde neredeyse her zaman
tokioexecutor üzerindekiasync/awaityapısını kullanır - async fonksiyonlar
Futuredöndürür ve await edilmeden ya da spawn edilmeden çalışmaz - Derleyici,
.awaitnoktalarının öncesi ve sonrasındaSend/Syncdurumunu izler; non-Sendbir değeri await sonrasına taşırsanız derleme hatası verir - Yerleşik goroutine tarzı preemption olmadığından, CPU-bound işleri async task içinde uzun süre çalıştırmak executor’ü aç bırakabilir; bunları
tokio::task::spawn_blockingveyarayonile devretmeniz gerekir
- Go’nun eşzamanlılık modeli
-
context.ContextvsCancellationToken- Go’da her blocking call’a
context.Contextgeçirilir - Rust’ta yerleşik bir
context.Contextyoktur; iptal için en yakın karşılıktokio_util::sync::CancellationToken’dır - timeout için future,
tokio::time::timeout(dur, fut)ile sarılır - deadline ve değerler, tek bir context nesnesi yerine çoğu zaman açık parametrelerle veya
tracingspan’ları üzerinden aktarılır - Dave Cheney’nin The Zen of Go yazısından alıntı:
- “Go’nun bir goroutine’e çıkmasını söyleyen bir yolu yoktur. İyi bir nedenle, stop veya kill fonksiyonu yoktur. Bir goroutine’e durmasını emredemiyorsak, onun yerine kibarca istememiz gerekir.”
- Go’da bu “kibar rica” genellikle
context.Contextile taşınır; Rust’ta iseCancellationTokenveyawatchkanalıyla yapılır, ancak derleyici eksiklikleri size bildirebilir
- Go’da her blocking call’a
-
Dizgeler:
stringvsStringve&str- Go’daki
string, UTF-8 byte slice’tır; atama sırasında header kopyalanır ve alttaki byte’lar paylaşılan, değiştirilemez bir yapıda kalır - Rust bunu iki tipe ayırır
String: sahip olunan, heap üzerinde ayrılmış ve büyüyebilen tür&str: başka bir string verisine ödünç alınmış bir görünüm; çoğu durumda Go’dakistringparametresine karşılık gelir
- Pratik kural, argüman olarak
&stralmak ve yeni veri üretiliyorsaStringdöndürmektir &strveStringayrımı, Rust’ın “borrow vs own” modelinin küçültülmüş bir yansımasıdır
- Go’daki
Go jenerikleri üzerine değerlendirme
- Go, 1.18 ile Mart 2022’de jenerikleri kullanıma sundu; bu da dilin çıkışından 13 yıl sonra oldu
- Jenerikler faydalı olsa da Rust, Haskell ve modern C++’ta beklenen avantajları tam olarak sunmadığı, buna karşılık jenerik tip sistemlerinin önemli bir kısmındaki dezavantajları da beraberinde getirdiği değerlendiriliyor
-
Standart kütüphane neredeyse hiç kullanmıyor
- Jeneriklerin gelmesinden 3 yıl sonra bile Go standart kütüphanesi büyük ölçüde jenerikleri kullanmaktan kaçınıyor
sort.Slice, hâlâcmp.Orderedconstraint yerinefunc(i, j int) boolclosure alıyorsync.Map, hâlâany/anyolarak tiplendiriliyor- Mevcut generic helper’lar,
slices,maps,cmp,syncaltındaki bazı öğeler gibi az sayıdaki pakette bulunuyor - Go 1 uyumluluk sözü nedeniyle mevcut non-generic API’leri dönüştürmenin zor olması bunu kısmen açıklasa da, Rust’taki gibi jenerikler ana araç olarak kullanılmıyor
- Rust’ta ise en baştan beri
Option<T>,Result<T, E>,Vec<T>,HashMap<K, V>,Iterator,From/Into, tüm koleksiyonlar ve akıllı işaretçiler jeneriklerle iç içe
-
Trait sistemi yok ve yalnızca yapısal constraint’ler var
- Rust jenerikleri; ad-hoc çok biçimlilik, supertrait, associated type, blanket impl ve coherence sağlayan trait’lerle birlikte geliyor
- Go constraint’leri, type-set membership için
~operatörü eklenmiş interface’lere daha yakın - Go’da Rust’taki
trait Ord: Eq + PartialOrdgibi bir supertrait hiyerarşisi,Iteratoriçindekitype Item;benzeri associated type ya daimpl<T: Display> ToString for Ttürü blanket impl yok - Go’da tip parametreli metotlar kullanılamadığı için
func (s Set[T]) Map[U](<https://corrode.dev/learn/migration-guides/go-to-rust/f func(T>) U) Set[U]gibi bir biçim mümkün değil - Soyutlama, “birkaç işlemi olan rastgele bir
Tüzerinde çalışan fonksiyon” seviyesini aştığında, Go yenidenany, tip doğrulaması, kod üretimi ve çalışma zamanı reflection’a dönüyor
-
Tip çıkarımı ve uygulama stratejisindeki farklar
- Rust, closure, iterator chain ve
?operatörü dahil tüm expression boyunca tip bilgisini yayabiliyor - Go’nun tip çıkarımı daha sığ; genelde fonksiyon argümanlarından type parameter çıkarıyor ama return-position context içinde çıkarım yapamıyor ve çağrı noktasında sık sık açık type argument gerektiriyor
- Go, hızlı derleme sürelerini korumak için GCShape stenciling and dictionaries adlı orta yolu seçiyor, ancak type parameter üzerindeki her metot çağrısında dolaylı erişim eklenebiliyor
- Bunu gösteren kaynak olarak PlanetScale yazısı sunuluyor
- Rust ise
Vec<i32>veVec<String>için ayrı ayrı özelleştirilmiş makine kodu üretiyor ve çalışma zamanında dispatch yapmıyor - Monomorfizasyonun bedeli derleme süresi; iki dil farklı hedefler için optimizasyon yapıyor
- Rust, closure, iterator chain ve
-
Tip sistemindeki boşlukları kapatmıyor
- Rust’ta jenerikler ve trait’ler,
Box<dyn Any>veya çalışma zamanı reflection gerektiren durumların büyük bölümünü ortadan kaldırıyor - Go jenerikleri ise
any,reflect, ORM, decoder ve mock’larda baskın olan kod üretim kalıplarını ortadan kaldıramıyor encoding/jsonhâlâ reflection kullanıyor,database/sqlhâlâanykullanıyor vemockgenhâlâ kod üretiyor- Go’daki jenerikler, dar bazı durumlarda yararlı yeni bir araç gibi hissettirirken; Rust’taki jenerikler, çıkarıldığında dilin çökeceği bir temel gibi işliyor
- Rust’ta jenerikler ve trait’ler,
Rust backend ekosistemi
- Rust ekosisteminde de genel backend servisleri için bir ölçüde “varsayılan seçenekler” üzerinde uzlaşılmış durumda
- Başlıca eşleştirmeler:
- HTTP server: Go
net/http,chi,gin,echo,fiber→ Rustaxumonhyper - HTTP client: Go
net/http,resty→ Rustreqwest - gRPC: Go
google.golang.org/grpc+protoc-gen-go→ Rusttonic+prost - SQL: Go
database/sql,sqlc,sqlx,gorm→ Rustsqlx,sea-orm,diesel - Migrations: Go
golang-migrate,goose→ Rustsqlx migrate,refinery - JSON: Go
encoding/json,sonic,goccy/go-json→ Rustserde+serde_json - Logging: Go
log/slog,zerolog,zap→ Rusttracing+tracing-subscriber - Metrics: Go
prometheus/client_golang→ Rustmetrics+metrics-exporter-prometheus - Config: Go
viper,koanf→ Rustconfig/ config-rs,figment - CLI: Go
cobra,urfave/cli→ Rustclapderive - Errors: Go
errors,pkg/errors→ Rustthiserrorfor libraries,anyhowfor binaries - Testing: Go
testing,testify,gomega→ Rust yerleşik#[test],rstest,assert_matches - Mocking: Go
mockgen,moq→ Rust’ta elle yazılmış fake’ler yaygın kabul edilir;mockallda kullanılır - Background tasks: Go goroutine’ler +
errgroup→ Rusttokio::spawn+JoinSet
- HTTP server: Go
- Tipik bir backend servisinde
axum+sqlx+tokio+tracing+serde+clapkombinasyonunun gerekenlerin %90’ını karşıladığı ifade ediliyor
Ödünç alma denetleyicisi ve öğrenme eğrisi
- Go’dan Rust’a gelindiğinde duvara çarpmanın kaçınılmaz olduğu varsayılmalı
- Go runtime’ı bellek ve aliasing’i sizin yerinize yönetir; Rust ise bu kararları tür sistemine taşıdığı için ilk birkaç haftada “kesin çalışması gereken” kodlar derleyici tarafından reddedilebilir
- Go geliştiricilerinin sık takıldığı kalıplar:
- Uzun ömürlü referanslar: Go’da map’ten alınan
*User’ı uzun süre elde tutmak doğalken, Rust’ta bu ödünç alma yaşadığı sürece map’in değiştirilmesi engellenir - Kendi kendine referans veren struct’lar: Go’da veriyi ve o veri üzerindeki iterator’ü aynı struct’ta tutabilirsiniz; Rust’ta ise
Pin,ouroborosveya yeniden tasarım gerekir - goroutine’ler arasında değiştirilebilir durum paylaşımı: Go’daki
mu sync.Mutex; data map[K]Vkalıbı Rust’taArc<Mutex<HashMap<K, V>>>biçimine dönüşür - Fonksiyondan referans döndürme: ömür etiketi devreye girer ve bu, Go geliştiricileri için yeni bir kavramdır
- Uzun ömürlü referanslar: Go’da map’ten alınan
- Ödünç alma denetleyicisi, işi zorlaştıran bir “kapı bekçisi” değil, gerçekten var olan hataları ortaya çıkaran bir mekanizma olarak görülmeli
- Bir değerin taşındıktan sonra yeniden kullanılması, birden çok thread’in aynı veriye aynı anda dokunması, null veya dangling pointer’ların dereference edilmesi ya da referansın değerden daha uzun yaşaması gibi durumları derleme zamanında eler
- Ödünç alma kavramı içselleştirildiğinde karşı çıkılan bir şey olmaktan çıkıp işbirlikçiye dönüşür; deneyimli Rust geliştiricileri genellikle 4 ila 12 hafta arasında ödünç alma denetleyicisinin yardımcı hale geldiğini söyler
- PubNub CTO’su Stephen Blum, Rustacean Station programında ilk ayı “ilk kez programlama öğreniyormuş gibi” diye anlatıyor ve ödünç alma denetleyicisiyle ömürleri zorunlu olarak ele almak gerektiğini söylüyor
clapmaintainer’ı Ed Page, Rustacean Station: clap with Ed Page bölümünde ödünç alma denetleyicisinin yüksek seviyeli sorunlara odaklanmasını sağladığını ve kendi analizinin yakalayamadığı noktaları da bulduğunu söylüyor
Rust’a geçişte başlıca zorluklar
-
Derleme süresi
- Rust derleme süresi, Go’ya kıyasla açık bir gerileme olarak kabul edilmeli; orta ölçekli bir servisin temiz release build’i, Go’nun neredeyse anında derlemesine karşılık birkaç dakika sürebilir
- Incremental build ve
cargo checkmakul araçlardır ve derleme süreleri her yıl iyileşti, ancak Go ile fark hissedilir - Düzenleme döngüsünde
cargo checkkullanın, fayda sağlamaya başladığı noktada workspace’e bölün ve çok sayıda procedural macro içeren crate’leri ayrı crate’lerde tutarak yalnızca değiştiklerinde yeniden derlenmelerini sağlayın - Daha fazlası için Rust derleme süresini azaltma ipuçları yazısına bakılabilir
-
Async renklendirme problemi
- Rust’taki
async fn/fnayrımı, Go’dan geçerken kullanılabilirlik açısından en büyük gerilemelerden biridir - async trait, Rust 1.75’ten beri stabil olsa da dinamik dispatch ile karıştırıldığında hâlâ pürüzlü yanları vardır
- Bazı durumlarda bu pürüzleri örtmek için
async-traitcrate’i kullanılır
- Rust’taki
-
Daha küçük ekosistem
- Rust crate ekosistemi büyüyor ve kütüphane kalitesi genel olarak yüksek, ancak Go bazı backend’e yakın alanlarda önde
- Go’nun önde olduğu alanlar arasında Kubernetes operatörleri, bulut sağlayıcı SDK’ları ve belirli niş depolama sistemlerine yönelik veritabanı sürücüleri yer alıyor
- Geçiş kararını kesinleştirmeden önce, bağımlı olduğunuz kütüphaneler için kullanılabilir Rust alternatifleri olup olmadığını kontrol etmeye yaklaşık bir gün ayırmak gerekir
- Bazı ekipler sahipsiz kalmış XML şema doğrulama crate’lerini güncellemek veya daha az bilinen protokoller için istemciyi kendileri yazmak zorunda kalabilir
Entegrasyon stratejisi
- Go’dan Rust’a başarılı geçiş, her şeyi tek seferde yeniden yazmaktan çok taktiksel seçimlerle ilgilidir
- Microsoft Principal Engineer Victor Ciura, Rust in Production programında bunu “her şeyi eğlencesine Rust ile yeniden yazmak değil; yeni bileşen Rust’a daha uygunsa Rust’ı seçmeye yönelik taktiksel bir karar” diye tanımlıyor
-
1. Sıcak yolu ayrı bir servis olarak çıkarmak
- Belirli bir servis sürekli sorun çıkarıyorsa, aynı API sözleşmesinin arkasında tutup yalnızca o servisi Rust ile yeniden yazmak en düşük riskli geçiş yöntemidir
- Hedef; CPU kullanımı yüksek, gecikmeye duyarlı veya istikrar sorunları tekrar eden bir servis olabilir
- Diğer Go servisleri HTTP/gRPC üzerinden iletişim kurmaya devam edeceği için iç uygulama dilini bilmeleri gerekmez
- Radar CTO’su Jeff Kao, Rust in Production programında Discord’un Go’dan Rust’a geçişini anlattığı yazının Radar’da da aynı yaklaşımı düşünmelerini sağladığını söylüyor
-
2. Sidecar veya worker process’i değiştirmek
- Arka plan worker’ları, kuyruk tüketicileri, toplama pipeline’ları ve CPU-bound batch işleri iyi ilk hedeflerdir
- Bunlar genellikle kuyruk veya topic gibi net giriş/çıkış sınırlarına sahiptir ve sistemin geri kalanıyla process içi paylaşılan durum barındırmaz
-
3. cgo mümkün ama sancılı
- Go’dan cgo aracılığıyla Rust çağrılabilir ve bunu ele alan iyi rehberler de vardır
- Ancak backend servislerinde genellikle önerilmez
- Build karmaşıklığı ve FFI ek yükü, çoğu zaman “bir Rust servisi kurup onu ağ çağrısının arkasına koyma” yaklaşımına göre sağlayacağı avantajları dengeler
- Kütüphaneler ve CLI araçlarında daha pratik olabilir
-
4. Gateway arkasında Strangler Pattern uygulamak
- Bir API gateway veya reverse proxy varsa, yalnızca belirli endpoint’leri yeni Rust servisine yönlendirip geri kalanını Go’da bırakabilirsiniz
- Kimlik doğrulama, arama veya ödeme gibi tek bir sınırlandırılmış bağlam geçiş birimi olarak uygunsa bu yöntem özellikle iyi çalışır
- Bu kalıba, yeni servisin mevcut servisin etrafında büyüyüp sonunda onu tamamen değiştirmesi anlamında “strangler fig” denir
Pratik geçiş ipuçları
- Sınırları net olan servislerle başlanmalı; en merkezi ve en sık deploy edilen servis seçilmemeli
- Sistem geri kalanıyla sözleşmesi iyi tanımlanmış ve etki alanı küçük bir servis seçilmeli
-
Aynı API sözleşmesini korumak
- Go servisi bir REST API sunuyorsa, Rust servisi de aynı yolları, aynı JSON biçimini ve aynı hata sarmalayıcısını korumalı
- İstemciler açısından geçiş görünmez olur ve gateway üzerinden trafik kademeli olarak taşınabilir
-
Deyimleri bire bir taşımamak
if err != nil { return err }ifadesi?olur- İstek başına goroutine kalıbı yalnızca gerçekten gerektiğinde
tokio::spawnile taşınır axumzaten istekleri eşzamanlı olarak işler- Tek metotlu interface’ler çoğu durumda
Box<dyn Trait>değil, generics üzerindeki trait bound’a dönüşür
-
Derleyiciyi pair programmer gibi kullanmak
- Rust derleyici hataları genelde yüksek kalitelidir ve yavaşça okunduğunda neredeyse her zaman doğru cevabı verir
- En uzun süre zorlanan ekip üyeleri, derleyiciyi işbirlikçi olarak görmek yerine onunla savaşanlardır
-
Başta eğitime yatırım yapmak
- Rust geçişini işi yürütürken “yandan” öğrenmeye çalışmak çoğu zaman iyi sonuç vermez
- Workshop’lar, çevrimiçi kurslar ve gerçek kod tabanı üzerinde pair session’lar gibi öğrenme zamanı gerçekten ayrılmalı
- Ekip yetkin hale geldiğinde bu başlangıç yatırımı kat kat geri döner
Go’nun uygun kalmaya devam ettiği alanlar
- Her şeyi Rust’a taşımak gerekmez; Go’nun özellikle iyi olduğu alanlar vardır
-
Kubernetes yerel araçları
- Operatör, kontrolcü ve CRD alanlarında ekosistem ezici biçimde Go merkezlidir
-
CLI yardımcı programları ve geliştirme araçları
- Hızlı derleme, kolay çapraz derleme ve basit dağıtım güçlü yanlarıdır
-
Glue servisleri
- İnce API katmanları, proxy’ler ve biçim dönüştürücülerde Rust’ın boilerplate oranı buna değmeyebilir
-
Ekip hızının mutlak doğruluk garantisinden daha önemli olduğu yerler
- Hızlı hareket edilmesi gereken alanlarda Go hâlâ uygun olabilir
- Canonical VP of Engineering Jon Seager, Rust in Production programında Go’nun ağ servisleri için çok iyi bir seçenek olduğunu, Canonical’da çokça Go kullanıldığını ve Juju’nun da devasa bir Go kod tabanı olduğunu söylüyor
- Hibrit strateji yaygındır; birçok ekip, “sıkıcı” servisler için Go’yu, ek çabanın istikrar ve performansla geri kazanıldığı servisler içinse Rust’ı kullanarak çok dilli bir backend’e yönelir
Beklenebilecek iyileştirmeler
- Rakamlar iş yüküne göre büyük ölçüde değiştiğinden bunlara bir vaat değil, kabaca bir kılavuz olarak bakılmalıdır
- Go’dan Rust’a geçişte gözlemlenen yaklaşık iyileşme aralıkları:
- CPU kullanımı: %20~60 azalma
- Go zaten verimli olduğu için Python’dan Rust’a geçişteki kadar dramatik değildir
- Kazanç, GC’nin olmamasından ve daha sıkı döngülerden gelir
- Bellek: %30~50 azalma
- Bunun başlıca nedeni GC ek yükünün olmaması ve runtime’ın daha küçük olmasıdır
- P99 gecikmesi: çok daha tutarlı
- Rust servisleri, Go servislerinde görülen GC kaynaklı jitter’ın azalması ve düzleşmesi eğilimindedir
- Go tarafı da düşük gecikmeli GC’nin gelmesinden sonra çok gelişti, ancak yüksek yük altında fark sürüyor
- Prodüksiyon arızaları: ekiplerin en güçlü şekilde bildirdiği iyileşme alanıdır
go test -racetestini geçip prodüksiyona kadar ulaşabilen veri race’leri, nil dereference’ları ve kaçırılmış hata yolları gibi hata türleri Rust’ta derlenmez- Rust’a geçişten sonra nöbetçi on-call vardiyaları genellikle çok sıkıcı hâle gelir
- CPU kullanımı: %20~60 azalma
- InfluxData Staff Engineer Andrew Lamb, Rustacean Station: Rebuilding InfluxDB with Rust programında InfluxDB yeniden yazımından sonra çöküşler, garip çok iş parçacıklı race condition’lar ve eskiden çok zaman alan sorunların izini sürme ihtiyacının ortadan kalktığını söylüyor
- Go’dan Rust’a geçmenin, Python’dan Rust’a geçişte olduğu gibi throughput’u 10 kat artırması pek olası değildir
- Gerçek fayda; “saçma hataların” azalması, gecikme kuyruğunun daha düz hâle gelmesi ve aynı dille gömülü geliştirme ya da sistem programlama gibi başka alanlara da genişleyebilme yeteneğidir
Ek dikkat noktaları
- Rust’ın tip sistemi tüm senkronizasyon mantığı hatalarını ortadan kaldırmaz, ancak senkronizasyon olmadan iş parçacıkları arasında paylaşılamayan tipler derlenmez
- “Kilidi almayı unuttum” türündeki, sessiz veri bozulmasına yol açabilecek sorunlar Rust tip sistemi tarafından engellenebilir
- Go
string, değiştirilemez bir bayt dizisidir ve geleneksel olarak UTF-8 kabul edilir, ancak bu tip düzeyinde garanti edilmez - En yakın eşleşme, salt okunur görünüm için Go
string↔ Rust&str, değiştirilebilir tampon için Go[]byte↔ RustVec<u8>şeklindedir - Rust
String,&str’nin sahipli ve büyütülebilir sürümüdür; ayrıca içeriğin geçerli UTF-8 olduğuna dair ek garanti sunar - Ayrıntılar için Strings, bytes, runes and characters in Go yazısına bakabilirsiniz
- Go 1.18’den itibaren jenerik fonksiyonlar ve jenerik tipler mümkündür, ancak metodların kendilerine ait tip parametreleri eklenmemiştir
- Rust’taki
(0..100).filter(|n| ...).collect()gibi iterator zincirleri Go geliştiricilerine yabancı gelebilir, ancak Rust’ta dafordöngüleri kullanılabilir ve tek seferlik kodlarda bu çoğu zaman doğru tercihtir
Sonuç
- Go’dan Rust’a geçiş, Python veya TypeScript dünyasından Rust’a geçişten farklıdır
- Go kökenli geliştiriciler statik tiplerin ve derlenen dillerin avantajlarını zaten bildiğinden, bu dinamik tipleri ya da yavaş runtime’ı bırakma türünde bir geçiş değildir
- Temel takas,
nil’den vazgeçip daha sağlam bir kod tabanı, daha az tuzak ve daha fazla hatayı derleme zamanında yakalayan daha katı bir derleyici elde etmektir - Bunun karşılığında öğrenme eğrisi daha diktir
- Kuruluşun bağımlı olduğu, yüksek erişilebilirlik gerektiren ve işletme açısından kritik servislerde — örneğin temel servisler — bu takas açıkça değerlidir
- Diğer servislerde ise doğru cevap hâlâ Go olabilir
- Migrasyonun amacı, her problemi onu en iyi çözen dile yerleştirmektir
1 yorum
Hacker News yorumları
C/C++ ya da Python’dan Rust’a geçmek çeşitli nedenlerle anlaşılır, ancak web backend için Go daha uygun bir seçim gibi görünüyor
Neredeyse sadece Rust kullanıyorum ama en son Rust ile web sunucusu tarafında çalıştığımda keşke Go kullansaydım diye düşündüm
Asıl yazı Go’nun hata işleme sözdiziminin ayrıntılı ve uzun olduğunu söylüyor; bu doğru bir tespit. Rust’ta da aynı sorun vardı, sonra hata varsa hata değerini döndüren
?sözdizimi eklendi. Go’nun hata işlemesi çoğunlukla bunun açık açık yazılmış haliRust’ta birleşik bir hata tipi yok;
io::Error,thiserror,anyhowgibi başlıca hata düzenleri var ve bunları çağrı zinciri boyunca yukarı taşımak uğraştırıcıYeni bir dilde başta eksik olup sonradan eklemesi zor olan şeyler var. Sabit tipleri, boolean tipi, hata tipi, çok boyutlu dizi tipi, 2/3/4 boyutlu vektör ve matris tipleri ile standart işlemler buna örnek. Bunlar başta standartlaşmazsa aynı kavramın farklı gösterimlerini uzlaştırmakla çok zaman harcanıyor
Hata işleme dışında web geliştirmede etkisi daha az olabilir, ama sayısal hesaplama, grafik ve modellemede sayı dizilerine standart işlemler uygulamak gerektiği için büyük acı yaratıyor
Go’nun web servislerinde iki büyük avantajı var. Birincisi yazının da söylediği goroutine’ler, ikincisi ise yazıda çok ele alınmayan kütüphaneler. Go’da web servisleri için gereken kütüphanelerin çoğu var ve bunlar Google içinde de kullanıldığı için çok sert ortamlardan geçti. Buna karşılık Rust crate’leri daha az olgun ve çoğunda resmî kalite güvencesi yok
Ayrıca Rust, Go’ya kıyasla hâlâ birçok C/C++ kütüphanesine bağımlı; bu yüzden çapraz derleme, yeniden üretilebilir build’ler ve statik binary üretimi kolayca sorun olabiliyor
Go’nun dezavantajı ise çöp toplayıcısının fazla basit olması. Gecikme sıçramaları yaşanırsa, acı verici bir yeniden yazım dışında elde pek çözüm kalmıyor
Listelenenler sadece bunu kullanmanın yaygın yolları ve isterseniz sadece
Boxkullanıp gayet rahat edebilirsiniz. Bu da büyük ölçüdeanyhow::Errorın yaptığı şeye benziyorYine de standart kütüphane tarafında Go’nun Rust’tan çok daha iyi iş çıkardığını düşünüyorum
Rust dilini seviyorum ve onu embedded firmware ile PC uygulamalarında kullanıyorum, ama web backend için hâlâ Python kullanıyorum. Çünkü Rust’ta Django ya da Rails ayarında bir araç seti yok
Flask benzeri şeyler var ama Flask’ın sağlam ekosistemi yok. Go deneyimim az ama web backend için Rust yerine muhtemelen Go seçerdim. Nedeni kütüphane ve framework ekosistemi
Ayrıca genel olarak söylenen nedenlerden ötürü Async Rust’ı pek sevmiyorum. Rust web ekosisteminde neredeyse her şey asenkron kullanım gerektiriyor denecek kadar yakın
io::Error, bunu uygulayan birçok tipten sadece biri; özel bir konumu yok.thiserrorile tanımlanan hatalar da bu trait’i uygularanyhow, bir fonksiyonun döndürebileceği hata tipini API sözleşmesinde ayrıntılı yazmak istemediğinizde rahatça “herhangi bir Error” diyebilmenizi sağlıyorRust ile Go’ya kıyasla deterministik kod yazmak daha kolay; bu yüzden deterministik simülasyon testleri ve özellik tabanlı testler gerektiğinde çok faydalı
Yakın zamanda Go ile Postgres-to-Iceberg veri aynalama aracı https://github.com/polynya-dev/pg2iceberg yazdım ama Go runtime’ı ile boğuşmadan deterministik simülasyon testleri yapmak istediğim için bunu Rust’a port ettim
Yine de ilgili alan böyle bir test seviyesini haklı çıkaracak kadar kritik değilse, her zaman Rust yerine Go’yu seçerim
İlgili yazı: https://www.polarsignals.com/blog/posts/2024/05/28/mostly-ds...
Klişe ve tekrar gibi gelebilir ama Rust’la ilgili en büyük şikâyetim paket yönetimi durumu ve bunun tamamen geliştirici zihniyetinin bir sonucu olduğunu düşünüyorum
Rust tarafındaki kullanılabilirliği seviyorum. Veri tiplerine yönelik fonksiyonel yaklaşım çok zarif. Ancak şu anda bir Rust projesi ile bir Go projesi üzerinde yan yana çalışıyorum ve bağımlılık ağacı tamamen başka bir canavar
Go projesi büyük ölçüde standart kütüphane ile çözülüyor ama Rust projesinde sadece
rusqlite(sqlite),clap(CLI),ratatui(TUI),tauri(GUI) istedim ve buna rağmen bağımlılık sayısı 400’ü aşıyor gibi görünüyor. Özellikletauriaçık ara en büyük suçlu; onu çıkarsam bile neredeyse 100 bağımlılık var, bu da bana çılgınca geliyorBağımlılıkları makul biçimde ele alan, iyi yönetilen Rust crate alternatifleri olsa çok daha iyi olurdu ama henüz bulamadım. Sisteme shai hulud sokmak istemiyorum sadece; ama Rust web tarafındaki insanlar bu açıdan
cargoyunpme benzetmek istiyor gibi görünüyorBu yüzden bağımlılık sayısı gerçekte olduğundan büyük görünüyor. Ayrı crate olsalar da bakımcıları aynı ve çoğu aynı upstream Git deposunun parçası
Yine de genel hisse katılıyorum. Rust’ta 0.x sürümünde, yarı terk edilmiş çok sayıda crate var ve çoğu zaman daha iyi bir alternatif de olmuyor
Sonra da
httplib3ten sonrahttplib4çıkıyorYani demek istediğim, Rust yaklaşımını çok daha fazla tercih ediyorum. Standart kütüphaneye bağımlı olmakla başka bir bağımlılığa sahip olmak arasında benim için büyük fark yok. Sonuçta ikisi de bağımlılık
Standart kütüphane olduğu için kalite daha yüksek ya da bakım daha iyi olacak diye düşünmek bence ayrı bir mesele
Sonunda her şey kaynaklara dayanıyor. Elbette standart kütüphane daha çok kaynak alabilir ama tersine aşırı büyüyüp bakımı imkânsız hâle de gelebilir
rusqlite,clap,ratatui,taurikarşılıklarının hepsini standart kütüphanede sunan bir dil, Java hariç gerçekten var mı emin değilimAyrıca Tauri’nin kendisi 14 crate’ten oluşuyor ve bunların her biri build ağacında görünüyor
https://github.com/tauri-apps/tauri/blob/dev/Cargo.toml
Ratatui de 6 tane
https://github.com/ratatui/ratatui/blob/main/Cargo.toml
Bunu kimse gerçekten “çözebilmiş” değil ve bundan sonra da tek bir çözüm çıkması zor görünüyor
Go’da kütüphane geliştiricisinin semantic versioning kurallarına doğru düzgün uyacağına güvenmek zorundasınız ve sürümü sabitleyemiyorsunuz. Bu benim de kişisel olarak oldukça sinir olduğum bir nokta
Bazı dolaylı çözümler var. Git commit hash’i gibi SHA kullanarak sahte sürüm üretmek ya da bilinen bağımlılık önbelleği olan vendoring’e gitmek gibi. Ama vendoring de önbellek yönetimi sorunlarını beraberinde getiriyor
Hafta sonu Python sanal ortamları kullanmak zorunda kaldım, hiç iyi bitmedi ve neden Python’dan uzaklaştığımı tekrar hatırladım
Perl’in CPAN’i, Java’nın Maven/Gradle’ı, Ruby’nin gems’i, Go’nun dep/glide/vgo/modules’ı, Rust’ın Cargo’su, Node’un npm/yarn’ü... hepsinde benzer dertler var
İşletim sistemlerinde de Redhat’in yum/rpm’i, Debian’ın apt’si, Ubuntu’nun snap’i var. Özellikle snap neden böyle, hiç anlamıyorum
Belki kullanım senaryosunda frontend’i Go olarak bırakıp sadece backend’i Rust yapmak mantıklı olabilir mi diye düşünüyorum
Bu metin hem bir geçiş rehberi hem de Rust savunusu olmaya çalıştığı için tuhaf hissettiriyor
Sonuçta Rust mı Go mu kullanılacağı sorusunda asıl mesele neredeyse tamamen “yönetilen bir runtime istiyor musunuz” noktasına çıkıyor. Bir nesil Rust programcısı, yönetilen runtime’ın kötü olduğuna ve onun olmamasının başlı başına önemli bir özellik olduğuna kendini inandırdı
Ama bu açıkça yanlış. Yönetilen runtime isteyen programlama alanları, istemeyenlerden daha fazla
Bu, böyle durumlarda varsayılan seçimin hep Go olması gerektiği anlamına gelmiyor. Rust’ı tercih etmek için birçok öznel neden de var. Go kullanırken
matchi özlüyorum amatokioyu ve Async Rust’ı özlemiyorumİkisi de problem alanını zorla bükmeyi gerektirmeyen neredeyse her durumda meşru seçimler. Örneğin Go ile Linux kernel modülü yazmaya kalkmak garip bir tercih olurdu
Rust ve Go kavgası bizim alanın tuhaf ve biraz utandırıcı bir kenar kavgası gibi. Sektörün büyük bir kısmı Python ya da Node ile gayet iyi bütün sistemler kuruyor ve hangi statik tipli derlenen dili kullanacağını tartışan ineklerle dalga geçiyor. Gerçek soru Rust’a karşı Go değil, Python’a karşı Rust/Go
Ama genel olarak Rust ve Go tarafının dinamik tipleme kötülüğüne karşı güç birliği yapması gerektiğini düşünüyorum. Tip ipuçlarının artık en iyi uygulama sayılması, aslında bunun bir kusur olduğunun kabulü değil mi?
İyi tip ipuçları olsa bile tip çıkarımının gerisinde kalıyorlar. Tip çıkarımı, tip değiştiğinde çok sayıda kod satırını olduğu gibi bırakmaya izin verirken istem dışı tip değişimlerini de engelliyor
Keşke TS tarafında biraz daha fazla runtime olsaydı. Python’da kıskandığım tek şey, HTTP endpoint’lerinde JSON şema doğrulamasını çok doğal biçimde yapabilmeleri
Zod’dan geçmek zorunda olmak sürekli bir sinir kaynağı ve bence bu, TS ekibinin fazla doktriner olmasının sonucu
LLM yazımının izleri giderek daha incelikli hâle geliyor ama yine de hemen fark ediliyor. Özellikle genuine kelimesi buna örnek
“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” gibi örnekler
Bence yazının tamamı AI üretimi değil ama AI desteği almış gibi duruyor. Eğer öyleyse, yazar bunu genuinely iyi yapmış
Başkalarının buna değinmemesinden, bunun içeriğe ciddi zarar vermediği anlaşılıyor sanırım; ama bunun gittikçe yaygınlaşıp tespit edilmesinin zorlaşması tuhaf geliyor
“Go is clearly working for a lot of people,” cümlesine gelene kadar AI yardımı şüphesine kapılmıştım. Tabii öyle olmayabilir, ben bu konuda çok iyi ayırt edemiyorum
Somut bir ipucundan çok, ironik biçimde bir his meselesi. Bir yazı AI destekli gibi “geldiğinde”, yazı iyi olsa bile ilgimi hemen kaybediyorum
İnsanların kendi düşüncelerini, akıllarına geldiği gibi, doğrudan yazarken daha rahat olmalarını isterdim
it's worth being precise about ...ifadesi, genuine kullanımından çok daha güçlü biçimde AI kokuyorMesela şu paragraf buna örnek: “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.”
Her cümle bir şey söylüyor, her cümle önemli ve kendi görevini yapıyor. Böyle bir yazı, blog yazısından çok son derece profesyonel bir kitapta ya da makalede beklenir
Bu yüzden de ironik biçimde yazıyı daha zor okunur ve daha sıkıcı yapıyor
LLM üretimi metnin klişe ifadelerle dolu olmayacağını zaten beklemiyorum. Ama umarım hepimiz daha iyi bir editörlük sezgisi geliştiririz de aynı sesi tekrar tekrar okumak zorunda kalmayız
Yeni bir projeyse rahatlıkla Rust ile yazılabilir
Ama ortada mevcut, çalışan ve gelir üreten bir sistem varsa, yeniden yazılması gereken parçaları mevcut dilde düzeltip devam etmek en doğrusu
Bildiğiniz bir dil ve güvendiğiniz bir ekiple sistemi küçük ve ölçülebilir adımlarla iyileştirin. Bunun dışı israf niteliğinde bir din savaşı
Benchmark çalıştırmadan önce de Rust’ı seviyordum ama çoğu LLM için Rust ve Go yazma verimliliği farkı beklediğimden çok daha büyüktü. Özellikle başlangıç ortamı sorunlarını düzeltebilen ajan tipi harness’lerde bu daha da belirgindi
Bunu görünce oldukça güçlü bir Rust savunucusuna dönüştüm. Mevcut codebase’den çağrılacak batch processing tool’ları Rust ile yazıp iyi sonuç aldım ama tüm production geçişini henüz denemedim
Yazıda sözü geçen Go sorunlarının, özellikle
nilişleme ile ilgili olanların, Codex ile yapılan kapsamlı code review sayesinde giderek çözüldüğünü düşünüyorum. En iyisi baştan sorun olmaması ama tasarım ve implementasyona ne kadar emek veriliyorsa review ve anlamaya da o kadar emek veren geliştiriciler için bu tür güvenlik açıkları artık giderek isteğe bağlı bir şeye dönüşüyorDil verileri burada: https://gertlabs.com/rankings?mode=agentic_coding
Rust, kullanıcıyı oldukça sıkı biçimde belirli bir raya oturtuyor. Codex her zaman bir şeyleri derlenir hâle getiriyor
Dezavantajı ise bazen deyimsel yaklaşım mümkün olmadığında başarısız olmak gerekirken, bunun yerine derlenen ve isteneni yapan ama aptalca bir implementasyon üretebilmesi
LLM’ler insanlardan daha hızlı kod yazdığı için, göreli olarak derleme bekleme süresi daha büyük hâle geliyor. 100 bin satır ve üzeri gibi belirli bir ölçeğe ulaşan projelerde Rust’ın yaklaşık 10 kat daha yavaş derleme süresi darboğaz olmaya başlıyor
Çekirdek altyapıyı yazıyorsanız bu maliyeti ödemeye değer olabilir ama internete açık olmayan dahili servisler geliştiriyorsanız asıl mesele geliştirme hızı olabilir
Yavaş derlemenin insanların geliştirme hızını da etkilediğini düşünüyorum ama ilginç biçimde geliştiriciler bunu nicel olarak ölçmeye çok nadiren çalışıyor
Eğer asıl engel ayrıntılı ve uzun kodsa, Go 1.28’e gelmesi planlanan şu özellik bunu ciddi biçimde azaltabilir
https://github.com/golang/go/issues/12854#issue-110104883
“Kuruluşun bağımlı olduğu, yüksek uptime gerektiren ve iş için kritik servis” ifadesi komik geldi
Hele bu Rust servisi Kubernetes üzerinde çalışıyorsa daha da komik
Zaten Rust kullanıyorum ve Go deneyimim yok, dolayısıyla bu yazı bana bire bir hitap etmiyor olabilir
Ama takıldığım bir nokta var. Rust’ta veri yarışlarının “derleme zamanında yakalandığını” söylemek en azından biraz abartılı geliyor
Bu ifade, sanki Rust mutex starvation gibi şeyleri ya da diğer eşzamanlılık problemlerini de çözebiliyormuş izlenimi verebilir. Oysa durum böyle değil
Data race’in dar kapsamlı, biçimsel bir terim olduğunu biliyorum ama yine de bunun daha açık yazılabileceğini düşünüyorum