Iterator (या किसी अन्य विशेषता) को वापस करने का सही तरीका क्या है?


114

निम्नलिखित रस्ट कोड संकलित करता है और बिना किसी समस्या के चलता है।

fn main() {
    let text = "abc";
    println!("{}", text.split(' ').take(2).count());
}

उसके बाद, मैंने कुछ इस तरह की कोशिश की .... लेकिन यह संकलन नहीं था

fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}

fn to_words(text: &str) -> &Iterator<Item = &str> {
    &(text.split(' '))
}

मुख्य समस्या यह है कि मुझे यकीन नहीं है कि फ़ंक्शन किस प्रकार का to_words()होना चाहिए। संकलक कहता है:

error[E0599]: no method named `count` found for type `std::iter::Take<std::iter::Iterator<Item=&str>>` in the current scope
 --> src/main.rs:3:43
  |
3 |     println!("{}", to_words(text).take(2).count());
  |                                           ^^^^^
  |
  = note: the method `count` exists but the following trait bounds were not satisfied:
          `std::iter::Iterator<Item=&str> : std::marker::Sized`
          `std::iter::Take<std::iter::Iterator<Item=&str>> : std::iter::Iterator`

इस रन को बनाने के लिए सही कोड क्या होगा? .... और मेरा ज्ञान अंतर कहाँ है?

जवाबों:


143

मैंने संकलक को मेरा मार्गदर्शन करने के लिए उपयोगी पाया है:

fn to_words(text: &str) { // Note no return type
    text.split(' ')
}

संकलन देता है:

error[E0308]: mismatched types
 --> src/lib.rs:5:5
  |
5 |     text.split(' ')
  |     ^^^^^^^^^^^^^^^ expected (), found struct `std::str::Split`
  |
  = note: expected type `()`
             found type `std::str::Split<'_, char>`
help: try adding a semicolon
  |
5 |     text.split(' ');
  |                    ^
help: try adding a return type
  |
3 | fn to_words(text: &str) -> std::str::Split<'_, char> {
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

संकलक के सुझाव और कॉपी-पेस्ट के बाद कि मेरी वापसी प्रकार के रूप में (थोड़ी सफाई के साथ):

use std::str;

fn to_words(text: &str) -> str::Split<'_, char> {
    text.split(' ')
}

समस्या यह है कि आप एक विशेषता को वापस नहीं कर सकते Iteratorक्योंकि एक विशेषता का आकार नहीं है। इसका मतलब यह है कि रस्ट को पता नहीं है कि टाइप के लिए कितना स्थान आवंटित करना है। आप किसी स्थानीय चर का संदर्भ नहीं लौटा सकते हैं , इसलिए, लौटना &dyn Iteratorएक गैर स्टार्टर है।

इम्प्लांट का निशान

1.26 जंग के रूप में, आप उपयोग कर सकते हैं impl trait:

fn to_words<'a>(text: &'a str) -> impl Iterator<Item = &'a str> {
    text.split(' ')
}

fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}

इस पर प्रतिबंध हैं कि इसका उपयोग कैसे किया जा सकता है। आप केवल एक ही प्रकार (कोई शर्त नहीं!) लौटा सकते हैं और इसका उपयोग नि: शुल्क फ़ंक्शन या अंतर्निहित कार्यान्वयन पर किया जाना चाहिए।

बॉक्स्ड

अगर आपको थोड़ी सी दक्षता खोने का मन नहीं है, तो आप एक वापसी कर सकते हैं Box<dyn Iterator>:

fn to_words<'a>(text: &'a str) -> Box<dyn Iterator<Item = &'a str> + 'a> {
    Box::new(text.split(' '))
}

fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}

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

नया प्रकार

use std::str;

struct Wrapper<'a>(str::Split<'a, char>);

impl<'a> Iterator for Wrapper<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        self.0.next()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

fn to_words(text: &str) -> Wrapper<'_> {
    Wrapper(text.split(' '))
}

fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}

उपनाम लिखें

जैसा कि रीम ने बताया

use std::str;

type MyIter<'a> = str::Split<'a, char>;

fn to_words(text: &str) -> MyIter<'_> {
    text.split(' ')
}

fn main() {
    let text = "word1 word2 word3";
    println!("{}", to_words(text).take(2).count());
}

बंदों से निपटना

जब impl Traitउपयोग के लिए उपलब्ध नहीं है, तो बंद करने से चीजें अधिक जटिल हो जाती हैं। क्लोज़र अनाम प्रकार बनाते हैं और इन्हें रिटर्न प्रकार में नाम नहीं दिया जा सकता है:

fn odd_numbers() -> () {
    (0..100).filter(|&v| v % 2 != 0)
}
found type `std::iter::Filter<std::ops::Range<{integer}>, [closure@src/lib.rs:4:21: 4:36]>`

कुछ मामलों में, इन क्लोजर को फ़ंक्शंस से बदला जा सकता है, जिन्हें नाम दिया जा सकता है:

fn odd_numbers() -> () {
    fn f(&v: &i32) -> bool {
        v % 2 != 0
    }
    (0..100).filter(f as fn(v: &i32) -> bool)
}
found type `std::iter::Filter<std::ops::Range<i32>, for<'r> fn(&'r i32) -> bool>`

और उपरोक्त सलाह का पालन करें:

use std::{iter::Filter, ops::Range};

type Odds = Filter<Range<i32>, fn(&i32) -> bool>;

fn odd_numbers() -> Odds {
    fn f(&v: &i32) -> bool {
        v % 2 != 0
    }
    (0..100).filter(f as fn(v: &i32) -> bool)
}

सशर्त से निपटना

यदि आपको सशर्त रूप से एक पुनरावृत्ति चुनने की आवश्यकता है, तो कई संभावित पुनरावृत्तियों में से एक पर सशर्त रूप से पुनरावृति देखें ।


धन्यवाद, इससे मुझे बहुत मदद मिली। संकलक गाइड को आपके लिए बहुत उपयोगी बताने के लिए "ट्रिक", मैं भविष्य में इसका उपयोग अवश्य करूँगा। ... और हाँ, यह गंभीर रूप से बदसूरत है! मुझे उम्मीद है कि आरएफसी इसे जारी करने वाले उम्मीदवार के लिए बनाता है।
13

8
जबकि रैपर प्रकार जटिलता को छिपाने के लिए अच्छा हो सकता है, मुझे typeइसके बजाय सिर्फ उपनाम का उपयोग करना बेहतर लगता है , क्योंकि एक न्यूटाइप का उपयोग करने का मतलब है कि आपका Iterator RandomAccessIteratorअंतर्निहित लक्षण लागू नहीं करेगा, भले ही अंतर्निहित Iterator करता हो।
रीम

4
हाँ! प्रकार एलियास जेनेरिक मापदंडों का समर्थन करते हैं। उदाहरण के लिए, कई पुस्तकालय type LibraryResult<T> = Result<T, LibraryError>एक सुविधा के रूप में करते हैं IoResult<T>, जो सिर्फ एक प्रकार का उपनाम है।
रीम

1
क्या आप स्पष्ट कर सकते हैं कि किसी को 'aजीवन भर क्यों जोड़ना है Box? इसका क्या मतलब है? मैंने हमेशा सोचा कि यह केवल सीमा के लिए था, "टी केवल कुछ के रूप में लंबे समय तक जीवित रहने पर निर्भर हो सकता है" कहने के लिए 'a
टर्कीले

1
@torkleyy शायद stackoverflow.com/q/27790168/155423 या stackoverflow.com/q/27675554/155423 आपके प्रश्न का उत्तर देगी? यदि नहीं, तो मैं आपको अपने प्रश्न के लिए खोज करने के लिए प्रोत्साहित करूंगा, और यदि आप इसे नहीं पा सकते हैं, तो एक नया पूछें।
15pm पर Shepmaster
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.