コンパイラーに案内してもらうと便利です。
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
、特性にサイズがないために、特性を返すことができないことです。つまり、Rustはその型に割り当てるスペースを認識していません。ローカル変数への参照も返すことができないため、返すこと&dyn Iterator
はスターターではありません。
インプリ特性
Rust 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());
}
タイプエイリアス
Asをリームによって指摘
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)
}
条件文の扱い
イテレータを条件付きで選択する必要がある場合は、可能なイテレータの1つを条件付きで反復するを参照してください。