2016-04-11 5 views
6

मैं एक enum संस्करण को अपडेट करना चाहते हैं, जबकि किसी भी क्लोनिंग के बिना नया एक के लिए पुराने संस्करण के एक क्षेत्र से आगे बढ़ के क्षेत्र बढ़ रहा है:बदलें, जबकि नए संस्करण

enum X { 
    X1(String), 
    X2(String), 
} 

fn increment_x(x: &mut X) { 
    match x { 
     &mut X::X1(s) => { 
      *x = X::X2(s); 
     } 
     &mut X::X2(s) => { 
      *x = X::X1(s); 
     } 
    } 
} 

इसका कारण यह है काम नहीं करता है हम &mut X से s स्थानांतरित नहीं कर सकते हैं।

कृपया enum X { X1, X2 } को लागू करने और struct S { variant: X, str: String } आदि का उपयोग करने जैसी चीजों का सुझाव न दें। यह एक सरलीकृत उदाहरण है, कल्पना करें कि कई अन्य फ़ील्ड वेरिएंट में हैं, और एक फ़ील्ड को एक संस्करण से दूसरे में ले जाना चाहते हैं।

+3

'स्ट्रिंग' के मामले में, आप' मेम कोई रिक्त स्ट्रिंग क्षेत्र में replace' :: सकते हैं, और परिणाम का उपयोग नया संस्करण बनाने के लिए। बस कुछ चालें। लेकिन यह केवल उन प्रकारों के लिए काम करता है जिनमें खाली तार जैसे सस्ते रूप होते हैं। –

उत्तर

7

यह काम नहीं करता है क्योंकि हम से s स्थानांतरित नहीं कर सकते हैं।

तो ऐसा नहीं है ... मूल्य से struct लेने के लिए और वापस जाने के लिए एक नया:

enum X { 
    X1(String), 
    X2(String), 
} 

fn increment_x(x: X) -> X { 
    match x { 
     X::X1(s) => X::X2(s), 
     X::X2(s) => X::X1(s), 
    } 
} 

अंत में, संकलक आप क्योंकि अगर आप बाहर स्ट्रिंग के लिए कदम सकता है सुरक्षा कर रहा है गणना के बाद, यह कुछ आधा निर्मित राज्य में होगा। स्ट्रिंग को मुक्त करने के लिए कौन जिम्मेदार होगा यदि फ़ंक्शन उस सटीक पल पर घबराए? क्या यह स्थानीय चर में enum या स्ट्रिंग में स्ट्रिंग को मुक्त करना चाहिए। यह डबल-फ्री के रूप में दोनों स्मृति-सुरक्षा समस्या नहीं हो सकता है।

आप एक परिवर्तनशील संदर्भ पर इसे लागू करने के लिए किया था, तुम वहाँ अस्थायी रूप से में एक डमी मान संग्रहीत कर सकता है:

use std::mem; 

fn increment_x_inline(x: &mut X) { 
    let old = mem::replace(x, X::X1(String::new())); 
    *x = increment_x(old); 
} 

बनाना एक खाली String बहुत बुरा (यह सिर्फ कुछ संकेत नहीं है, कोई ढेर आवंटन), लेकिन यह हमेशा संभव नहीं है। उस मामले में, आप Option उपयोग कर सकते हैं:

fn increment_x_inline(x: &mut Option<X>) { 
    let old = x.take(); 
    *x = old.map(increment_x); 
} 
3

आप एक शून्य लागत तरह से मूल्य से बाहर जाने के बिना ऐसा करना चाहते हैं, तो आप (AFAIK) असुरक्षित कोड का एक सा का सहारा लेना है:

#[derive(Debug)] 
enum X { 
    X1(String), 
    X2(String), 
} 

fn increment_x(x: &mut X) { 
    let interim = unsafe { mem::uninitialized() }; 
    let prev = mem::replace(x, interim); 
    let next = match prev { 
     X::X1(s) => X::X2(s), 
     X::X2(s) => X::X1(s), 
    }; 
    let interim = mem::replace(x, next); 
    mem::forget(interim); // Important! interim was never initialized 
} 

संपादित करें: धन्यवाद बाहर बाँटे की जगह के लिए @Shepmaster करने के लिए ...

+0

आपको ** बहुत सावधान रहना होगा ** यहां। 'Mem :: uninitialized() 'और' mem :: forget' के बीच होने वाली कोई भी आतंक अनियमित मान को शेष कार्यक्रम में रिसाव करने की अनुमति देगी। इस उदाहरण में, * मुझे * ऐसी संभावना दिखाई नहीं दे रही है। हालांकि, यह बहुत संभावना है कि कोई व्यक्ति उस कोड को कॉल करने में सक्षम होने के लिए उदाहरण को संशोधित करता है जो घबरा सकता है। एफडब्ल्यूआईडब्ल्यू, मैंने [दूसरे 'mem :: replace'] निकाला होगा (https://play.integer32.com/?gist=46b4233e57b243819bf0ef2bd1cda799&version=stable)। – Shepmaster

+0

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

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