Reduzieren Sie den Iterator von Strings auf Vec <char> (oder sogar FromIterator <char>).
Wie der Titel schon sagt, habe ich ein Iteratorvon Strings und möchte ein a erzeugen Vec<char>, indem ich alle verkette (und dabei jedes Stringals eine Folge von Unicode-Skalarwerten betrachte .chars()). (Ich muss keinen String zwischen dem Strings oder so etwas verschachteln , obwohl ich verstehe, dass es nicht viel schwieriger wäre, z. B. itertools::interspersewie in anderen Antworten beschrieben .)
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
// ???
}
(Es wäre schön, aber nicht besonders notwendig, wenn ich die etwas allgemeinere Typensignatur implementieren könnte :)
pub fn flatten_strings<S>(ss: impl Iterator<Item=String>) -> S where S: FromIterator<char> {
// ???
}
Wie auch immer, hier ist ein einfacher Versuch:
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.flat_map(|s| s.chars()).collect()
}
Leider funktioniert dies nicht, da die charsRückgabe Charseinen Verweis auf das enthält, auf dem Stringsie basiert. Aber das Lambda hat das Eigentum an dem übernommen Stringund wird es fallen lassen, wenn es zurückkommt.
error[E0515]: cannot return value referencing function parameter `s`
--> src/main.rs:2:21
|
2 | ss.flat_map(|s| s.chars()).collect()
| -^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `s` is borrowed here
Ich kann dies beheben, indem ich einfach alle Strings in einem Zwischenvektor Stringsammle , so dass jemand alle s besitzt und einen Iterator von &Strings anstelle von Strings flach abbildet . Aber diesen Zwischenvektor von Strings zuzuweisen, scheint ineffizient zu sein und den Punkt der Verwendung von Rusts netten Iterator-Abstraktionen etwas zu vereiteln:
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.collect::<Vec<String>>().iter().flat_map(|s| s.chars()).collect()
}
Ebenso könnte ich eine Schleife handrollen, aber das scheint auch hässlich. Kann ich diese Funktion ohne zusätzliche Zuordnungen effizient implementieren, aber auch ohne Rusts Iterator-Abstraktionen zu hinterlassen?
Antworten
Das Hauptproblem besteht darin, dass der s: StringParameter im Abschluss gelöscht wird, bevor jemand das Charsdavon abhängige Objekt verwenden kann.
Hier sind zwei Möglichkeiten, um damit umzugehen. Der erste ist ausführlicher als Ihr Code, verwendet jedoch dieselbe Typensignatur. Der zweite &strbasiert auf einem Iterator, der die Funktion und alle ihre CharsObjekte überlebt .
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
let mut res = Vec::new();
for s in ss {
res.extend(s.chars());
}
res
}
pub fn flatten_strings2<'a>(ss: impl Iterator<Item=&'a str>) -> Vec<char> {
ss.flat_map(|s| s.chars()).collect()
}
Wenn Sie mit einer temporären Zuordnung leben können, können Sie jede Zeichenfolge Vec<char>im Verlauf der Liste in eine konvertieren und diese für die flat_map bereitstellen. Dadurch kann auch der allgemeinere Fall funktionieren. Sie können etwas allgemeiner vorgehen (wie von Sven Marnach vorgeschlagen ) und einfach eine impl Iterator<Item = char>ohne das Sammeln zurückgeben und dem Anrufer das Sammeln erlauben, wenn er möchte.
use std::collections::HashSet;
use core::iter::FromIterator;
pub fn flatten_strings(ss: impl Iterator<Item=String>) -> Vec<char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>()).collect()
}
pub fn flatten_strings2<S>(ss: impl Iterator<Item=String>) -> S where S: FromIterator<char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>()).collect()
}
pub fn flatten_strings3(ss: impl Iterator<Item=String>) -> impl Iterator<Item = char> {
ss.flat_map(|s| s.chars().collect::<Vec<_>>())
}
fn main() {
let v = vec!["A string ".to_string(), "another".to_string() ];
println!("{:?}",flatten_strings(v.clone().into_iter()));
let h: HashSet<char> = flatten_strings2(v.clone().into_iter());
println!("{:?}",h);
let h: HashSet<char> = flatten_strings3(v.clone().into_iter()).collect();
println!("{:?}",h);
}