C ++ की संगामिति सुविधाओं से जंग कैसे हटती है?


35

प्रशन

मैं यह समझने की कोशिश कर रहा हूं कि क्या R ++ मौलिक और पर्याप्त रूप से C ++ की संगामिति सुविधाओं में सुधार करता है या नहीं, ताकि यह तय किया जा सके कि मुझे Rust सीखने के लिए समय बिताना चाहिए या नहीं।

विशेष रूप से, कैसे मुहावरेदार जंग में सुधार होता है, या किसी भी दर से, मुहावरेदार C ++ की संगामिति सुविधाओं में सुधार होता है?

क्या सुधार (या विचलन) ज्यादातर वाक्यात्मक है, या क्या यह प्रतिमान में काफी सुधार (विचलन) है? या यह कुछ और है? या यह वास्तव में एक सुधार (विचलन) नहीं है?


दलील

मैं हाल ही में अपने आप को C ++ 14 की संगति सुविधाओं को सिखाने की कोशिश कर रहा हूं, और कुछ ठीक नहीं लगता है। कुछ महसूस होता है। क्या महसूस होता है? बताना कठिन है।

यह लगभग ऐसा महसूस करता है जैसे संकलक वास्तव में मुझे सही कार्यक्रमों को लिखने में मदद करने की कोशिश नहीं कर रहे थे जब यह संगामिति की बात आती है। यह लगभग महसूस करता है जैसे मैं एक संकलक के बजाय एक कोडांतरक का उपयोग कर रहा था।

जाहिर है, यह पूरी तरह से संभव है कि मैं अभी तक एक सूक्ष्म, दोषपूर्ण अवधारणा से पीड़ित हूं जब यह संगामिति की बात आती है। हो सकता है कि मैं अभी तक स्टेटफुल प्रोग्रामिंग और डेटा रेस के बीच बार्टोज़ मिलिव्स्की के तनाव को कम नहीं कर पाया। शायद मुझे समझ में नहीं आ रहा है कि संकलक में ध्वनि समवर्ती पद्धति कितनी है और ओएस में यह कितना है।

जवाबों:


56

एक बेहतर संगोष्ठी कहानी रस्ट परियोजना के मुख्य लक्ष्यों में से एक है, इसलिए सुधार की उम्मीद की जानी चाहिए, बशर्ते हम अपने लक्ष्यों को प्राप्त करने के लिए परियोजना पर भरोसा करते हैं। पूर्ण अस्वीकरण: मेरे पास रुस्ट की एक उच्च राय है और इसमें निवेश किया गया है। अनुरोध के अनुसार, मैं मानों के निर्णयों से बचने और (IMHO) सुधारों के बजाय मतभेदों का वर्णन करने का प्रयास करूँगा ।

सुरक्षित और असुरक्षित जंग

"जंग" दो भाषाओं से बना है: एक जो आपको सिस्टम प्रोग्रामिंग के खतरों से अलग करने की बहुत कोशिश करता है, और इस तरह की किसी भी आकांक्षाओं के बिना अधिक शक्तिशाली है।

Unsafe Rust एक गंदी, क्रूर भाषा है जो C ++ की तरह बहुत कुछ महसूस करती है। यह आपको मनमाने ढंग से खतरनाक चीजें करने की अनुमति देता है, हार्डवेयर से बात करता है, (गलत-) मेमोरी को मैन्युअल रूप से प्रबंधित करता है, अपने आप को पैर में गोली मारता है, आदि यह सी और सी ++ की तरह बहुत है कि कार्यक्रम की शुद्धता अंततः आपके हाथों में है। और इसमें शामिल सभी अन्य प्रोग्रामर के हाथ। आप इस भाषा को कीवर्ड के साथ चुनते हैं unsafe, और C और C ++ की तरह, एक ही स्थान पर एक गलती से पूरी परियोजना दुर्घटनाग्रस्त हो सकती है।

सेफ रस्ट "डिफ़ॉल्ट" है, रस्ट कोड का सबसे बड़ा विशाल हिस्सा सुरक्षित है, और यदि आपने unsafeअपने कोड में कभी भी कीवर्ड का उल्लेख नहीं किया है , तो आप सुरक्षित भाषा कभी नहीं छोड़ते हैं। बाकी पोस्ट ज्यादातर उस भाषा के साथ ही चिंता करेंगे, क्योंकि unsafeकोड किसी भी और सभी गारंटी को तोड़ सकता है जो सुरक्षित जंग आपको देने के लिए बहुत मेहनत करता है। दूसरी तरफ, unsafeकोड बुराई नहीं है और समुदाय द्वारा ऐसा नहीं माना जाता है (हालांकि, यह आवश्यक नहीं है जब दृढ़ता से हतोत्साहित किया जाता है)।

यह खतरनाक है, हाँ, लेकिन यह भी महत्वपूर्ण है, क्योंकि यह उन अमूर्त तत्वों को बनाने की अनुमति देता है जो सुरक्षित कोड का उपयोग करता है। अच्छा असुरक्षित कोड दूसरों को इसका दुरुपयोग करने से रोकने के लिए प्रकार प्रणाली का उपयोग करता है, और इसलिए एक रस्ट प्रोग्राम में असुरक्षित कोड की उपस्थिति को सुरक्षित कोड को परेशान करने की आवश्यकता नहीं है। निम्नलिखित सभी अंतर मौजूद हैं क्योंकि Rust के प्रकार के सिस्टम में ऐसे उपकरण हैं जो C ++ के पास नहीं हैं, और क्योंकि असुरक्षित कोड जो कि संगामिति सार को लागू करता है, इन उपकरणों का प्रभावी ढंग से उपयोग करता है।

गैर-अंतर: साझा / परस्पर स्मृति

हालाँकि, मैसेज पासिंग पर बहुत जोर देता है और साझा मेमोरी को बहुत सख्ती से नियंत्रित करता है, यह साझा मेमोरी कंसिस्टेंसी को नियंत्रित नहीं करता है और स्पष्ट रूप से कॉमन एब्स्ट्रक्शन (लॉक्स, एटॉमिक ऑपरेशंस, कंडीशन वैरिएबल, कंसर्ट कलेक्शन) को सपोर्ट करता है।

इसके अलावा, सी ++ की तरह और कार्यात्मक भाषाओं के विपरीत, रस्ट वास्तव में पारंपरिक अनिवार्य डेटा संरचनाओं को पसंद करता है। मानक पुस्तकालय में कोई निरंतर / अपरिवर्तनीय लिंक सूची नहीं है। वहाँ है, std::collections::LinkedListलेकिन यह std::listC ++ में जैसा है और उसी कारण से हतोत्साहित किया गया है std::list(कैश का बुरा उपयोग)।

हालाँकि, इस खंड के शीर्षक ("साझा / परिवर्तनशील स्मृति") के संदर्भ में, रस्ट के C ++ में एक अंतर है: यह दृढ़ता से प्रोत्साहित करता है कि स्मृति को "साझा XOR उत्परिवर्तित" किया जाए, अर्थात, स्मृति कभी भी साझा नहीं की जाती है और एक ही समय में परिवर्तनशील होती है। पहर। स्मृति को "अपने स्वयं के धागे की गोपनीयता" में पसंद करें, इसलिए बोलना है। C ++ के साथ इसका विरोध करें जहां साझा की जाने योग्य स्मृति डिफ़ॉल्ट विकल्प है और व्यापक रूप से उपयोग की जाती है।

जबकि साझा-एक्सोर-म्यूटेबल प्रतिमान नीचे के मतभेदों के लिए बहुत महत्वपूर्ण है, यह भी एक काफी अलग प्रोग्रामिंग प्रतिमान है जिसका उपयोग करने के लिए कुछ समय लगता है, और यह महत्वपूर्ण प्रतिबंध लगाता है। कभी-कभी किसी को इस प्रतिमान से बाहर निकलना पड़ता है, उदाहरण के लिए, परमाणु प्रकार के साथ ( AtomicUsizeसाझा साझा स्मृति का सार है)। ध्यान दें कि ताले साझा-एक्सोर-म्यूटेबल नियम का भी पालन करते हैं, क्योंकि यह समवर्ती पढ़ता है और लिखता है (जबकि एक धागा लिखता है, कोई अन्य धागा पढ़ या लिख ​​नहीं सकता है)।

गैर-अंतर: डेटा दौड़ अपरिभाषित व्यवहार (UB) हैं

यदि आप Rust कोड में डेटा रेस को ट्रिगर करते हैं, तो यह C ++ की तरह ही गेम ओवर है। सभी दांव बंद हैं और कंपाइलर जो चाहे उसे कर सकता है।

हालांकि, यह एक कठिन गारंटी है कि सुरक्षित रस्ट कोड में डेटा दौड़ (या उस मामले के लिए कोई यूबी) नहीं है। यह दोनों मूल भाषा और मानक पुस्तकालय तक फैली हुई है। यदि आप एक रस्ट प्रोग्राम लिख सकते हैं जो unsafe(तीसरे पक्ष के पुस्तकालयों में लेकिन मानक पुस्तकालय को छोड़कर) का उपयोग नहीं करता है जो यूबी को ट्रिगर करता है, तो इसे बग माना जाता है और इसे ठीक किया जाएगा (यह पहले से ही कई बार हो चुका है)। अगर यह C ++ के विपरीत है, जहां यह UB के साथ प्रोग्राम लिखने के लिए मामूली है।

अंतर: सख्त ताला अनुशासन

C ++, जंग (में एक ताला std::sync::Mutex, std::sync::RwLockआदि) का मालिक डेटा यह रक्षा कर रहा है। ताला लेने के बजाय और फिर कुछ साझा की गई मेमोरी में हेरफेर करना जो केवल दस्तावेज़ में लॉक से जुड़ा है, साझा डेटा अप्राप्य है जबकि आप लॉक को पकड़ नहीं पाते हैं। एक RAII गार्ड लॉक रखता है और साथ ही साथ लॉक किए गए डेटा तक पहुंच देता है (यह C ++ द्वारा लागू किया जा सकता है, लेकिन std::लॉक द्वारा नहीं )। जीवनकाल प्रणाली यह सुनिश्चित करती है कि लॉक जारी करने के बाद आप डेटा तक पहुँच नहीं रख सकते (RAII गार्ड को छोड़ दें)।

आप निश्चित रूप से एक ताला लगा सकते हैं जिसमें कोई उपयोगी डेटा नहीं है ( Mutex<()>), और बस कुछ मेमोरी को स्पष्ट रूप से उस लॉक के साथ जोड़कर साझा करें। हालाँकि, संभावित असंबद्ध साझा मेमोरी की आवश्यकता होती है unsafe

अंतर: आकस्मिक बंटवारे की रोकथाम

यद्यपि आप साझा की गई स्मृति को साझा कर सकते हैं, आप केवल तभी साझा करते हैं जब आप स्पष्ट रूप से इसके लिए पूछते हैं। उदाहरण के लिए, जब आप संदेश पासिंग (जैसे चैनल से std::sync) का उपयोग करते हैं, तो जीवनकाल प्रणाली यह सुनिश्चित करती है कि आप डेटा को किसी अन्य थ्रेड पर भेजने के बाद किसी भी संदर्भ को न रखें। एक लॉक के पीछे डेटा साझा करने के लिए, आप स्पष्ट रूप से लॉक का निर्माण करते हैं और इसे दूसरे धागे को देते हैं। आपके साथ असम्बद्ध मेमोरी साझा करने के लिए unsafe, अच्छी तरह से, उपयोग करना होगा unsafe

यह अगले बिंदु में शामिल है:

अंतर: थ्रेड-सुरक्षा ट्रैकिंग

रस्ट प्रकार प्रणाली थ्रेड सुरक्षा की कुछ धारणा को ट्रैक करती है। विशेष रूप से, Syncलक्षण उन प्रकारों को दर्शाता है जिन्हें डेटा थ्रेड्स के जोखिम के बिना कई थ्रेड्स द्वारा साझा किया जा सकता है, जबकि Sendउन लोगों को चिह्नित करता है जिन्हें एक धागे से दूसरे में स्थानांतरित किया जा सकता है। यह पूरे कार्यक्रम में कंपाइलर द्वारा लागू किया जाता है, और इस तरह पुस्तकालय डिजाइनर ऐसे अनुकूलन करते हैं जो इन स्थैतिक जांचों के बिना मूर्खतापूर्ण रूप से खतरनाक होंगे। उदाहरण के लिए, C ++ का std::shared_ptrजो हमेशा परमाणु संदर्भों का उपयोग अपने संदर्भ संख्या में हेरफेर करने के लिए करता है, यूबी से बचने के लिए यदि shared_ptrकई थ्रेड्स द्वारा उपयोग किया जाता है। जंग है Rcऔर Arc, जो केवल उस में अलग-अलग है जो Rc गैर-परमाणु भाटा संचालन का उपयोग करता है और थ्रेडसेफ़ नहीं है (यानी लागू नहीं करता है Syncया Send) जबकि Arcबहुत पसंद हैshared_ptr (और दोनों लक्षण लागू करता है)।

ध्यान दें कि यदि कोई प्रकार मैन्युअल रूप से सिंक्रनाइज़ेशन को लागू करने के लिए उपयोग नहीं करता हैunsafe , तो लक्षण की उपस्थिति या अनुपस्थिति को सही ढंग से अनुमान लगाया गया है।

अंतर: बहुत सख्त नियम

यदि कंपाइलर पूरी तरह से सुनिश्चित नहीं हो सकता है कि कुछ कोड डेटा रेस और अन्य यूबी से मुक्त है, तो यह संकलित नहीं होगा, अवधि । उपरोक्त नियम और अन्य उपकरण आपको काफी दूर तक पहुंचा सकते हैं, लेकिन जितनी जल्दी या बाद में आप कुछ ऐसा करना चाहते हैं जो सही है, लेकिन सूक्ष्म कारणों से जो संकलक के नोटिस से बच जाते हैं। यह एक पेचीदा लॉक-फ्री डेटा संरचना हो सकती है, लेकिन यह कुछ हद तक सांसारिक भी हो सकती है क्योंकि "मैं एक साझा सरणी में यादृच्छिक स्थानों पर लिखता हूं लेकिन सूचकांक ऐसे गणना किए जाते हैं कि हर स्थान केवल एक धागे से लिखा जाता है"।

उस बिंदु पर आप या तो बुलेट को काट सकते हैं और अनावश्यक सिंक्रनाइज़ेशन का एक सा जोड़ सकते हैं, या आप कोड को फिर से बनाते हैं ताकि कंपाइलर अपनी शुद्धता (अक्सर उल्लेखनीय, कभी-कभी काफी कठिन, कभी-कभी असंभव) देख सके, या आप unsafeकोड में गिर जाते हैं । फिर भी, यह अतिरिक्त मानसिक ओवरहेड है, और रस्ट आपको unsafeकोड की शुद्धता के लिए कोई गारंटी नहीं देता है ।

अंतर: कम उपकरण

उपरोक्त मतभेदों के कारण, जंग में यह बहुत अधिक दुर्लभ है कि एक कोड लिखता है जिसमें डेटा दौड़ हो सकती है (या मुफ्त के बाद उपयोग, या एक डबल मुक्त, या ...)। हालांकि यह अच्छा है, इसका दुर्भाग्यपूर्ण साइड इफेक्ट है कि इस तरह की त्रुटियों को ट्रैक करने के लिए पारिस्थितिकी तंत्र एक से अधिक अविकसित है जो कि युवाओं और समुदाय के छोटे आकार को देखते हुए उम्मीद करता है।

हालांकि वेलग्रिंड और एलएलवीएम के थ्रेड सैनिटाइजर जैसे उपकरण रूस्ट कोड पर लागू हो सकते हैं, क्या यह वास्तव में काम करता है, टूल से टूल में भिन्न होता है (और यहां तक ​​कि काम करने के लिए सेट करना मुश्किल हो सकता है, खासकर जब से आपको कोई अप-अप नहीं मिल सकता है -उपाय कैसे करना है, इस पर संसाधन)। यह वास्तव में मदद नहीं करता है कि रस्ट वर्तमान में एक वास्तविक विनिर्देश और विशेष रूप से एक औपचारिक मेमोरी मॉडल की कमी है।

संक्षेप में, unsafeRust कोड को सही ढंग से लिखना C ++ कोड को सही तरीके से लिखने की तुलना में कठिन है, दोनों भाषाओं में क्षमता और जोखिम के मामले में लगभग तुलनीय होने के बावजूद। बेशक यह इस तथ्य के खिलाफ भारित किया जाना चाहिए कि एक ठेठ जंग कार्यक्रम में unsafeकोड का केवल एक छोटा सा अंश होगा , जबकि एक C ++ प्रोग्राम अच्छी तरह से, पूरी तरह से C ++ है।


6
मेरी स्क्रीन पर कहां है 5:11 अपवोट स्विच? मैं यह नहीं मिल सकता है! इस जानकारीपूर्ण उत्तर की बहुत सराहना की जाती है। यह मुझे उन बिंदुओं पर कोई स्पष्ट प्रश्न नहीं देता है जो इसे कवर करता है। इसलिए, अन्य बिंदुओं के लिए: अगर मैं समझ गया कि जंग के दस्तावेज, Rust के पास [a] एकीकृत परीक्षण सुविधाएं हैं और [b] कार्गो नामक एक निर्माण प्रणाली है। क्या आपके विचार में ये उचित रूप से उत्पादन-तैयार हैं? इसके अलावा, कार्गो के बारे में, क्या मुझे शेल, पायथन और पर्ल स्क्रिप्ट्स, लाटेक्स संकलन इत्यादि को निर्माण प्रक्रिया में जोड़ने की अनुमति देने के बारे में अच्छा लगता है?
THB

2
@thb परीक्षण सामग्री बहुत नंगे-हड्डियों (जैसे कोई मज़ाक नहीं) लेकिन कार्यात्मक है। कार्गो काफी अच्छी तरह से काम करता है, हालांकि इसका ध्यान रस्ट पर और प्रतिलिपि प्रस्तुत करने योग्यता पर है इसका मतलब है कि यह स्रोत कोड से अंतिम कलाकृतियों तक सभी चरणों को कवर करने के लिए सबसे अच्छा विकल्प नहीं हो सकता है। आप स्क्रिप्ट लिख सकते हैं, लेकिन यह आपके द्वारा उल्लिखित सभी चीजों के लिए उपयुक्त नहीं हो सकता है। (लोग, हालांकि, नियमित रूप से सी लाइब्रेरीज़ को संकलित करने के लिए स्क्रिप्ट्स का उपयोग करते हैं या सी लाइब्रेरीज़ के मौजूदा संस्करण

2
वैसे, इसकी कीमत क्या है, इसके लिए आपका जवाब काफी निर्णायक है। चूंकि मुझे C ++ पसंद है, चूंकि C ++ में मेरे लिए आवश्यक लगभग सभी चीजों के लिए सभ्य सुविधाएं हैं, क्योंकि C ++ स्थिर और व्यापक रूप से उपयोग की जाती है, मुझे हर संभव गैर-उद्देश्य के लिए C ++ का उपयोग करने के लिए हेटोफ़ोर काफी संतुष्ट है (मैंने जावा में कभी कोई रुचि नहीं विकसित की है) , उदाहरण के लिए)। लेकिन अब हमारे पास समवर्ती है, और सी ++ 14 मुझे लगता है कि इसके साथ संघर्ष कर रहा है। मैंने स्वेच्छा से एक दशक में एक नई प्रोग्रामिंग भाषा की कोशिश नहीं की है, लेकिन (जब तक हास्केल को एक बेहतर विकल्प नहीं दिखाना चाहिए) मुझे लगता है कि मुझे रस्ट की कोशिश करनी होगी।
thb

Note that if a type doesn't use unsafe to manually implement synchronization, the presence or absence of the traits are inferred correctly.वास्तव में यह अभी भी unsafeतत्वों के साथ भी करता है । बस कच्चे पॉइंटर्स न तो हैं Syncऔर न ही Shareइसका मतलब है कि डिफ़ॉल्ट रूप से युक्त संरचना उनके पास नहीं होगी।
हौलेथ

@ HappenukaszNiemier यह ठीक से काम करने के लिए हो सकता है, लेकिन एक बिलियन तरीके हैं, जिसमें एक असुरक्षित उपयोग करने वाले प्रकार को हवा दे सकते हैं Sendया Syncभले ही यह वास्तव में नहीं होना चाहिए।

-2

रुस्त भी एर्लांग और गो की तरह ही है। यह उन चैनलों का उपयोग करता है जो बफर और सशर्त प्रतीक्षा कर रहे हैं। गो की तरह, यह आपको साझा मेमोरी, परमाणु संदर्भ गिनती और ताले का समर्थन करने और आपको थ्रेड से थ्रेड तक चैनल पास करने की अनुमति देकर एर्लैंग के प्रतिबंधों को शांत करता है।

हालांकि, रस्ट एक कदम आगे निकल जाता है। जबकि गो आपको सही काम करने के लिए भरोसा करता है, रुस्त एक संरक्षक को सौंपता है जो आपके साथ बैठता है और शिकायत करता है कि क्या आप गलत काम करने की कोशिश करते हैं। रस्ट के संरक्षक कंपाइलर हैं। यह थ्रेड के आसपास से गुजरने वाले मानों के स्वामित्व को निर्धारित करने के लिए परिष्कृत विश्लेषण करता है और संभावित समस्याएं होने पर संकलन त्रुटियों को प्रदान करता है।

निम्नलिखित RUST डॉक्स का एक उद्धरण है।

संदेश भेजने में स्वामित्व नियम महत्वपूर्ण भूमिका निभाते हैं क्योंकि वे हमें सुरक्षित, समवर्ती कोड लिखने में मदद करते हैं। समवर्ती प्रोग्रामिंग में त्रुटियों को रोकना हमारे रस्ट कार्यक्रमों में स्वामित्व के बारे में सोचने के लिए व्यापार-बंद करके हमें मिलने वाला लाभ है। - मूल्यों के स्वामित्व के साथ संदेश गुजर रहा है।

यदि एर्लैंग ड्रेकियन है और गो एक स्वतंत्र राज्य है, तो रस्ट एक नानी राज्य है।

आप प्रोग्रामिंग लैंग्वेजेस की कंसीडर विचारधारा से अधिक जानकारी प्राप्त कर सकते हैं : जावा, सी #, सी, सी +, गो, और जंग।


2
स्टैक एक्सचेंज में आपका स्वागत है! कृपया ध्यान दें कि जब भी आप अपने स्वयं के ब्लॉग से लिंक करते हैं, तो आपको स्पष्ट रूप से बताने की आवश्यकता होती है; सहायता केंद्र देखें ।
ग्लोरफाइंडेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.