Yineleyiciyi (veya başka bir özelliği) döndürmenin doğru yolu nedir?


114

Aşağıdaki Rust kodu herhangi bir sorun olmadan derlenir ve çalışır.

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

Ondan sonra böyle bir şey denedim ... ama derlemedi

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

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

Asıl sorun, işlevin hangi dönüş türüne to_words()sahip olması gerektiğinden emin değilim . Derleyici şunu söylüyor:

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`

Bunu çalıştırmak için doğru kod ne olabilir? .... ve bilgi açığım nerede?

Yanıtlar:


143

Derleyicinin bana rehberlik etmesine izin vermeyi yararlı buldum:

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

Derleme şunları verir:

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> {
  |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Derleyicinin önerisini takiben ve bunu dönüş tipim olarak kopyalayıp yapıştırarak (biraz temizleme ile):

use std::str;

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

Sorun şu ki, bir özelliğin Iteratorboyutu olmadığı için olduğu gibi bir özelliği geri dönemezsiniz . Bu, Rust'un tür için ne kadar alan ayıracağını bilmediği anlamına gelir. Sen , ya yerel bir değişkene gönderimi döndüremez böylece dönen, &dyn Iteratorolmayan bir marş olduğunu.

Impl özelliği

Rust 1.26'dan itibaren şunları kullanabilirsiniz 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());
}

Bunun nasıl kullanılacağına dair kısıtlamalar vardır. Yalnızca tek bir tür döndürebilirsiniz (koşulsuz!) Ve serbest bir işlevde veya doğal bir uygulamada kullanılmalıdır.

Kutulu

Biraz verimlilik kaybetmeyi düşünmüyorsanız, şunu döndürebilirsiniz 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());
}

Bu, dinamik gönderime izin veren birincil seçenektir . Yani, kodun tam olarak uygulanmasına derleme zamanında değil çalışma zamanında karar verilir. Bu, bir koşula bağlı olarak birden fazla somut yineleyici türü döndürmeniz gereken durumlar için uygun olduğu anlamına gelir.

Yeni tip

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());
}

Takma adı yazın

Reem tarafından işaret edildiği gibi

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());
}

Kapanışlarla başa çıkmak

Ne zaman impl Traitkullanıma hazır değildir, kapanışları daha karmaşık şeyler yapmak. Kapanışlar anonim türler oluşturur ve bunlar dönüş türünde adlandırılamaz:

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]>`

Bazı durumlarda, bu kapamalar, adlandırılabilen işlevlerle değiştirilebilir:

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>`

Ve yukarıdaki tavsiyeye uyarak:

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)
}

Koşullarla başa çıkmak

Koşullu olarak bir yineleyici seçmeniz gerekiyorsa, olası birkaç yineleyiciden birinin Koşullu yineleme bölümüne bakın .


Teşekkür ederim, bu bana çok yardımcı oldu. Derleyicinin size rehberlik etmesine izin veren "numara" oldukça kullanışlıdır, gelecekte kesinlikle kullanacağım. ... ve evet, bu gerçekten çirkin! Umarım RFC sürüm adayına ulaşır.
forgemo

8
Sarmalayıcı türleri karmaşıklığı gizlemek için güzel olsa da, typebunun yerine yalnızca takma adlar kullanmayı daha iyi buluyorum , çünkü yeni bir tür kullanmak Yineleyicinizin RandomAccessIteratortemel Yineleyici yapsa bile nitelikleri uygulamayacağı anlamına gelir .
Reem

4
Evet! Tür takma adları, genel parametreleri destekler. Örneğin, birçok kitaplık , aynı zamanda bir tür takma adı olan, type LibraryResult<T> = Result<T, LibraryError>benzer bir kolaylık sağlar IoResult<T>.
Reem

1
Birinin neden bir 'aömür uzatması gerektiğini açıklar Boxmısınız? Bu ne anlama geliyor? Her zaman bunun sadece sınırlar için olduğunu düşünmüşümdür, "T ancak en az onun kadar uzun yaşayan bir şeye bağlı olabilir" demek 'a.
torkleyy

1
@torkleyy belki stackoverflow.com/q/27790168/155423 veya stackoverflow.com/q/27675554/155423 sorunuzu yanıtlar mı? Değilse, sorunuzu aramanızı ve bulamazsanız yeni bir soru sormanızı öneririm.
Shepmaster
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.