1 पॉइंट द्वारा GN⁺ 2025-10-05 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Ada और Rust इन दो भाषाओं का उपयोग करके Advent of Code समस्याएँ हल करते समय सामने आए अंतर और विशेषताओं की तुलना की गई है
  • सुरक्षा और विश्वसनीयता पर केंद्रित इन दोनों भाषाओं के language design और वास्तविक program लिखने के तरीकों के अंतर का विश्लेषण किया गया है
  • प्रत्येक भाषा की standard library, फीचर के built-in होने या न होने, performance के अंतर, error handling style आदि कई दृष्टिकोणों से भिन्नताएँ सामने आती हैं
  • modularity, generics, loops, error handling आदि के वास्तविक code examples के जरिए, लिखने और संचालन के दौरान आने वाले ठोस अनुभवों को समझाया गया है
  • static typing के तरीके, array handling, और error handling interface के अंतर के कारण development experience में स्पष्ट भिन्नता दिखाई देती है

परिचय और उद्देश्य

  • Advent of Code (आगे AoC) समस्याएँ हल करने की प्रक्रिया में पहले केवल Ada का उपयोग किया गया, लेकिन 2023 से Rust और Modula-2 में भी समाधान लिखते हुए सीधी तुलना संभव हुई
  • पहले से मौजूद Ada-केंद्रित solutions को Rust में स्थानांतरित करते समय दोनों भाषाओं के संरचनात्मक अंतर और उनकी विशिष्ट approach को प्रत्यक्ष रूप से महसूस किया गया
  • code की सुरक्षा, विश्वसनीयता, और language design के दृष्टिकोण से वास्तविक उपयोग में मौजूद अंतर को स्पष्ट करना इसका उद्देश्य है

तुलना में उपयोग किए गए language versions

  • Ada 2022 (ज़रूरत पड़ने पर Spark 2014 के कुछ नियमों का संदर्भ)
  • Rust 2021 (मुख्य तुलना Rust 1.81.0 संस्करण पर आधारित)

बाहर रखी गई सुविधाएँ और तुलना के मानदंड

  • प्रत्येक भाषा की प्रतिनिधि विशेषताएँ (= killer features) मुख्य लेख में संक्षिप्त टिप्पणियों के रूप में उल्लेखित हैं
  • व्यक्तिगत अनुभव और प्रत्येक solution की व्यावहारिक आवश्यकता के आधार पर कुछ सुविधाओं को शामिल नहीं किया गया है
  • जहाँ तक संभव हो, व्यक्तिगत राय से बचते हुए मुख्य विशेषताओं पर ध्यान केंद्रित किया गया है

लेखक की पृष्ठभूमि और दृष्टिकोण

  • Ada और Rust दोनों के उपयोगकर्ता के रूप में लेखक मूल-भाषी नहीं हैं, और C/C++, Pascal, Modula-2 जैसी 1980 के दशक की भाषाओं का अनुभव आधार है
  • परिणामस्वरूप, code style आधुनिक या idiomatic शैली से अलग हो सकती है
  • संभव है कि implementation सर्वोत्तम न हो, और समस्या की प्रकृति के अनुसार कभी सहज तो कभी अपरंपरागत समाधान चुने गए हों

Ada और Rust की positioning

  • Ada अब भी अत्यंत सुरक्षित और उच्च विश्वसनीयता वाली system/embedded development language है, जो code readability को महत्व देती है
  • Rust memory safety और system programming की ताकतों के साथ आती है, और Stack Overflow developer survey में कई वर्षों तक ‘सबसे पसंदीदा भाषा’ के रूप में नामित रही है
  • Ada एक general-purpose high-level language के रूप में, पढ़ने और maintenance के लिए विशेष रूप से उपयुक्त स्पेक्ट्रम प्रदान करती है
  • Rust low-level system program development को लक्ष्य बनाती है, और explicit memory management तथा error/option type-आधारित सुरक्षित programming संस्कृति स्थापित करती है

सुरक्षा और संरचनात्मक विशेषताओं की तुलना

  • Ada

    • ISO standard (कड़ा specification)
    • समस्या की प्रकृति के अनुरूप type (range, digits आदि) घोषित करना आसान
    • array index का संख्यात्मक होना आवश्यक नहीं
    • Spark नाम का और भी कठोर specification उपलब्ध
  • Rust

    • specification आधिकारिक दस्तावेज़ों (Reference) और compiler-केंद्रित है
    • type declaration machine types (जैसे: f64, u32) पर निर्भर करती है
    • array indexing स्वाभाविक रूप से केवल numeric types के साथ होती है

फीचर/built-in उपलब्धता तालिका का मुख्य सार

  • array bounds check, generic containers, concurrency, labeled loops, pattern matching जैसी सुविधाओं के समर्थन में अंतर है
  • Ada में Exception आधारित error handling है, जबकि Rust में Result/Option types के माध्यम से return-based handling अपनाई जाती है
  • Rust की विशिष्टता macros, pattern matching, functional purity के समर्थन आदि में स्पष्ट दिखाई देती है
  • Ada में contract-based design और DBC (Design By Contract) का compile-time verification Spark में समर्थित है
  • memory safety के संदर्भ में Rust और Spark इसे मजबूती से लागू करते हैं, जबकि Ada में Null pointer के उपयोग की अनुमति है

performance और execution time की तुलना

  • सामान्य रूप से Rust की runtime तेज़ लेकिन compile speed धीमी मानी जाती है, जबकि Ada में इसके उलट compile time तेज़ और runtime validation checks के आधार पर कुछ धीमा होने की प्रतिष्ठा है
  • benchmark परिणामों में day24 समस्या पर Rust में f64 type की सीमा के कारण overflow हुआ, जबकि Ada में digits 18 जैसे high-level type specification संभव होने से compiler उपयुक्त machine type चुनकर overflow से बच सका और बेहतर प्रदर्शन दिखा
  • Rust में इसके लिए unstable f128 या external library की आवश्यकता पड़ सकती है, जबकि Ada में compiler specification के अनुरूप type declaration मात्र से बढ़त मिल सकती है

फ़ाइल processing और error handling (Case Study 1)

Ada में फ़ाइल processing

  • मूल रूप से Ada.Text_IO का उपयोग
  • फ़ाइल को explicit रूप से खोलना, line-by-line पढ़ना, इच्छित range, position-based line handling आदि अपेक्षाकृत सहज रूप से संभव
  • error होने पर स्पष्ट error message के बजाय exception के रूप में handling होती है, और function signature में error की संभावना प्रकट नहीं होती

Rust में फ़ाइल processing

  • std::fs::File और BufReader का उपयोग
  • फ़ाइल खोलते समय Result type के रूप में return, जिससे error की संभावना स्पष्ट रूप से सामने आती है
  • सीधे character index access का समर्थन नहीं; अनिवार्य रूप से Iterator के जरिए processing करनी होती है
  • map, filter, collect, sum जैसे functional/iterative tools केंद्रीय हैं, और विभिन्न macros (जैसे: include_str!) का समर्थन है
  • return type में error को explicit घोषित करके function स्तर पर error propagation की स्पष्टता मिलती है

modularity और generics (Case Study 2)

Ada की modularity

  • package आधारित स्पष्ट specification (interface) और implementation का पृथक्करण
  • modularization मजबूत करने के लिए sub-packages और use/rename syntax के संयोजन से readability को समायोजित किया जा सकता है
  • package के generic समर्थन से type/constant/पूरे sub-package तक को सामान्यीकृत किया जा सकता है

Rust की modularity

  • mod/crate प्रणाली से module संरचना बनती है, और specification व implementation का विभाजन documentation generator द्वारा स्वचालित हो जाता है
  • pub/private access specifier के जरिए घोषणात्मक access control
  • use/as से import और renaming का संयोजन
  • built-in test support के कारण code के भीतर सीधे test module घोषित कर build और automatic execution संभव है

generics

  • Ada में केवल package/procedure स्तर के generics समर्थित हैं (केवल type स्तर पर नहीं)
  • Rust में type स्वयं पर generics लागू किए जा सकते हैं (template-आधारित)
  • Ada में type range जैसी अतिरिक्त विशेषताओं को range type, subtype आदि से स्पष्ट रूप से व्यक्त किया जा सकता है, जबकि Rust में instance constants का उपयोग होता है

enum type की तुलना (Case Study 3)

  • Ada में संक्षिप्त declaration के साथ स्वतः discrete, ordered, loop/array index में उपयोग की सुविधा मिलती है
  • Rust enum की declaration भी मिलती-जुलती है, लेकिन pattern matching और iteration आदि के लिए अधिक explicit approach की आवश्यकता होती है

निष्कर्ष

  • high-level specification types, verifiability, और runtime checks जैसे पहलुओं में Ada अधिक कठोर नियंत्रण प्रदान करती है
  • functional programming style, macro programming, compiler-assisted error handling जैसे क्षेत्रों में Rust developer convenience और safety दोनों में बढ़त रखती है
  • व्यावहारिक समस्या-समाधान में Ada पुराने code की compatibility और maintenance में मजबूत दिखती है, जबकि Rust आधुनिक developer tooling ecosystem तथा safety/parallelism support में लाभ देती है

1 टिप्पणियां

 
GN⁺ 2025-10-05
Hacker News राय
  • यह अफ़सोस की बात है कि Ada में बहुत अच्छे आइडिया होने के बावजूद, उसका उपयोग ज़्यादातर केवल उन क्षेत्रों में हुआ जहाँ safety बेहद महत्वपूर्ण है। खासकर numeric types की value range सीमित करने वाली सुविधा कुछ bugs को रोकने में बहुत उपयोगी है। Spark Ada सीखना भी आसान था और SIL 4 (सबसे कड़ा software safety standard) अनुरूप software development में लागू करना भी। पिछले कई दशकों में software industry ‘growth first, stability later’ की दिशा में भागती रही, लेकिन अब फिर से safe software development की ओर लौटने का रुझान महसूस हो रहा है। उम्मीद है कि इस दौरान जमा हुए safety lessons आगे चलकर बेहतर भाषाओं में बदलें। हक़ीक़त यह रही कि अच्छे आइडिया अक्सर minor languages में छिपे रहकर गायब हो जाते हैं
    • software development लंबे समय तक करने पर महसूस होता है कि ‘reinventing the wheel’ सच में बहुत होता है। Ada और Rust दोनों safety के पीछे हैं, लेकिन उसकी परिभाषा और दायरा अलग है। Rust बहुत केंद्रित रूप में एक महत्वपूर्ण तरह की safety को बहुत ताक़त से साधता है, जबकि Ada safety की ज़्यादा व्यापक और ठोस परिभाषा रखता है। जब मैंने 90 के शुरुआती दशक में Ada सीखी थी, तब सबसे आम आलोचना यही थी कि भाषा इतनी बड़ी और जटिल है कि development speed धीमी हो जाती है (उस समय Ada 83 validated compiler की कीमत प्रति व्यक्ति आज के हिसाब से लगभग 20,000 डॉलर थी)। लेकिन समय बदल गया, और अब लोग मानते हैं कि Rust जैसी बड़ी और जटिल भाषा वास्तव में safe concurrency programming के लिए ज़रूरी हो सकती है
    • Nim भी Ada और Modula से प्रेरित होकर type value range सीमित करने वाले subrange को support करता है
      type
        Age = range[0..200]
      
      let ageWorks = 200.Age
      let ageFails = 201.Age
      
      compile करते समय यह error आता है कि 201 को Age type में convert नहीं किया जा सकता
      Nim Subranges विवरण लिंक
    • Ada (GNAT के आधार पर) compile time पर physical units/dimensional analysis, यानी unit checking, को support करता है। engineering के काम में यह बहुत practical है, इसलिए हैरानी होती है कि दूसरी भाषाएँ ऐसी महत्वपूर्ण सुविधा केवल third-party libraries के ज़रिए ही क्यों देती हैं
      संबंधित दस्तावेज़
    • C++ में भी value range सीमित numeric types को code से आसानी से बनाया जा सकता है (standard library में नहीं है, लेकिन खुद implement करना बहुत आसान है)। कुछ safety checks runtime के बजाय compile time पर किए जा सकते हैं। अच्छा होता अगर ऐसी सुविधाएँ हर भाषा standard रूप में देती
    • Ada में जो बात सबसे ज़्यादा खलती है, वह OOP के प्रति उसका स्पष्ट approach है। ज़्यादातर भाषाएँ OOP concepts को “class” नाम के एक ही ढेर में डाल देती हैं, लेकिन Ada message passing, dynamic dispatch, subtyping, generics आदि को अलग-अलग चुनकर लागू करने देता है। मुझे यह बहुत पसंद था कि ये सारी सुविधाएँ कितनी खूबसूरती से एक-दूसरे के साथ जुड़ती हैं
  • लेखक Ada में formal specification होने और Rust में न होने जैसी बातों को अंतर के रूप में बताते हैं, लेकिन व्यवहार में user के नज़रिए से language adoption/ecosystem (tooling, libraries, community) उससे ज़्यादा महत्वपूर्ण हैं। Ada aerospace/safety क्षेत्रों में सफल रहा है और AOC या embedded low-level काम के लिए उपयुक्त भी है, लेकिन असली projects (distributed systems, OS components आदि) में data formats, protocols, IDE support और सहकर्मियों के साथ collaboration जैसी चीज़ें ज़्यादा मायने रखती हैं। अंततः भाषा चुनते समय यही पर्यावरणीय पहलू निर्णायक बनते हैं
    • हाल में Rust को भी Ferrocene से Ada specification style को संदर्भ बनाकर एक spec document दान में मिला है। यह public है, इसलिए देखा जा सकता है
      Rust स्पेक
    • Rust और Ada दोनों ही सख़्त अर्थ में “formal specification” (ऐसा दस्तावेज़ जिसे machine से prove किया जा सके) के मामले में कमज़ोर हैं। Spark Ada में भी language semantics से जुड़ी धारणाएँ मौजूद हैं, लेकिन यह भी पूरी तरह formal और machine-readable नहीं है
    • aircraft control software के developers भी शायद यही कहेंगे कि “अगर कोई चीज़ वास्तविक operational environment में महत्वपूर्ण नहीं है, तो हाँ, हमारा process ज़्यादा भारी लगेगा।” वास्तव में safety-critical क्षेत्रों में Ada जैसी कड़ी भाषा और process ही standard हैं
  • यह दिलचस्प लगा कि type-related features में Rust आगे होने के बावजूद, code readability में कई बार Ada बेहतर निकलता है। comparison लेख में compiler speed का ज़िक्र नहीं था, लेकिन Ada को complex language मानना शायद अतीत की बात है; आज के Rust से तुलना करें तो ज़रूरी नहीं कि वह उतना जटिल लगे। यह लेख पढ़कर Ada में कोई वास्तविक project करके देखने का मन हुआ
    • “type-related कमियाँ” से ठीक क्या मतलब है, यह जानने की जिज्ञासा है। मेरे अनुभव में Ada का type system बेहद expressive है। user-defined value-range types, arbitrary enum values से index होने वाले arrays, type-specific operator definitions, compile/runtime checks, preconditions/postconditions जैसी कई सहायक सुविधाएँ type में जोड़ी जा सकती हैं। discriminated records और representation clauses भी हैं। यह कमी नहीं, बल्कि बहुत शक्तिशाली feature set है
  • Ada और Rust के string अंतर पर बात करना चाहूँगा। Ada में 1980 के शुरुआती design समय में char array को ही “string” माना गया था, इसलिए उसे सीधे byte array की तरह index करना आसान है। Rust को मूल रूप से Unicode-aware बनाकर डिजाइन किया गया था, इसलिए Rust strings UTF-8 encoded, यानी असली ‘text’ हैं। इस वजह से Ada में array जैसा random indexing संभव है, जबकि Rust में string की अवधारणा अलग है; चाहें तो उसे byte array में बदला जा सकता है
    • Ada की built-in Unicode string आम तौर पर UTF-32 array होती है। Rust के विपरीत, यह सीधे UTF-8 literals नहीं देती; 8/16/32-bit arrays से conversion करना पड़ता है
    • Rust strings में indexing संभव है। लेकिन Rust strings को सामान्य arrays की तरह नहीं, बल्कि substring slices के रूप में ज़्यादा इस्तेमाल करता है। अगर किसी character के बीच में काटकर index किया जाए, तो panic होता है (यानी Unicode encoding boundary तोड़ने वाला मामला)। AoC जैसी स्थिति में जहाँ हमेशा ASCII ही इस्तेमाल होता है, वहाँ [u8] या str::as_bytes method से byte slice लेना सही है
  • लेखक का यह कहना अजीब लगा कि Rust “concurrent programming को मूल रूप से support नहीं करता।” Rust में thread functionality language में built-in है, और कई मामलों में async से इस्तेमाल करना आसान भी है। समस्या तभी आती है जब बहुत बड़ी संख्या में threads चाहिए हों और resources की सीमा आ जाए; अधिकांश software के लिए built-in threads काफ़ी हैं
    • (Rust न इस्तेमाल करने वाले की हैसियत से सच में जिज्ञासा है) Rust में thread और async के बीच cancel handling कैसे अलग होती है, और दूसरी भाषाओं के async से इसका क्या फ़र्क है? C++, Python और C# में async cancellation thread से कहीं बेहतर लगी थी। सुना है कि Rust में इस तरह की cancel/interrupt handling exceptions से नहीं होती, इसलिए शायद थोड़ा और कठिन है; वास्तविक काम के अनुभव जानना चाहूँगा। Ada में यह cancellation handling कैसी है, यह भी सुनना चाहूँगा
    • Tokio जैसे work-stealing scheduler केवल कई threads चलाने की तुलना में व्यवहार में कितना तेज़ होता है, इसकी सीमा कहाँ है, यह जानना चाहता हूँ। जैसे simple arrays (उदाहरण: VecMap) कम elements पर तेज़ होते हैं, लेकिन एक सीमा पार होने पर दूसरी data structures ज़्यादा efficient हो जाती हैं, कुछ वैसा ही लगता है। वास्तव में किस बिंदु से work-stealing फ़ायदेमंद हो जाता है, यह जानने की जिज्ञासा है
    • व्यावहारिक रूप से Async का उपयोग करने की मुख्य वजह यह है कि इस्तेमाल होने वाला third-party crate ही Async होता है। (उदाहरण: Reqwest को Tokrio चाहिए) high-level app development में अगर आप non-Async पर अड़े रहें, तो अंततः सीमा आ ही जाती है
    • जिन environments में platform का thread support कमज़ोर है (WASM, embedded आदि), वहाँ Async उल्टे ज़्यादा उपयुक्त हो सकता है। लाखों लोग एक साथ ब्लॉग पर आएँ, यह अक्सर अवास्तविक है; ऐसे संदर्भों में Async की ज़रूरत को बढ़ा-चढ़ाकर बताना कुछ अतिशयोक्ति जैसा लगता है
  • यह जानकर दिलचस्प लगा कि Ada का open source compiler भी है। पहले मुझे लगता था कि केवल proprietary compilers ही हैं, इसलिए मैंने Ada में कभी खास रुचि नहीं ली; अब दोबारा देखना चाहिए
    • GNAT compiler को आए 30 साल से ज़्यादा हो चुके हैं, और एक समय GPL runtime exception न होने के कारण यह ग़लतफ़हमी थी कि compile किए गए output भी GPL होने चाहिए, लेकिन अब यह issue हल हो चुका है
    • GNAT 90 के दशक से GCC पर आधारित बनता आया है, और कुछ universities में real-time programming जैसे practical courses में GNAT का सीधे उपयोग भी किया गया। Ada को programming की शुरुआती भाषा के रूप में इस्तेमाल करने की कोशिश की थी, लेकिन बाद में जल्दी Pascal और C++ पर चला गया था
  • 3D printing क्षेत्र में जिन projects ने ध्यान खींचा, उनमें हाल में Prunt नाम का printer control board और firmware था। firmware को Ada में बनाना काफ़ी अलग और conceptually उपयुक्त choice लगती है
    Prunt होमपेज
    Prunt GitHub
  • Case Study 2 के अंत में कहा गया है, “अगर client को SIDE_LENGTH जानना हो, तो उसे return करने वाला function जोड़ो”, लेकिन function के बजाय pub const SIDE_LENGTH: usize = ROW_LENGTH; जैसी constant declaration ज़्यादा सीधी विधि है
  • मैं इस दावे से सहमत नहीं हूँ कि दोनों भाषाएँ stack-centered programming को बढ़ावा देती हैं। Ada तो उल्टे static allocation को सक्रिय रूप से प्रोत्साहित करता है
  • यह थोड़ा अजीब लगा कि Ada में array index arbitrary type हो सकता है, इसे एक बड़े फ़ायदे की तरह पेश किया गया। लगभग हर भाषा में dictionary (hash map) standard library में होती है, और Rust में तो दो तरह की भी मिलती हैं
    • यहाँ बात language-built-in arrays की हो रही है। उदाहरण के लिए, Ada में अगर “eggs” नाम के array का index BirdSpecies type हो, तो eggs[Robin], eggs[Seagull] ठीक हैं, लेकिन eggs[5] मान्य नहीं होगा। Rust में भी आप अपनी इच्छित data structure (जैसे Index<BirdSpecies> implementation) बना सकते हैं, और eggs[Robin] चलेगा लेकिन eggs[5] error देगा। बस Rust में language level पर इसे सीधे “array” के रूप में support नहीं किया जाता। Ada की तरह जब “user-defined type को integer का subset बनाकर declare” किया जा सकता है, तब यह indexing सच में बहुत उपयोगी बनती है। Rust में अभी pure user-defined types के रूप में range-limited integers जैसी चीज़ें नहीं बनाई जा सकतीं (अंदरूनी तौर पर केवल NonZeroI16 आदि उपलब्ध हैं)। अगर Rust इस स्तर तक support दे, तो वाकई बहुत अच्छा होगा
    • Ada में hash maps और sets जैसी संरचनाओं का भी built-in support है। Ada containers standard (A.18 section देखें)। array index type में सामान्य ‘contiguous values’ (जैसे 0~N-1) range का उपयोग dense maps या contiguous memory access वाले मामलों में dictionary की तुलना में बहुत तेज़ और cache-efficient होता है, इसलिए यह एक बड़ा लाभ है
    • Ada में array index type restriction (subtype) dictionary से संरचनात्मक रूप से पूरी तरह अलग अवधारणा है। यह language level पर array index के value kinds तक सीमित कर सकता है