2015-11-13 17 views

उत्तर

31

वहाँ जंग में downcasting करने के लिए दो तरीके हैं। पहला Any का उपयोग करना है। ध्यान दें कि यह केवल आपको सटीक, मूल ठोस प्रकार पर डाउनकास्ट करने की अनुमति देता है। इसलिए जैसा:

use std::any::Any; 

trait A { 
    fn as_any(&self) -> &Any; 
} 

struct B; 

impl A for B { 
    fn as_any(&self) -> &Any { 
     self 
    } 
} 

fn main() { 
    let a: Box<A> = Box::new(B); 
    // The indirection through `as_any` is because using `downcast_ref` 
    // on `Box<A>` *directly* only lets us downcast back to `&A` again. 
    // The method ensures we get an `Any` vtable that lets us downcast 
    // back to the original, concrete type. 
    let b: &B = match a.as_any().downcast_ref::<B>() { 
     Some(b) => b, 
     None => panic!("&a isn't a B!") 
    }; 
} 

दूसरी तरह के आधार विशेषता पर प्रत्येक 'लक्ष्य' के लिए एक विधि को लागू करने (इस मामले, A में), और प्रत्येक वांछित लक्ष्य प्रकार के लिए डाले लागू है।


प्रतीक्षा, हम क्यों as_any की ज़रूरत है?

भले ही आप A के लिए आवश्यकता के रूप में जोड़ते हैं, फिर भी यह सही तरीके से काम नहीं करेगा। पहली समस्या यह है कि ABox<A> में भी लागू Any ... जिसका अर्थ है कि जब आप downcast_ref पर कॉल करते हैं, तो आप वास्तव में इसे ऑब्जेक्ट प्रकार A पर कॉल करेंगे। Anyकेवल उस प्रकार के डाउनकास्ट को डाउनकास्ट किया जा सकता है, जिस पर इस मामले में A है, इसलिए आप केवल &A पर वापस आ सकते हैं जो आपके पास पहले से था।

लेकिन के अंतर्निहित प्रकार के लिए कहीं पर कार्यान्वयन है, है ना? खैर, हाँ, लेकिन आप इसे प्राप्त नहीं कर सकते हैं। जंग आपको &A से &Any से "कास्ट क्रॉस" करने की अनुमति नहीं देती है।

क्या as_any है; क्योंकि यह केवल हमारे "कंक्रीट" प्रकारों पर लागू होता है, इसलिए संकलक भ्रमित नहीं होता है कि इसे किसके लिए बुलाया जाना चाहिए। इसे &A पर कॉल करने से यह ठोस कार्यान्वयन (फिर से, इस मामले में, B::as_any) को गतिशील रूप से प्रेषित करने का कारण बनता है, जो के Any के कार्यान्वयन का उपयोग करके देता है, जो हम चाहते हैं।

ध्यान दें कि आप कर सकते हैं ओर कदम बस सभी पर Aका उपयोग नहीं द्वारा इस पूरे समस्या। विशेष रूप से, निम्नलिखित होगा भी काम:

fn main() { 
    let a: Box<Any> = Box::new(B); 
    let _: &B = match a.downcast_ref::<B>() { 
     Some(b) => b, 
     None => panic!("&a isn't a B!") 
    };  
} 

बहरहाल, यह किसी भी अन्य तरीकों होने से आप precludes; सभी आप यहां एक ठोस प्रकार के लिए डाउनकास्ट कर सकते हैं।

संभावित रुचि के अंतिम नोट के रूप में, mopa क्रेट आपको अपने खुद के गुण के साथ Any की कार्यक्षमता को जोड़ने की अनुमति देता है।

+0

हाँ:

यह बहुत ही इसी तरह के प्रश्न में बेहतर विकल्प है! मुझे लगता है कि यह मुझे फिट बैठता है। बहुत बहुत धन्यवाद। – Aleksandr

+0

मैं पहली बार बात कर रहा हूं :) – Aleksandr

+1

यह इंगित करने लायक है कि 'as_any' फ़ंक्शन की आवश्यकता क्यों है। यह 'बी' के लिए लागू किया गया है और 'B' प्रकार का पैरामीटर 'स्वयं' लेता है, जिसे 'और किसी भी' में परिवर्तित किया जाता है और बाद में इसे '& B' पर वापस लाया जा सकता है। 'A.as_any()' को '(& * ए और ए के साथ) के साथ प्रतिस्थापित किया जा सकता है, इसे केवल' और किसी भी 'में परिवर्तित प्रकार पर वापस लाया जा सकता है, जो 'और ए' है। 'और ए' और' & B' एक ही चीज़ नहीं हैं क्योंकि उनके पास भिन्न v-table हैं। – dhardy

4

यह स्पष्ट किया जाना चाहिए कि किरदार अगर वहाँ एक और प्रकार CA को लागू है और आप एक Box<B> में Box<C> कास्ट करने के लिए कोशिश असफल हो सकता है। मुझे आपकी स्थिति पता नहीं है, लेकिन मेरे लिए यह बहुत कुछ दिखता है जैसे आप जावा जैसे अन्य भाषाओं से तकनीक ला रहे हैं। मैंने कभी इस तरह की समस्या में जंग का सामना नहीं किया है - शायद इस तरह के कलाकारों से बचने के लिए आपके कोड डिज़ाइन में सुधार किया जा सकता है।

यदि आप चाहते हैं, तो आप mem::transmute के साथ बहुत कुछ "कास्ट" कर सकते हैं। अफसोस की बात है, अगर हम सिर्फ Box<A> से Box<B> या &A से &B पर trait पर पॉइंटर एक वसा-सूचक है जो वास्तव में दो पॉइंटर्स होते हैं: वास्तविक वस्तु में से एक, vptr में से एक। अगर हम इसे struct प्रकार पर कास्टिंग कर रहे हैं, तो हम केवल vptr को अनदेखा कर सकते हैं। कृपया याद रखें कि यह समाधान बेहद असुरक्षित और सुंदर हैकी है - मैं इसे "असली" कोड में उपयोग नहीं करता।

let (b, vptr): (Box<B>, *const()) = unsafe { std::mem::transmute(a) }; 

संपादित करें: पेंच है कि, यह और भी असुरक्षित से मैंने सोचा है। यदि आप इसे सही तरीके से करना चाहते हैं तो आपको std::raw::TraitObject का उपयोग करना होगा। हालांकि यह अभी भी अस्थिर है। मुझे नहीं लगता कि यह ओपी के लिए किसी भी प्रयोग का है; इसका इस्तेमाल न करें! How to match trait implementors

+0

उत्तर के लिए धन्यवाद। मैं समझने की कोशिश कर रहा हूं कि इस स्थिति का सामना कैसे किया जाए। इसलिए संभावित समाधानों के बारे में जानने में खुशी हुई। – Aleksandr

संबंधित मुद्दे