यह पोस्ट
उदाहरणों के साथ तुलना रस्ट और C ++ पर आधारित है
और मतभेदों का वर्णन करने वाले डी कोड के साथ वहां दिए गए उदाहरणों को पूरक करता है।
सभी उदाहरण DMD v2.065 x86_64 संकलक का उपयोग करके संकलित किए गए थे।
टेम्पलेट प्रकारों की जाँच करना
Rust में टेम्प्लेट्स को त्वरित होने से पहले शुद्धता के लिए जाँच की जाती है, इसलिए टेम्पलेट में त्रुटियों के बीच एक स्पष्ट अलगाव होता है (जो कि किसी अन्य / पुस्तकालय टेम्पलेट का उपयोग करने पर नहीं होना चाहिए) और तात्कालिक स्थान पर, जहाँ आप सभी के लिए आवश्यक आवश्यकताओं को पूरा करना है। टेम्पलेट में वर्णित प्रकार:
trait Sortable {} fn sort<T: Sortable>(array: &mut [T]) {} fn main() { sort(&mut [1,2,3]); }
डी एक अलग दृष्टिकोण का उपयोग करता है: आप टेम्प्लेट, फ़ंक्शंस, संरचनाओं पर गार्ड लटका सकते हैं, जो फ़ंक्शन को ओवरलोड सेट में शामिल होने से रोक देगा यदि टेम्पलेट पैरामीटर में एक निश्चित संपत्ति नहीं है।
import std.traits;
कंपाइलर इस प्रकार शिकायत करेगा:
source / main.d (27): त्रुटि: टेम्पलेट main.sort तर्क प्रकारों से फ़ंक्शन को घटा नहीं सकता है! () (int []), उम्मीदवार हैं:
source / main.d (23): main.sort (T) (T [] array) अगर (isFloatingPoint! T)
हालाँकि, आप रूस्ट के लिए लगभग समान "समाधान" व्यवहार प्राप्त कर सकते हैं:
template Sortable(T) {
संकलक आउटपुट:
source / main.d (41): एरर: स्टैटिक असॉर्स "Sortable is not for int। स्वैप फ़ंक्शन परिभाषित नहीं है। "
source / main.d (44): यहाँ से तात्कालिक: क्रमबद्ध! int
source / main.d (48): यहाँ से तात्कालिक: सॉर्ट! ()
अपने स्वयं के त्रुटि संदेशों को प्रदर्शित करने की क्षमता लगभग सभी मामलों में संभव है कि टेम्पलेट के साथ समस्याओं के बारे में किलोमीटर के लॉग से बचने के लिए, लेकिन ऐसी स्वतंत्रता की कीमत अधिक है - आपको अपने टेम्पलेट्स की प्रयोज्यता की सीमा पर सोचना होगा और अपने हाथों से समझने योग्य संदेश लिखना होगा। यह देखते हुए कि टेम्प्लेट पैरामीटर T हो सकता है: प्रकार, लैम्ब्डा, एक अन्य टेम्प्लेट (टेम्प्लेट टेम्प्लेट आदि), यह आपको निर्भर प्रकारों का अनुकरण करने की अनुमति देता है), अभिव्यक्ति, अभिव्यक्ति की सूची - अक्सर त्रुटियों के
उपयोगकर्ता की विकृत कल्पनाओं का एक सबसेट संसाधित होता है।
रिमोट मेमोरी तक पहुंच
डी में, डिफ़ॉल्ट
जीसी है , जो स्वयं संदर्भ गणना करता है और अनावश्यक वस्तुओं को हटाता है। डी में भी एक पृथक्करण है - वस्तु के संसाधनों का विमोचन और वस्तु का निष्कासन। पहले मामले में,
नष्ट () का उपयोग किया जाता है , दूसरे में
GC.free । आप GC -
GC.malloc द्वारा प्रबंधित मेमोरी को आवंटित कर सकते हैं। फिर प्रोग्राम स्वयं को जीसी की शुरुआत के दौरान मेमोरी को मुक्त कर देगा, यदि लिंक / पॉइंटर्स के माध्यम से मेमोरी का एक टुकड़ा पहुंच से बाहर है।
मॉलॉक फ़ंक्शंस के C-shn परिवार के माध्यम से मेमोरी आवंटित करना भी संभव है:
import std.c.stdlib; void main() { auto x = cast(int*)malloc(int.sizeof);
*** 'डेमो' में त्रुटि: डबल मुक्त या भ्रष्टाचार (फास्टटॉप): 0x0000000001b02650 ***
डी आपको विभिन्न स्तरों पर कार्यक्रम करने की अनुमति देता है, अंतर्निहित कोडांतरक तक। हम GC को मना करते हैं - हम त्रुटियों की एक श्रेणी के लिए जिम्मेदारी लेते हैं: लीक, दूरस्थ मेमोरी तक पहुंच। RAII (उदाहरण में अभिव्यक्ति का दायरा) का उपयोग करना इस दृष्टिकोण के साथ सिरदर्द को काफी कम कर सकता है।
हाल ही में प्रकाशित
डी कुकबुक में मैनुअल मेमोरी प्रबंधन के साथ कस्टम सरणियों के विकास और डी में कर्नेल मॉड्यूल लिखने (जीसी के बिना और रनटाइम के बिना) शामिल हैं। मानक पुस्तकालय वास्तव में रनटाइम और जीसी के पूर्ण परित्याग के साथ व्यावहारिक रूप से बेकार हो जाता है, लेकिन यह शुरू में उनकी सुविधाओं का उपयोग करने के लिए डिज़ाइन किया गया था। एंबेडेड स्टाइल लाइब्रेरी का स्थान अभी भी खाली है।
एक स्थानीय चर के लिए सूचक खो दिया है
जंग संस्करण:
fn bar<'a>(p: &'a int) -> &'a int { return p; } fn foo(n: int) -> &int { bar(&n) } fn main() { let p1 = foo(1); let p2 = foo(2); println!("{}, {}", *p1, *p2); }
डी पर एनालॉग (व्यावहारिक रूप से पोस्ट स्रोत से C ++ पर एक उदाहरण दोहराता है):
import std.stdio; int* bar(int* p) { return p; } int* foo(int n) { return bar(&n); } void main() { int* p1 = foo(1); int* p2 = foo(2); writeln(*p1, ",", *p2); }
निष्कर्ष:
2.2
इस उदाहरण में जंग का एक फायदा है, मुझे ऐसी किसी भी भाषा का पता नहीं है जिसमें चर के जीवनकाल का इतना शक्तिशाली विश्लेषक बनाया गया था। डी के बचाव में मैं केवल यही कह सकता हूं कि सुरक्षित मोड में कंपाइलर पिछले कोड को संकलित नहीं करता है:
त्रुटि: @ सुरक्षित फ़ंक्शन फू में पैरामीटर n का पता नहीं ले सकता
इसके अलावा, डी के लिए 90% कोड में, पॉइंटर्स का उपयोग नहीं किया जाता है (निम्न स्तर - उच्च जिम्मेदारी), ज्यादातर मामलों के लिए, रेफरी को हटाया जाता है:
import std.stdio; ref int bar(ref int p) { return p; } ref int foo(int n) { return bar(n); } void main() { auto p1 = foo(1); auto p2 = foo(2); writeln(p1, ",", p2); }
निष्कर्ष:
1.2
बिना किसी परिवर्तन के
सी ++
#include <stdio.h> int minval(int *A, int n) { int currmin; for (int i=0; i<n; i++) if (A[i] < currmin) currmin = A[i]; return currmin; } int main() { int A[] = {1,2,3}; int min = minval(A,3); printf("%d\n", min); }
D में, सभी डिफ़ॉल्ट मानों को T.init मान के साथ आरंभीकृत किया जाता है, लेकिन संकलक को यह बताना संभव है कि किसी विशिष्ट मामले में आरंभीकरण की आवश्यकता नहीं है:
import std.stdio; int minval(int[] A) { int currmin = void;
सकारात्मक बिंदु: पैर में शूट करने के लिए आपको
विशेष रूप से चाहिए। डी (शायद कॉपी-पेस्ट विधि का उपयोग करके) में एक चर को गलती से एकांतर करना लगभग असंभव है।
इस फ़ंक्शन का एक अधिक मुहावरेदार (और काम करने वाला) संस्करण इस तरह दिखेगा:
fn minval(A: &[int]) -> int { A.iter().fold(A[0], |u,&a| { if a<u {a} else {u} }) }
तुलना के लिए, D पर विकल्प:
int minval(int[] A) { return A.reduce!"a < b ? a : b";
नकलची निर्माणकर्ता
सी ++
struct A{ int *x; A(int v): x(new int(v)) {} ~A() {delete x;} }; int main() { A a(1), b=a; }
D पर समान संस्करण:
struct A { int *x; this(int v) { x = new int; *x = v; } } void main() { auto a = A(1); auto b = a; *bx = 5; assert(*ax == 1);
डी में, संरचनाएं केवल कॉपी शब्दार्थों का समर्थन करती हैं, और इसमें एक विरासत तंत्र (अशुद्धियों द्वारा प्रतिस्थापित), वर्चुअल फ़ंक्शन और ऑब्जेक्ट्स की अन्य विशेषताएं भी नहीं होती हैं। संरचना सिर्फ स्मृति का एक टुकड़ा है, संकलक कुछ भी अधिक नहीं जोड़ते हैं। उदाहरण के सही कार्यान्वयन के लिए, आपको एक पोस्टब्लिट कंस्ट्रक्टर (लगभग एक कॉपी कंस्ट्रक्टर) को परिभाषित करने की आवश्यकता है:
this(this)
एक स्पष्ट पृथक्करण है: यदि किसी वस्तु को प्रतिलिपि शब्दार्थ की आवश्यकता होती है - जैसे कि सरल प्रकार के प्रकार के लिए - संरचनाओं का उपयोग किया जाता है। यदि स्थानांतरण संदर्भ द्वारा होता है, तो कक्षाओं का उपयोग किया जाता है।
अलेक्जेंड्रेस्कु (एक
अनुवाद है ) की
पुस्तक में , इन सभी बिंदुओं पर प्रकाश डाला गया है।
जंग तुम्हारी पीठ के पीछे कुछ नहीं करेगी। Eq या क्लोन का एक स्वचालित कार्यान्वयन चाहते हैं? बस अपनी संरचना में व्युत्पन्न संपत्ति जोड़ें:
#[deriving(Clone, Eq, Hash, PartialEq, PartialOrd, Ord, Show)] struct A{ x: Box<int> }
डी में इस तंत्र का कोई एनालॉग नहीं है। संरचनाओं के लिए, इस तरह के सभी ऑपरेशन
संरचनात्मक टाइपिंग के माध्यम से अतिभारित होते हैं (अक्सर बतख टाइपिंग के साथ भ्रमित), यदि संरचना में एक उपयुक्त विधि है, तो इसका उपयोग किया जाता है, यदि नहीं, तो डिफ़ॉल्ट कार्यान्वयन।
ओवरलैपिंग मेमोरी क्षेत्र
#include <stdio.h> struct X { int a, b; }; void swap_from(X& x, const X& y) { xa = yb; xb = ya; } int main() { X x = {1,2}; swap_from(x,x); printf("%d,%d\n", xa, xb); }
यह हमें देता है:
2.2
समान डी कोड जो भी काम नहीं करता है:
struct X { int a, b; } void swap_from(ref X x, const ref X y) { xa = yb; xb = ya; } void main() { auto x = X(1,2); swap_from(x, x); writeln(xa, ",", xb); }
इस मामले में जंग निश्चित रूप से जीत जाती है। मुझे डी में संकलन समय पर मेमोरी ओवरलैपिंग का पता लगाने का कोई तरीका नहीं मिला।
बिखरा हुआ इटरेटर
डी में, इटर्चर एब्स्ट्रैक्शन को रंग से बदल दिया जाता
है , आइए पास होने पर कंटेनर को बदलने की कोशिश करें:
import std.stdio; void main() { int[] v; v ~= 1; v ~= 2; foreach(val; v) { if(val < 5) { v ~= 5 - val; } } writeln(v); }
निष्कर्ष:
[१, २, ४, ३]
रेंज एरे को बदलते समय, परिणामी वाला पहले नहीं बदलता है, जब तक कि फ़ॉरच ब्लॉक को समाप्त नहीं किया जाता है, तब तक यह रेंज "पुराने" एरे के डेटा को इंगित करेगा। आप देख सकते हैं कि सभी परिवर्तन सरणी की पूंछ में होते हैं, आप उदाहरण को जटिल कर सकते हैं और एक ही समय में शुरुआत और अंत में जोड़ सकते हैं:
import std.stdio; import std.container; void main() { DList!int v; v.insert(1); v.insert(2); foreach(val; v[])
निष्कर्ष:
[३, ४, १, २, ४, ३]
इस मामले में, मानक पुस्तकालय से दोगुनी लिंक की गई सूची का उपयोग किया गया था। एक सरणी का उपयोग करते समय, इसकी शुरुआत में जोड़ने से इसकी पुन: रचना होती है, लेकिन यह एल्गोरिथ्म को नहीं तोड़ता है, पुरानी श्रेणी पुरानी सरणी की ओर इशारा करती है, और हम सरणी की नई प्रतियों के साथ काम करते हैं, और जीसी के लिए धन्यवाद हम मेमोरी में लटका बिट्स के बारे में चिंता नहीं कर सकते। और सूची के मामले में, पूरी स्मृति की पुनः प्राप्ति की आवश्यकता नहीं है, केवल नए तत्वों के लिए।
खतरनाक स्विच
#include <stdio.h> enum {RED, BLUE, GRAY, UNKNOWN} color = GRAY; int main() { int x; switch(color) { case GRAY: x=1; case RED: case BLUE: x=2; } printf("%d", x); }
हमें एक "2" देता है। जंग में, आपको नमूने के साथ तुलना करते समय सभी विकल्पों को सूचीबद्ध करना होगा। इसके अलावा, कोड स्वचालित रूप से अगले विकल्प पर नहीं चढ़ता है अगर यह ब्रेक से नहीं मिलता है।
डी में, अंतिम कीवर्ड स्विच से पहले दिखाई दे सकता है, फिर कंपाइलर आपको सभी मिलान विकल्पों को लिखने के लिए मजबूर करेगा। अंतिम की अनुपस्थिति में, एक शर्त एक डिफ़ॉल्ट ब्लॉक की उपस्थिति है। इसके अलावा, संकलक के हाल के संस्करणों में, अगले लेबल पर एक निहित "डुबकी" को पदावनत के रूप में चिह्नित किया गया है, एक स्पष्ट गोटो मामले की आवश्यकता है। एक उदाहरण:
import std.stdio; enum Color {RED, BLUE, GRAY, UNKNOWN} Color color = Color.GRAY; void main() { int x; final switch(color) { case Color.GRAY: x = 1; case Color.RED: case Color.BLUE: x = 2; } writeln(x); }
संकलक आउटपुट:
source / main.d (227): त्रुटि: एनम सदस्य UNKNOWN अंतिम स्विच में प्रतिनिधित्व नहीं किया
source / main.d (229): चेतावनी: स्विच केस फालतू - 'गोटो केस;' का उपयोग करें यदि इरादा हो
source / main.d (229): चेतावनी: स्विच केस फालतू - 'गोटो केस;' का उपयोग करें यदि इरादा हो
रैंडम अर्धविराम
int main() { int pixels = 1; for (int j=0; j<5; j++); pixels++; }
जंग में, आपको छोरों और तुलना के निकायों को ब्रेसिज़ में संलग्न करना होगा। एक तिपहिया, बेशक, लेकिन एक कम त्रुटि वर्ग।
डी में, संकलक एक चेतावनी जारी करेगा (डिफ़ॉल्ट रूप से, चेतावनी त्रुटियां हैं) और प्रतिस्थापित करने की पेशकश करता है; {} पर।
बहु सूत्रण
#include <stdio.h> #include <pthread.h> #include <unistd.h> class Resource { int *value; public: Resource(): value(NULL) {} ~Resource() {delete value;} int *acquire() { if (!value) { value = new int(0); } return value; } }; void* function(void *param) { int *value = ((Resource*)param)->acquire(); printf("resource: %p\n", (void*)value); return value; } int main() { Resource res; for (int i=0; i<5; ++i) { pthread_t pt; pthread_create(&pt, NULL, function, &res); } //sleep(10); printf("done\n"); }
एक के बजाय कई संसाधन उत्पन्न करता है:
किया
संसाधन: 0x7f229c0008c0
संसाधन: 0x7f22840008c0
संसाधन: 0x7f228c0008c0
संसाधन: 0x7f22940008c0
संसाधन: 0x7f227c0008c0
डी में, रस्ट की तरह, संकलक साझा संसाधनों तक पहुंच की जांच करता है। डिफ़ॉल्ट रूप से, सभी मेमोरी अविभाज्य है, प्रत्येक थ्रेड पर्यावरण की अपनी प्रति (जो
टीएलएस में संग्रहीत है) के साथ काम करता है, और सभी साझा संसाधनों को साझा कीवर्ड के साथ चिह्नित किया जाता है। आइए D पर लिखने की कोशिश करते हैं:
import std.concurrency; import std.stdio; class Resource { private int* value; int* acquire() { if(!value) { value = new int; } return value; } } void foo(shared Resource res) {
संकलक ने स्पष्ट सिंक्रनाइज़ेशन नहीं देखा और कोड को संभावित दौड़ की स्थिति के साथ संकलित करने की अनुमति नहीं दी। डी में कई सिंक्रोनाइज़ेशन प्राइमेटिव हैं, लेकिन सरलता के लिए, ऑब्जेक्ट्स के लिए जावा-जैसे मॉनिटर-म्यूटेक्स पर विचार करें:
synchronized class Resource { private int* value; shared(int*) acquire() { if(!value) { value = new int; } return value; } }
निष्कर्ष:
किया
संसाधन 7FDED3805FF0
संसाधन 7FDED3805FF0
संसाधन 7FDED3805FF0
संसाधन 7FDED3805FF0
संसाधन 7FDED3805FF0
अधिग्रहण के लिए प्रत्येक कॉल के साथ, ऑब्जेक्ट का मॉनिटर थ्रेड द्वारा कैप्चर किया जाता है और संसाधन जारी होने तक अन्य सभी थ्रेड्स अवरुद्ध हो जाते हैं। अधिग्रहण फ़ंक्शन के वापसी प्रकार पर ध्यान दें, डी में ऐसे संशोधक, साझा, कास्ट, अपरिवर्तनीय सकर्मक हैं, यदि उनके साथ एक वर्ग संदर्भ चिह्नित किया जाता है, तो सभी फ़ील्ड और फ़ील्ड में दिए गए संकेत भी एक संशोधक के साथ चिह्नित होते हैं।
असुरक्षित कोड के बारे में थोड़ा सा
रस्ट के विपरीत, डी में सभी कोड डिफ़ॉल्ट रूप से @ सिस्टम है, अर्थात। असुरक्षित। कोड चिह्नित @ सुरक्षित प्रोग्रामर को सीमित करता है और पॉइंटर्स, असेंबलर आवेषण, असुरक्षित प्रकार के रूपांतरण और अन्य खतरनाक विशेषताओं के साथ खेलने से रोकता है। असुरक्षित कोड का उपयोग करने के लिए, सुरक्षित कोड में @ विश्वसनीय संशोधक है, ये प्रमुख स्थान हैं जिन्हें सावधानीपूर्वक परीक्षणों द्वारा कवर किया जाना चाहिए।
रस्ट के साथ तुलना करते हुए, मैं वास्तव में डी के लिए इस तरह के एक शक्तिशाली लिंक आजीवन विश्लेषण प्रणाली के लिए कामना करता हूं। इन भाषाओं के बीच "सांस्कृतिक" विनिमय केवल उन्हें लाभ देगा।
जीसी और संरचनाओं पर अतिरिक्त सामग्री के लिए
ReklatsMasters के लिए धन्यवाद।