Ich fand es nützlich, mich vom Compiler führen zu lassen:
fn to_words(text: &str) { // Note no return type
text.split(' ')
}
Kompilieren ergibt:
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> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Befolgen Sie den Vorschlag des Compilers und fügen Sie ihn als Rückgabetyp ein (mit einer kleinen Bereinigung):
use std::str;
fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}
Das Problem ist, dass Sie ein Merkmal nicht zurückgeben können, Iterator
da ein Merkmal keine Größe hat. Das bedeutet, dass Rust nicht weiß, wie viel Speicherplatz für den Typ reserviert werden soll. Sie können auch keinen Verweis auf eine lokale Variable zurückgeben , daher ist die Rückgabe &dyn Iterator
kein Starter.
Impl Merkmal
Ab Rust 1.26 können Sie Folgendes verwenden 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());
}
Es gibt Einschränkungen, wie dies verwendet werden kann. Sie können nur einen einzigen Typ zurückgeben (keine Bedingungen!) Und er muss für eine freie Funktion oder eine inhärente Implementierung verwendet werden.
Boxed
Wenn es Ihnen nichts ausmacht, ein wenig an Effizienz zu verlieren, können Sie Folgendes zurückgeben 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());
}
Dies ist die primäre Option, die einen dynamischen Versand ermöglicht . Das heißt, die genaue Implementierung des Codes wird zur Laufzeit und nicht zur Kompilierungszeit festgelegt. Dies bedeutet, dass dies für Fälle geeignet ist, in denen Sie mehr als einen konkreten Iteratortyp basierend auf einer Bedingung zurückgeben müssen.
Neuer Typ
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());
}
Geben Sie einen Alias ein
Wie von Reem hervorgehoben
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());
}
Umgang mit Verschlüssen
Wenn sie impl Trait
nicht zur Verfügung stehen, erschweren Verschlüsse die Sache. Durch Verschlüsse werden anonyme Typen erstellt, die im Rückgabetyp nicht benannt werden können:
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]>`
In bestimmten Fällen können diese Verschlüsse durch Funktionen ersetzt werden, die wie folgt benannt werden können:
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>`
Und folgen Sie den obigen Ratschlägen:
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)
}
Umgang mit Bedingungen
Wenn Sie einen Iterator unter bestimmten Bedingungen auswählen müssen, lesen Sie Bedingte Iteration über einen von mehreren möglichen Iteratoren .
type
hilfreich sein können, um die Komplexität zu verbergen, finde ich es besser, stattdessen nur Aliase zu verwenden, da die Verwendung eines neuen Typs bedeutet, dass Ihr Iterator keine Merkmale implementiert, wie diesRandomAccessIterator
auch der zugrunde liegende Iterator tut.type LibraryResult<T> = Result<T, LibraryError>
eine Annehmlichkeit ähnlichIoResult<T>
, die auch nur ein Typ-Alias ist.'a
Leben lang hinzufügen mussBox
? Was bedeutet das? Ich dachte immer, dies sei nur für Grenzen gedacht, um zu sagen, "T kann nur von etwas abhängen, das mindestens so lange lebt wie'a
".