Я вважаю корисним дозволити компілятору керувати мною:
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
не є початковим.
Impl риса
Станом на Іржу 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)
}
Справа з умовними умовами
Якщо вам потрібно умовно вибрати ітератор, зверніться до Умовно ітерації одного з декількох можливих ітераторів .