2015-07-05 4 views
21

से कनवर्ट करना अक्सर मुझे गणना से Option<String> प्राप्त हुआ है, और मैं या तो इस मान या डिफ़ॉल्ट हार्डकोडेड मान का उपयोग करना चाहता हूं।विकल्प <String> से विकल्प <&str>

let opt: Option<String> = "some value".to_owned(); 
let value = opt.unwrap_or("default string"); 

सटीक यहाँ त्रुटि है:

let opt: Option<i32> = Some(3); 
let value = opt.unwrap_or(0); // 0 being the default 

लेकिन एक String और एक &str साथ, संकलक के बारे में बेमेल प्रकार की शिकायत है:

यह एक पूर्णांक के साथ तुच्छ होगा

error: mismatched types: 
expected `collections::string::String`, 
    found `&'static str` 

एक विकल्प स्ट्रिंग स्लाइस को एक में परिवर्तित करना है स्वामित्व वाली स्ट्रिंग:

let rx: Regex = Regex::new(&opt.unwrap_or("default string".to_owned())); 

मैं नहीं बल्कि होगा:

let value = opt.unwrap_or("default string".to_owned()); 

लेकिन इस आवंटन, जो अवांछनीय है जब मैं तुरंत परिणाम Regex::new() करने के लिए, एक स्ट्रिंग टुकड़ा करने के लिए वापस परिवर्तित करने के लिए इस कॉल में के रूप में चाहते हैं का कारण बनता है इस आवंटन से बचने के लिए Option<String> को Option<&str> में कनवर्ट करें।

इसे लिखने का मूर्ख तरीका क्या है?

उत्तर

17

आप as_ref() और map() का उपयोग एक Option<&str> में एक Option<String> को बदलने के लिए कर सकते हैं।

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.as_ref().map(|x| &**x).unwrap_or("default string"); 
} 

पहले, as_ref() परोक्ष opt पर एक संदर्भ लेता है, दे रही है एक &Option<String> (क्योंकि as_ref()&self लेता है, यानी यह एक संदर्भ प्राप्त करता है), और यह एक Option<&String> में बदल जाता है। फिर हम इसे का उपयोग Option<&str> में बदलने के लिए करते हैं। यहां &**x क्या है: दाएं * (जिसे पहले मूल्यांकन किया गया है) &String को String lvalue प्रदान करता है। फिर, बाईं ओर * वास्तव में Deref विशेषता का आह्वान करता है, क्योंकि StringimplementsDeref<Target=str>, हमें str lvalue दे रहा है। अंत में, &str लैवल्यू का पता लेता है, जो हमें &str देता है।

आप आगे map_or का उपयोग कर एक भी आपरेशन में map और unwrap_or गठबंधन करने के लिए द्वारा इस थोड़ा आसान बनाने में कर सकते हैं:

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.as_ref().map_or("default string", |x| &**x); 
} 

तो &**x आपको भी जादुई लग रहा है, आप String::as_str बजाय लिख सकते हैं:

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.as_ref().map_or("default string", String::as_str); 
} 

या String::as_ref (AsRef विशेषता से, जो prelude में है):

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.as_ref().map_or("default string", String::as_ref); 
} 

या String::deref (हालांकि आप भी Deref विशेषता आयात करने की आवश्यकता):

use std::ops::Deref; 

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.as_ref().map_or("default string", String::deref); 
} 

इनमें से किसी के लिए काम करने के लिए, आप Option<&str> या unwrapped के रूप में रूप में लंबे समय के लिए एक Option<String> मालिक रखने की जरूरत है &str उपलब्ध रहने की जरूरत है। यदि यह बहुत जटिल है, तो आप Cow का उपयोग कर सकते हैं।

use std::borrow::Cow::{Borrowed, Owned}; 

fn main() { 
    let opt: Option<String> = Some("some value".to_owned()); 
    let value = opt.map_or(Borrowed("default string"), |x| Owned(x)); 
} 
+7

bikesheddy टिप्पणी: 'map (| x | & ** x) के बजाय 'आप' मानचित्र (स्ट्रिंग :: as_ref) भी कर सकते हैं। – fjh

+1

यह अच्छी तरह से काम करता है, हालांकि अभी भी एक छोटी verbose। स्पष्टीकरण के लिए, 'opt.as_ref() 'का प्रकार' विकल्प <&String> 'है, फिर' opt.as_ref()। नक्शा (स्ट्रिंग :: as_ref) 'इस' और स्ट्रिंग' को 'स्ट्रिंग :: as_ref' से गुजरता है, जो रिटर्न देता है एक 'और str'। 'विकल्प :: as_ref' से 'और स्ट्रिंग' को' और str' से क्यों नहीं जोड़ा जा सकता है? – thirtythreeforty

+1

@thirtythreeforty 'स्ट्रिंग :: as_ref' एक '& str' देता है, न कि '&& स्ट्रिंग' (देखें [यहां] (http://doc.rust-lang.org/collections/string/struct.String.html#method। as_ref)) – fjh

2

यहां एक तरीका है जिससे आप इसे कर सकते हैं। ध्यान रखें कि में है जो मूल String को रखने के लिए है, अन्यथा &str क्या एक टुकड़ा होगा?

let opt = Some(String::from("test")); // kept around 

let unwrapped: &str = match opt.as_ref() { 
    Some(s) => s, // deref coercion 
    None => "default", 
}; 

playpen

+1

कि नहीं था ' इसे एक विकल्प <&str> में बदल दें। – rspeer

11

एक अच्छे रास्ते T: Deref के लिए सामान्य रूप से इस लागू करने के लिए हो सकता है:

use std::ops::Deref; 

trait OptionDeref<T: Deref> { 
    fn as_deref(&self) -> Option<&T::Target>; 
} 

impl<T: Deref> OptionDeref<T> for Option<T> { 
    fn as_deref(&self) -> Option<&T::Target> { 
     self.as_ref().map(Deref::deref) 
    } 
} 

जो प्रभावी रूप से सामान्यीकरण करता as_ref

+1

यह एक महान उपयोगिता समारोह है। काश यह विकल्प पर एक समारोह के रूप में मानक पुस्तकालय का हिस्सा था! –

+1

धन्यवाद! यह मेरे लिए काम करता है - अगर मैं इस कोड को शामिल करता हूं, तो 'opt.as_deref() 'वास्तव में एक' विकल्प <&str> 'है। – rspeer

2

हालांकि मैं Veedrac के जवाब (मैं इसे प्रयोग किया जाता) प्यार करता हूँ, अगर आप सिर्फ एक बिंदु पर यह की जरूरत है और आप कुछ है कि अर्थपूर्ण है चाहते हैं आप उपयोग कर सकते as_ref(), map और String::as_str श्रृंखला:

let opt: Option<String> = Some("some value".to_string()); 

assert_eq!(Some("some value"), opt.as_ref().map(String::as_str)); 
संबंधित मुद्दे