- लगभग 3 साल पहले जब बाइटकोड VM और garbage collector को Zig और unsafe Rust में सीधे लिखा था, तब Zig की मानव-अनुकूल ergonomics बेहतर थी, लेकिन coding agent के दौर में आते-आते यह बढ़त लगभग महत्वहीन हो गई है
- Zig की मुख्य सुविधाओं से मिलने वाली 1.5~5 गुना developer productivity बढ़त, Rust-आधारित coding agent द्वारा मिलने वाली 100 गुना productivity बढ़त के सामने दब जाती है
- Zig के allocator interface, arbitrary bit-width integers, packed struct, comptime जैसी मुख्य सुविधाएं सब तब सबसे ज़्यादा चमकती हैं जब इंसान खुद सीधे code लिख रहा हो
- Rust का type system bounded polymorphism और invariants को enforce करके agent की गलतियों को compile time पर रोकने में अधिक प्रभावी है
- जब agent द्वारा बनने वाले code की मात्रा 100 गुना बढ़ गई हो, तब Rust की memory safety guarantees Zig की तुलना में निर्णायक लाभ बन जाती हैं
मुख्य बदलाव
- Zig के पास unsafe code की ergonomics में बड़ा फायदा था, लेकिन जैसे-जैसे इंसानों द्वारा सीधे code लिखने का अनुपात घटा, उस लाभ की व्यावहारिक उपयोगिता भी कम हो गई
- Zig की सुविधाओं से मिलने वाली 1.5~5 गुना मानव developer productivity बढ़त Rust में coding agent के 100 गुना लाभ के सामने फीकी पड़ जाती है
- Zig की कई प्रमुख सुविधाएं हाथ से code लिखने वाले इंसान की सुविधा बढ़ाती हैं, लेकिन coding agent के लिए यह अंतर बहुत महत्वपूर्ण नहीं है
- लगभग 3 साल पहले Zig और unsafe Rust में बाइटकोड VM और garbage collector लिखते समय, unsafe code लिखने का अनुभव Zig में बेहतर लगा था
- 2026 के हिसाब से भी Zig एक अच्छी भाषा है, लेकिन अब Rust ज्यादा पसंद की जाने वाली और coding agent के साथ ज्यादा मेल खाने वाली भाषा बन गई है
Zig का allocator interface
- Zig का allocator interface खास code paths को optimize करने के लिए arena, stack fallback जैसे specialized allocator को आसानी से लागू करने देता है
- अगर user input की एक line पढ़नी हो, तो input length सैद्धांतिक रूप से unlimited हो सकती है, इसलिए heap allocator चाहिए, लेकिन असल input आमतौर पर छोटे search term या path होते हैं, जो 1KB से काफी छोटे होते हैं
std.heap.stackFallback(256, heap_allocator) fixed-size buffer को stack पर रखता है, और input overflow होने पर ही heap पर जाता है, जिससे सामान्य मामलों में heap allocation के बिना काम हो जाता है
- पहले Rust में Zig के
Allocator interface जैसी सुविधा नहीं थी, इसलिए custom allocator वाला Vec<T> चाहिए होता तो standard library implementation को copy करके modify करना पड़ता था
- Bumpalo के collection source standard collections को fork करके bump allocator से जोड़ने के रूप में थे
- Rust nightly में कुछ समय तक
Allocator trait था, और अब यह काफी अच्छा दिखने वाले स्तर तक पहुंच गया है
- Rust का
Allocator trait-आधारित है, इसलिए यह static dispatch इस्तेमाल करता है, जबकि Zig का allocator vtable-आधारित होता है
- Zig की तरह data structure को allocator parameter के आधार पर design करने की पूरे community में बनी हुई परंपरा Rust में नहीं है, लेकिन AI के लिए code copy करके बदलना आसान होने से यह सीमा अब कम महत्वपूर्ण हो गई है
arbitrary bit-width integers और packed struct
- Zig के arbitrary bit-width integers और
packed struct, data-oriented design शैली की CPU cache optimization, tagged pointer, NaN boxing, bitflags जैसे कामों को आसान बना देते हैं
- जब Obj-C API को Metal के साथ Objective-C runtime C API के जरिए इस्तेमाल किया जाता है, तब
id aligned heap object pointer नहीं भी हो सकता, बल्कि tagged pointer हो सकता है
- अगर alignment मानने वाले code को tagged
NSNumber दे दिया जाए, तो UB हो सकता है, इसलिए यह सस्ते में जांचना जरूरी है कि यह “heap pointer है या tagged immediate”
- सरल किए गए Objective-C tagged pointer layout में सबसे निचला 1 bit यह दिखाता है कि यह “heap pointer नहीं” है, अगले 3 bit class slot पहचानते हैं, और बाकी 60 bit payload होते हैं
- Zig में
enum(u3) और packed struct के जरिए class: TaggedClass, payload: u60 की तरह bit layout को सीधे type के रूप में व्यक्त किया जा सकता है
- Zig में
@bitCast से raw u64 और ObjcTaggedPointer के बीच जाया जा सकता है, और is_ns_number में is_tagged तथा .ns_number class की जांच की जा सकती है
- Rust के समकक्ष code में
ObjcTaggedPointer(u64) के भीतर TAG_MASK, CLASS_MASK, CLASS_SHIFT, PAYLOAD_SHIFT जैसे constant रखे जाते हैं, और बनाने के समय OR operation तथा access के समय mask apply किया जाता है
- Rust में class slot कोई वास्तविक type नहीं बल्कि
u64 constant होता है, और हाथ से लिखने का यह तरीका Zig की तुलना में कम ergonomic है
- Rust में bitfield या bitflags जैसे crate इस्तेमाल करना बेहतर रहता है, लेकिन दोनों proc macro पर निर्भर हैं और Zig के
packed struct जितने अच्छे नहीं लगते
- coding agent होने पर ऐसे code को हाथ से लिखना झंझट लगता है, यह समस्या काफी कम हो जाती है
comptime की बदलती अहमियत
- Zig का comptime इसकी सबसे चमकदार सुविधा है, और कुछ जटिल dependent type भाषाओं को छोड़ दें तो Zig जितनी अच्छी compile-time evaluation देने वाली भाषाएं बहुत कम हैं
- वास्तविक उपयोग में comptime की उतनी कमी महसूस नहीं हुई, और इसका लगभग 95% उपयोग parameterized type वाले generic data structure बनाने में गया
fn ArrayList(comptime T: type) type जैसा pattern, जो type लेकर items: []T, capacity: usize, allocator: Allocator वाले struct type को लौटाता है, इसका प्रमुख उदाहरण है
- Rust का type system, Zig-शैली के comptime generics के बड़े हिस्से की जगह ले लेता है, और ज्यादा invariants enforce कर सकता है
- बाकी लगभग 5% मामलों में comptime न होना असुविधाजनक है, और भरोसेमंद विकल्प के रूप में सिर्फ codegen बचता है
- गेम development के दौरान अगर tool से generate हुआ hitbox geometry data hardcode करके data structure में डालना हो, तो Rust में Claude से Rust file generate करने वाली script लिखवानी पड़ती है
- फिर भी compile-time evaluation की वास्तव में बार-बार जरूरत नहीं पड़ती
Rust type system के फायदे
- Rust का type system, Zig के comptime की तुलना में ज्यादा मूल्यवान सौदा माना गया है, खासकर bounded polymorphism के लिए traits/typeclasses वाले क्षेत्र में यह मजबूत है
- Zig में उसी स्तर का bounded polymorphism लागू करना बहुत कठिन है
- Rust का type system ज्यादा invariants enforce कर सकता है, जिससे coding agent की आम गलतियों को रोकने में मदद मिलती है
- गेम code में euclid crate का इस्तेमाल, graphics programming की आम समस्या coordinate space confusion को रोकने के लिए किया जाता है
Point<Screen> या Point<World> जैसे हर coordinate space के लिए अलग type बना दिए जाएं, तो world coordinate और screen coordinate को गलती से मिलाना compile stage पर ही रुक जाता है
WorldPoint, WorldVector, ScreenPoint को अलग type रखने पर एक ही space के point और vector का addition अनुमत रहता है
Translation2D::<f32, WorldSpace, ScreenSpace> के जरिए world space से screen space में स्पष्ट conversion किया जा सकता है
- इसके उलट
let bad: ScreenPoint = player; की तरह WorldPoint को सीधे ScreenPoint में assign करने वाला code स्वीकार नहीं किया जाता
memory समस्याओं से कम जूझने का असर
- अगर coding agent 100 गुना ज्यादा code लिखना संभव बना दे, तो Zig code में memory समस्याओं की समीक्षा करने वाली मात्रा भी 100 गुना बढ़ जाती है
- अगर formal verification न हो, तो bug ढूंढने के लिए देखे जाने वाले search space की surface area बहुत बड़ी हो जाती है
- अभी की तरह generate होने वाले code की मात्रा बढ़ी हुई स्थिति में Rust ज्यादा आकर्षक लगता है
- Rust का पारंपरिक tradeoff यह था कि borrow checker से अपरिचित होने पर developer productivity घटती है, लेकिन coding agent होने पर इस नुकसान की अहमियत काफी कम हो जाती है
- Rust में
unsafe इस्तेमाल करने पर भी coding agent से miri जैसे tool चलवाकर यह जांचा जा सकता है कि UB तो नहीं हो रहा, या Rust के aliasing नियम टूट तो नहीं रहे
निष्कर्ष
- Zig अब भी याद आने वाली और अच्छी भाषा है
- 2026 के काम करने के तरीके में Rust ज्यादा पसंद किया जाता है, और coding agent के साथ इसकी अनुकूलता भी बेहतर है
1 टिप्पणियां
Lobste.rs की राय
मेरे पुराने टीम लीड का यह काफ़ी मज़बूत मानना था कि कॉपी-पेस्ट किया हुआ कोड हमेशा बुरी चीज़ नहीं होता
DRY सिद्धांत की वजह से यह सहज रूप से ग़लत या विवादास्पद लगता था, लेकिन वह बहुत व्यावहारिक इंसान थे और यह सिद्धांत ज़्यादातर बड़े test codebase पर लागू करते थे
उनका तर्क था कि चालाक shared interface को ज़बरदस्ती गढ़ने के बजाय, साधारण लेकिन बड़ा और ज़्यादा दोहराव वाला codebase maintain करना आसान हो सकता है
आजकल LLM इस्तेमाल करते हुए मैं भी फिर उसी सोच पर लौट रहा हूँ, और अब इसे software के ज़्यादा महत्वपूर्ण हिस्सों पर भी लागू कर रहा हूँ
code generation तेज़ है, और LLM भी शायद सरल लेकिन दोहराव वाले codebase में ज़्यादा सही बैठते हैं
दोहराव घटाने के लिए tests में बहुत ज़्यादा abstraction डालने से tests को समझना मुश्किल हो जाता है, और वे बारीक तरीक़े से ग़लत भी हो सकते हैं
इससे भी बुरा यह है कि अगर आप test किए जा रहे code की abstraction दोबारा इस्तेमाल करते हैं, तो test भी उसी तरह ग़लत हो सकता है जैसे वह code
और application code के विपरीत, tests लगभग मुफ़्त में “compose” हो जाते हैं
अगर आपने test harness को गंभीर रूप से ख़राब नहीं किया है, तो tests को मनचाहे जोड़ने या हटाने से दूसरे tests पर असर नहीं पड़ता, और integration friction भी नहीं होता, इसलिए duplication से बचने की एक वजह कम हो जाती है
tests में मैंने इसे DAMP के रूप में व्यक्त होते देखा है: “Descriptive and Meaningful Phrases”, यानी ऐसा सिद्धांत जो uniqueness से ज़्यादा readability पर ज़ोर देता है
यह सिद्धांत मिलते-जुलते code को दोहराने जैसी duplication पैदा कर सकता है, लेकिन tests को ज़्यादा साफ़ तौर पर सही दिखने में मदद करता है
https://testing.googleblog.com/2019/12/…
Go कम्युनिटी में भी ऐसा ही एक कथन है: “थोड़ी copy, थोड़ी dependency से बेहतर है” https://go-proverbs.github.io/
live coding शेयर करने के लिए मैं उनका आभारी हूँ
अगर मुझे सही याद है, तो किसी चीज़ की शुरुआत करते समय वह अक्सर उस code को ढूँढ़ते थे जो सबसे ज़्यादा मिलता-जुलता हो, उसे पूरा कॉपी करते थे, और फिर वहीं से बदलते थे
मुझे लगता था, “क्या आप बैठकर यह नहीं सोचेंगे कि दोनों की shared abstraction क्या है?” लेकिन वह बस कॉपी-पेस्ट करके आगे बढ़ जाते थे, और मुझसे कहीं ज़्यादा productive थे
Bun के Zig → Rust AI rewrite के timing को देखते हुए यह दिलचस्प है https://xcancel.com/jarredsumner/status/2053063524826620129#m
इनमें से 75 ऐसे थे जो destructor, move semantics, और borrow checker वाली भाषा में compile ही नहीं होते
यानी deploy होने वाली हर तीन PRs में से एक का मतलब हुआ “error path में free करना भूल गए”
108 में से लगभग 88 Zig में हैं, और C++ वाली लगभग 14 ज़्यादातर reference cycle और GC concurrency race जैसी residual category हैं जो किसी भी भाषा में बची रह सकती हैं
इसलिए Zig→Rust का फ़र्क़ वास्तविक है, Zig के bugs ठीक वही किस्म के हैं जिन्हें destructor और ownership से ठीक किया जा सकता है, और C++ वाला हिस्सा पहले ही काफ़ी नीचे पहुँच चुका है
ज़्यादा मज़बूत compile-time guarantee के बिना यह बस cat-and-mouse game बना रहेगा
प्रस्ताव यह है कि सबसे बड़ी bug category को अलग-अलग patch करते रहने के बजाय उसे संरचनात्मक रूप से हटाया जाए
– bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
“बाक़ी 5% मामलों में comptime के बिना जीवन दर्दनाक है, और equivalent result तक reliably पहुँचने का एकमात्र तरीका code generation है” — इस हिस्से में लेखक का मतलब क्या है, यह साफ़ नहीं है
क्योंकि वह procedural macros के बारे में कुछ भी नहीं कहते
इसे ठीक से बनाना थोड़ा झंझट वाला हो सकता है, लेकिन इससे बहुत कुछ किया जा सकता है
मुझे लगता है code generation की बदनामी कुछ हद तक बेवजह है
build.rsscript के ज़रिए मैंने कई झुंझलाहट वाली समस्याएँ code generation से हल की हैं, और यह अच्छा काम करता हैहाँ, हो सकता है बाद में मुझे पछतावा हो
लेख का मुख्य तर्क लगभग यह लगता है
Rust एक अच्छी भाषा है, यह सही है, लेकिन फिर भी यह कुछ ज़्यादा ही है
यह coding agent का विज्ञापन जैसा लगता है
यह उसी लेखक के linked लेख से आई बात है: graphics programming में बहुत आम गलती coordinate spaces को गड़बड़ा देना है, और type system इतना शक्तिशाली हो सकता है कि किस coordinate space और transformation को वैध माना जाए, इसे types में व्यक्त कर सके
यही बात currencies, SI units बनाम yard-pound units में distance और weight, validated strings बनाम user-provided strings, और secret values पर भी लागू हो सकती है
और अगर ठीक से प्रबंधन किया जाए, तो state types असंभव या sound न होने वाली states को रोक सकते हैं
लेकिन व्यक्तिगत रूप से Rust में मुझे सबसे ज़्यादा जिस सुविधा की चाह है, वह है data races का पूर्ण उन्मूलन
managed languages में भी data races होते हैं
“बस Go इस्तेमाल करो” — Go में हर चीज़ thread के बीच reference के ज़रिए mutable है, ऊपर से slice gymnastics भी है
यहाँ तक कि पहले एकदम खिलौना भाषा मानी जाने वाली, सबसे pure और safe पक्ष की JavaScript में भी हर
awaitएक संभावित race हैeverything-is-an-EventEmitter pattern की बुराई को एक तरफ़ रख भी दें, तब भी
इसलिए हाँ, सही बात है। अगर सिर्फ़ GC होता… 🤫
लगता है कि यह बात के मूल हिस्से को थोड़ा छिपा रहा है
coding agents Python और JavaScript भी बहुत अच्छी तरह संभालते हैं
वे Rust से बेहतर हैं या नहीं, यह व्यक्तिपरक बहस का विषय है, लेकिन फिर भी मैं कई कामों के लिए उन भाषाओं को नहीं चुनूँगा
मैं सोचता हूँ, समस्या Zig के features के बार-बार बदलने में है, या बस इसलिए कि यह नई भाषा है और AI training data अभी गड़बड़ है
मेरा एहसास है कि Zig, Rust की तुलना में लिखने में कठिन लेकिन पढ़ने में आसान है
AI युग में लिखे जाने वाले code से ज़्यादा पढ़े जाने वाले code की मात्रा बढ़ेगी, इसलिए मैं Zig की ओर झुकता हूँ
अभी जो code की मात्रा generated हो रही है, उसे देखते हुए यह कहना कि Rust ज़्यादा आकर्षक है, बस पहला क़दम है
जैसे-जैसे computer ज़्यादा code लिखेंगे, वैसे-वैसे ज़्यादा formal languages फ़ायदेमंद होंगी
यह dynamic typing बहस के एक और चरण जैसा लगता है
जैसे, “dynamic typing इंसानों के लिए आसान है, तो फिर मशीनों के लिए वही बात तीन बार क्यों लिखें?”
types, lifetimes… और क्या-क्या चीज़ें हैं जिन्हें मशीनें लिखने और consume करने में ज़्यादा आसानी महसूस करती हैं
मैं सोचता हूँ कि भविष्य में जिन भाषाओं में computer code लिखेंगे, उनमें इंसानों के लिए सीधे coding करना कितना कठिन हो जाएगा