2015-05-27 27 views
5

अपडेट: पोस्ट का शीर्षक अपडेट कर दिया गया है, और उत्तर को प्रश्न से बाहर कर दिया गया है। संक्षिप्त जवाब यह है कि आप नहीं कर सकते हैं। कृपया इस प्रश्न का मेरा जवाब देखें।मैं एक इटरेटर कैसे लौटा सकता हूं जो एक फ़ंक्शन द्वारा उत्पन्न होता है जो 'एक म्यूट स्वयं (जब स्वयं स्थानीय रूप से बनाया जाता है)?

मैं ब्लॉग पोस्ट here हैंडलिंग (GitHub के लिए यह here है) एक त्रुटि अनुसरण कर रहा हूँ, और मैं इतना है कि search समारोह एक Vec के बजाय एक Iterator रिटर्न कोड के लिए कुछ संशोधन करने की कोशिश की। यह बेहद मुश्किल रहा है, और मैं अटक गया हूँ।

fn search<'a, P: AsRef<Path>, F>(file_path: &Option<P>, city: &str) 
    -> Result<FilterMap<csv::reader::DecodedRecords<'a, Box<Read>, Row>, F>, 
       CliError> 
    where F: FnMut(Result<Row, csv::Error>) 
        -> Option<Result<PopulationCount, csv::Error>> { 
:

src/main.rs:97:1: 133:2 error: the trait `core::marker::Sized` is not implemented for the type `core::ops::FnMut(core::result::Result<Row, csv::Error>) -> core::option::Option<core::result::Result<PopulationCount, csv::Error>>` [E0277] 
src/main.rs:97 fn search<'a, P: AsRef<Path>>(file_path: &Option<P>, city: &str) -> Result<FilterMap<csv::reader::DecodedRecords<'a, Box<Read>, Row>, FnMut(Result<Row, csv::Error>) -> Option<Result<PopulationCount, csv::Error>>>, CliError> { 
src/main.rs:98  let mut found = vec![]; 
src/main.rs:99  let input: Box<io::Read> = match *file_path { 
src/main.rs:100   None => Box::new(io::stdin()), 
src/main.rs:101   Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), 
src/main.rs:102  }; 
       ... 
src/main.rs:97:1: 133:2 note: `core::ops::FnMut(core::result::Result<Row, csv::Error>) -> core::option::Option<core::result::Result<PopulationCount, csv::Error>>` does not have a constant size known at compile-time 
src/main.rs:97 fn search<'a, P: AsRef<Path>>(file_path: &Option<P>, city: &str) -> Result<FilterMap<csv::reader::DecodedRecords<'a, Box<Read>, Row>, FnMut(Result<Row, csv::Error>) -> Option<Result<PopulationCount, csv::Error>>>, CliError> { 
src/main.rs:98  let mut found = vec![]; 
src/main.rs:99  let input: Box<io::Read> = match *file_path { 
src/main.rs:100   None => Box::new(io::stdin()), 
src/main.rs:101   Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), 
src/main.rs:102  }; 
       ... 
error: aborting due to previous error 

मैं भी इस समारोह परिभाषा की कोशिश की है:

fn search<'a, P: AsRef<Path>>(file_path: &Option<P>, city: &str) 
    -> Result<FilterMap<csv::reader::DecodedRecords<'a, Box<Read>, Row>, 
         FnMut(Result<Row, csv::Error>) 
          -> Option<Result<PopulationCount, csv::Error>>>, 
       CliError> { 
    let mut found = vec![]; 
    let input: Box<io::Read> = match *file_path { 
     None => Box::new(io::stdin()), 
     Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), 
    }; 

    let mut rdr = csv::Reader::from_reader(input); 
    let closure = |row: Result<Row, csv::Error>| -> Option<Result<PopulationCount, csv::Error>> { 
     let row = match row { 
      Ok(row) => row, 
      Err(err) => return Some(Err(From::from(err))), 
     }; 
     match row.population { 
      None => None, 
      Some(count) => if row.city == city { 
       Some(Ok(PopulationCount { 
        city: row.city, 
        country: row.country, 
        count: count, 
       })) 
      } else { 
       None 
      } 
     } 
    }; 
    let found = rdr.decode::<Row>().filter_map(closure); 

    if !found.all(|row| match row { 
     Ok(_) => true, 
     _ => false, 
    }) { 
     Err(CliError::NotFound) 
    } else { 
     Ok(found) 
    } 
} 
संकलक से निम्न त्रुटि के साथ

:

मैं इस बिंदु तक मिल गया है

संकलक से इन त्रुटियों के साथ:

src/main.rs:131:12: 131:17 error: mismatched types: 
expected `core::iter::FilterMap<csv::reader::DecodedRecords<'_, Box<std::io::Read>, Row>, F>`, 
found `core::iter::FilterMap<csv::reader::DecodedRecords<'_, Box<std::io::Read>, Row>, [closure src/main.rs:105:19: 122:6]>` 
(expected type parameter, 
found closure) [E0308] 
src/main.rs:131   Ok(found) 

मैं Box बंद नहीं कर सकता क्योंकि तब इसे filter_map द्वारा स्वीकार नहीं किया जाएगा।

मैं तो यह पता करने की कोशिश की:

fn search<'a, P: AsRef<Path>>(file_path: &Option<P>, city: &'a str) 
    -> Result<(Box<Iterator<Item=Result<PopulationCount, csv::Error>> + 'a>, csv::Reader<Box<io::Read>>), CliError> { 
    let input: Box<io::Read> = match *file_path { 
     None => box io::stdin(), 
     Some(ref file_path) => box try!(fs::File::open(file_path)), 
    }; 

    let mut rdr = csv::Reader::from_reader(input); 
    let mut found = rdr.decode::<Row>().filter_map(move |row| { 
     let row = match row { 
      Ok(row) => row, 
      Err(err) => return Some(Err(err)), 
     }; 
     match row.population { 
      None => None, 
      Some(count) if row.city == city => { 
       Some(Ok(PopulationCount { 
        city: row.city, 
        country: row.country, 
        count: count, 
       })) 
      }, 
      _ => None, 
     } 
    }); 

    if found.size_hint().0 == 0 { 
     Err(CliError::NotFound) 
    } else { 
     Ok((box found, rdr)) 
    } 
} 

fn main() { 
    let args: Args = Docopt::new(USAGE) 
          .and_then(|d| d.decode()) 
          .unwrap_or_else(|err| err.exit()); 


    match search(&args.arg_data_path, &args.arg_city) { 
     Err(CliError::NotFound) if args.flag_quiet => process::exit(1), 
     Err(err) => fatal!("{}", err), 
     Ok((pops, rdr)) => for pop in pops { 
      match pop { 
       Err(err) => panic!(err), 
       Ok(pop) => println!("{}, {}: {} - {:?}", pop.city, pop.country, pop.count, rdr.byte_offset()), 
      } 
     } 
    } 
} 

कौन मुझे इस त्रुटि देता है:

src/main.rs:107:21: 107:24 error: `rdr` does not live long enough 
src/main.rs:107  let mut found = rdr.decode::<Row>().filter_map(move |row| { 
            ^~~ 
src/main.rs:100:117: 130:2 note: reference must be valid for the lifetime 'a as defined on the block at 100:116... 
src/main.rs:100  -> Result<(Box<Iterator<Item=Result<PopulationCount, csv::Error>> + 'a>, csv::Reader<Box<io::Read>>), CliError> { 
src/main.rs:101  let input: Box<io::Read> = match *file_path { 
src/main.rs:102   None => box io::stdin(), 
src/main.rs:103   Some(ref file_path) => box try!(fs::File::open(file_path)), 
src/main.rs:104  }; 
src/main.rs:105  
       ... 
src/main.rs:106:51: 130:2 note: ...but borrowed value is only valid for the block suffix following statement 1 at 106:50 
src/main.rs:106  let mut rdr = csv::Reader::from_reader(input); 
src/main.rs:107  let mut found = rdr.decode::<Row>().filter_map(move |row| { 
src/main.rs:108   let row = match row { 
src/main.rs:109    Ok(row) => row, 
src/main.rs:110    Err(err) => return Some(Err(err)), 
src/main.rs:111   }; 
       ... 
error: aborting due to previous error 

मैंने कुछ गलत डिज़ाइन किया गया है, या मैं गलत दृष्टिकोण ले रहा हूँ? क्या मैं कुछ सचमुच सरल और बेवकूफ हूँ? मुझे यकीन नहीं है कि यहां से कहाँ जाना है।

+0

क्या आप समझा सकते हैं कि यह एक इटरेटर वापस करने का सही तरीका क्यों नहीं है (http://stackoverflow.com/q/27535289/155423)? – Shepmaster

+0

मुझे लगता है कि नाशेनास वास्तव में नीचे एक इटेटरेटर लौट रहा है, लेकिन एक और समस्या है - एक स्थानीय उधार लेना। – bluss

+0

@bluss सही है। मैंने जो काम पूरा करने की कोशिश की थी, उसके आधार पर मैंने सवाल का नाम दिया, लेकिन ऐसा लगता है कि मैंने जो कुछ सीखा है उसके आधार पर इसका नाम बदला जाना चाहिए। आप सभी की क्या सिफारिश करेंगे? – Nashenas

उत्तर

2

इस उत्तर @bluss के answer + irc.mozilla.org पर #rust से मदद पर आधारित है

एक मुद्दा यह है कि कोड से स्पष्ट नहीं है, और जो पैदा कर रहा था अंतिम त्रुटि बस के ऊपर प्रदर्शित, csv::Reader::decode की परिभाषा के साथ करना है (इसे source देखें)। यह &'a mut self लेता है, इस समस्या का स्पष्टीकरण इस answer में शामिल है। यह अनिवार्य रूप से पाठक के जीवनकाल को उस ब्लॉक से बाध्य करने का कारण बनता है जिसे इसे बुलाया जाता है। इसे ठीक करने का तरीका फ़ंक्शन को आधे में विभाजित करना है (क्योंकि मैं पिछले उत्तर लिंक में अनुशंसित फ़ंक्शन परिभाषा को नियंत्रित नहीं कर सकता)। मुझे पाठक पर जीवन भर की आवश्यकता थी जो main फ़ंक्शन के भीतर मान्य था, इसलिए पाठक को search फ़ंक्शन में पारित किया जा सकता था। नीचे दिए गए कोड देखें (यह निश्चित रूप से और अधिक साफ किया जा सकता है):

fn population_count<'a, I>(iter: I, city: &'a str) 
    -> Box<Iterator<Item=Result<PopulationCount,csv::Error>> + 'a> 
    where I: IntoIterator<Item=Result<Row,csv::Error>>, 
      I::IntoIter: 'a, 
{ 
    Box::new(iter.into_iter().filter_map(move |row| { 
     let row = match row { 
      Ok(row) => row, 
      Err(err) => return Some(Err(err)), 
     }; 

     match row.population { 
      None => None, 
      Some(count) if row.city == city => { 
       Some(Ok(PopulationCount { 
        city: row.city, 
        country: row.country, 
        count: count, 
       })) 
      }, 
      _ => None, 
     } 
    })) 
} 

fn get_reader<P: AsRef<Path>>(file_path: &Option<P>) 
    -> Result<csv::Reader<Box<io::Read>>, CliError> 
{ 
    let input: Box<io::Read> = match *file_path { 
     None => Box::new(io::stdin()), 
     Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), 
    }; 

    Ok(csv::Reader::from_reader(input)) 
} 

fn search<'a>(reader: &'a mut csv::Reader<Box<io::Read>>, city: &'a str) 
    -> Box<Iterator<Item=Result<PopulationCount, csv::Error>> + 'a> 
{ 
    population_count(reader.decode::<Row>(), city) 
} 

fn main() { 
    let args: Args = Docopt::new(USAGE) 
     .and_then(|d| d.decode()) 
     .unwrap_or_else(|err| err.exit()); 

    let reader = get_reader(&args.arg_data_path); 
    let mut reader = match reader { 
     Err(err) => fatal!("{}", err), 
     Ok(reader) => reader, 
    }; 

    let populations = search(&mut reader, &args.arg_city); 
    let mut found = false; 
    for pop in populations { 
     found = true; 
     match pop { 
      Err(err) => fatal!("fatal !! {}", err), 
      Ok(pop) => println!("{}, {}: {}", pop.city, pop.country, pop.count), 
     } 
    } 

    if !(found || args.flag_quiet) { 
     fatal!("{}", CliError::NotFound); 
    } 
} 

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

8

रिटर्निंग इटरेटर्स संभव है, लेकिन यह कुछ प्रतिबंधों के साथ आता है।

यह संभव, दो उदाहरण है प्रदर्शित करने के लिए, (ए) स्पष्ट इटरेटर प्रकार और (बी) के साथ मुक्केबाजी (playpen link).

use std::iter::FilterMap; 

fn is_even(elt: i32) -> Option<i32> { 
    if elt % 2 == 0 { 
     Some(elt) 
    } else { None } 
} 

/// (A) 
pub fn evens<I: IntoIterator<Item=i32>>(iter: I) 
    -> FilterMap<I::IntoIter, fn(I::Item) -> Option<I::Item>> 
{ 
    iter.into_iter().filter_map(is_even) 
} 

/// (B) 
pub fn cumulative_sums<'a, I>(iter: I) -> Box<Iterator<Item=i32> + 'a> 
    where I: IntoIterator<Item=i32>, 
      I::IntoIter: 'a, 
{ 
    Box::new(iter.into_iter().scan(0, |acc, x| { 
     *acc += x; 
     Some(*acc) 
    })) 
} 

fn main() { 
    // The output is: 
    // 0 is even, 10 is even, 
    // 1, 3, 6, 10, 
    for even in evens(vec![0, 3, 7, 10]) { 
     print!("{} is even, ", even); 
    } 
    println!(""); 

    for cs in cumulative_sums(1..5) { 
     print!("{}, ", cs); 
    } 
    println!(""); 
} 

का उपयोग कर आप (ए) के साथ समस्या हुई - स्पष्ट प्रकार! अनबॉक्स किए गए बंद, कि हम |a, b, c| .. वाक्यविन्यास के साथ नियमित लैम्ब्डा अभिव्यक्तियों से प्राप्त होते हैं, अद्वितीय अज्ञात प्रकार होते हैं। कार्यों के लिए स्पष्ट वापसी प्रकार की आवश्यकता होती है, ताकि यह यहां काम न करे।

लौटने बंद के लिए कुछ समाधान:

  • उदाहरण (ए) के रूप में एक समारोह सूचक fn() का प्रयोग करें। अक्सर आपको एक बंद वातावरण की आवश्यकता नहीं होती है।
  • बंद बॉक्स को बंद करें। यह उचित है, भले ही iterators इस समय इसे कॉल करने का समर्थन नहीं करते हैं। तुम्हारी गलती नहीं।
  • बॉक्सर इटेटर
  • एक कस्टम इटरेटर संरचना लौटें। कुछ बॉयलरप्लेट की आवश्यकता है।

आप देख सकते हैं कि उदाहरण में (बी) हमें जीवनकाल से काफी सावधान रहना होगा।यह कहता है कि वापसी मूल्य Box<Iterator<Item=i32> + 'a> है, यह 'a क्या है? यह बॉक्स के अंदर कुछ भी कम से कम जीवनकाल की आवश्यकता है! हमने 'a को I::IntoIter पर बाध्य किया है - यह सुनिश्चित करता है कि हम इसे बॉक्स के अंदर रख सकें।

यदि आप केवल Box<Iterator<Item=i32>> कहते हैं तो यह 'static मान लेगा।

हमें स्पष्ट रूप से हमारे बॉक्स की सामग्री का जीवनकाल घोषित करना होगा। सुरक्षित रहने के लिए।

यह वास्तव में आपके कार्य के साथ मौलिक समस्या है। आपके पास यह है: DecodedRecords<'a, Box<Read>, Row>, F>

यह देखें कि, 'a! इस प्रकार कुछ उधार लेता है। समस्या यह है कि यह इनपुट से उधार नहीं लेता है। इनपुट पर 'a नहीं हैं।

आपको पता चलेगा कि यह फ़ंक्शन के दौरान आपके द्वारा बनाए गए मान से उधार लेता है, और फ़ंक्शन लौटने पर उस मान का जीवन समाप्त होता है। We cannot return DecodedRecords<'a> from the function, because it wants to borrow a local variable.

यहां से कहाँ जाना है? मेरा सबसे आसान जवाब सीएसवी करता है कि एक ही विभाजन करने के लिए होगा। एक भाग (संरचना या मूल्य) जो पाठक का मालिक है, और एक भाग (संरचना या मूल्य) जो पाठक से इटरेटर और उधार है।

शायद सीएसवी क्रेट का स्वामित्व वाला डिकोडर है जो पाठक के स्वामित्व को लेता है जो यह प्रसंस्करण कर रहा है। उस स्थिति में आप उधार लेने की परेशानी को दूर करने के लिए इसका उपयोग कर सकते हैं।

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

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