Ich bin neu in Rust in der Funktion zugeordnet auf Daten abhängig und Handling und im Fehler Die Rust Programming Language Lesen Abschnitt there is a "case study" ein Programm beschreiben, Daten aus einer CSV-Datei zu lesen, die csv
mit und rustc-serialize
Bibliotheken (unter Verwendung von getopts
zum Parsen von Argumenten).Rückkehr faul Iterator,
Der Autor schreibt eine Funktion search
, die durch die Zeilen der csv-Datei mit einem csv::Reader
-Objekt schreitet und diejenigen Einträge sammelt, deren "Stadt" -Feld einen bestimmten Wert in einen Vektor einfügt und zurückgibt. Ich habe einen etwas anderen Ansatz gewählt als der Autor, aber das sollte meine Frage nicht beeinflussen. Mein (Arbeits-) Funktion sieht wie folgt aus:
extern crate csv;
extern crate rustc_serialize;
use std::path::Path;
use std::fs::File;
fn search<P>(data_path: P, city: &str) -> Vec<DataRow>
where P: AsRef<Path>
{
let file = File::open(data_path).expect("Opening file failed!");
let mut reader = csv::Reader::from_reader(file).has_headers(true);
reader.decode()
.map(|row| row.expect("Failed decoding row"))
.filter(|row: &DataRow| row.city == city)
.collect()
}
wo der DataRow
Typ ist nur ein Datensatz,
#[derive(Debug, RustcDecodable)]
struct DataRow {
country: String,
city: String,
accent_city: String,
region: String,
population: Option<u64>,
latitude: Option<f64>,
longitude: Option<f64>
}
Nun, der Autor stellt, als die gefürchtete „Übung für den Leser“, das Problem Ändern dieser Funktion, um einen Iterator anstelle eines Vektors zurückzugeben (Eliminieren des Aufrufs an collect
). Meine Frage ist: Wie kann das überhaupt gemacht werden und was sind die prägnantesten und idiomatischsten Wege, dies zu tun?
Ein einfacher Versuch, die ich denke, wird die Art Signatur richtig ist
fn search_iter<'a,P>(data_path: P, city: &'a str)
-> Box<Iterator<Item=DataRow> + 'a>
where P: AsRef<Path>
{
let file = File::open(data_path).expect("Opening file failed!");
let mut reader = csv::Reader::from_reader(file).has_headers(true);
Box::new(reader.decode()
.map(|row| row.expect("Failed decoding row"))
.filter(|row: &DataRow| row.city == city))
}
ich ein Merkmal Objekt vom Typ zurückgeben Box<Iterator<Item=DataRow> + 'a>
, um nicht den internen Filter
Typen zu haben zu belichten, und wo die Lebensdauer 'a
wird nur eingeführt, um zu vermeiden, dass ein lokaler Klon von city
erstellt werden muss. Aber das kompiliert nicht, weil reader
nicht lange genug lebt; es ist auf dem Stapel zugeordnet und wird daher freigegeben, wenn die Funktion zurückkehrt.
Ich denke, das bedeutet, dass reader
auf den Haufen (d. H. Boxed) von Anfang an zugewiesen werden muss, oder irgendwie aus dem Stapel verschoben, bevor die Funktion endet. Wenn ich eine Schließung zurücksende, ist dies genau das Problem, das gelöst werden würde, indem man es zu einer move
Schließung macht. Aber ich weiß nicht, wie ich etwas ähnliches machen soll, wenn ich keine Funktion zurückgebe. Ich habe versucht, einen benutzerdefinierten Iteratortyp zu definieren, der die benötigten Daten enthält, aber ich konnte es nicht zum Laufen bringen, und es wurde immer hässlicher und ausgeklügelter (machen Sie nicht zu viel von diesem Code, ich schließe ihn nur ein zeigen die allgemeine Richtung meiner Versuche):
fn search_iter<'a,P>(data_path: P, city: &'a str)
-> Box<Iterator<Item=DataRow> + 'a>
where P: AsRef<Path>
{
struct ResultIter<'a> {
reader: csv::Reader<File>,
wrapped_iterator: Option<Box<Iterator<Item=DataRow> + 'a>>
}
impl<'a> Iterator for ResultIter<'a> {
type Item = DataRow;
fn next(&mut self) -> Option<DataRow>
{ self.wrapped_iterator.unwrap().next() }
}
let file = File::open(data_path).expect("Opening file failed!");
// Incrementally initialise
let mut result_iter = ResultIter {
reader: csv::Reader::from_reader(file).has_headers(true),
wrapped_iterator: None // Uninitialised
};
result_iter.wrapped_iterator =
Some(Box::new(result_iter.reader
.decode()
.map(|row| row.expect("Failed decoding row"))
.filter(|&row: &DataRow| row.city == city)));
Box::new(result_iter)
}
This question scheint das gleiche Problem zu befassen, sondern der Verfasser der Antwort löst sie, indem sie die betreffenden Daten machen static
, die ich nicht glaube, ist eine Alternative für diese Frage.
Ich verwende Rust 1.10.0, die aktuelle stabile Version von Arch Linux-Paket rust
.
Ich möchte Ihnen danken, dass Sie eine gut konstruierte Frage gestellt haben. Viele häufige Besucher zeigen nicht so viel Vorbereitung, geschweige denn Erstsucher. Kudos! – Shepmaster
@Shempmaster Danke, ich habe mein Bestes gegeben, um eine gute erste Frage zu schreiben, und es scheint, ich habe eine gut qualifizierte Antwort dafür bekommen! Trotzdem, danke für deine stilistischen Korrekturen. –