Я счел полезным позволить компилятору направлять меня:
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
является запуском.
Impl черта
Начиная с 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());
}
Псевдоним типа
Как указал Рим
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)
}
Работа с условными выражениями
Если вам нужно условно выбрать итератор, обратитесь к разделу Условный перебор одного из нескольких возможных итераторов .