Jestem nowy w Rust i czytanie Rust Programming Language, aw Error Handling sekcja there is a "case study" opisujący program do odczytu danych z pliku CSV, używając csv
i rustc-serialize
bibliotek (używając getopts
do analizy argumentów).Powrót leniwe iterator, który zależy od danych przydzielonych w ramach funkcji
Autor pisze funkcję search
, która przechodzi przez wiersze pliku csv za pomocą obiektu csv::Reader
i zbiera te wpisy, których pole "city" jest zgodne z określoną wartością do wektora i zwraca je. Podjąłem nieco inne podejście niż autor, ale nie powinno to wpłynąć na moje pytanie. My (roboczy) funkcja wygląda tak:
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()
}
gdzie typ DataRow
jest tylko zapis,
#[derive(Debug, RustcDecodable)]
struct DataRow {
country: String,
city: String,
accent_city: String,
region: String,
population: Option<u64>,
latitude: Option<f64>,
longitude: Option<f64>
}
Teraz, autor stawia, jak strasznych „ćwiczenie dla czytelnika”, problem modyfikowania tej funkcji, aby zwrócić iterator zamiast wektora (eliminując wywołanie do collect
). Moje pytanie brzmi: jak to w ogóle można zrobić i jakie są najbardziej zwięzłe i idiomatyczne sposoby robienia tego?
Prosta próba że myślę dostaje podpis typu prawo jest
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))
}
wrócę obiekt cecha typu Box<Iterator<Item=DataRow> + 'a>
tak aby nie trzeba wystawiać wewnętrzną Filter
rodzaj, a gdzie życie 'a
została wprowadzona tylko po to, aby uniknąć konieczności tworzenia lokalnego klona o numerze city
. Ale to się nie kompiluje, ponieważ reader
nie żyje wystarczająco długo; jest przydzielany na stosie, a więc jest zwalniany, gdy funkcja zwraca.
Domyślam się, że oznacza to, że reader
musi zostać przydzielone na stercie (tj. W pudełku) od początku, lub w jakiś sposób przeniesione ze stosu przed zakończeniem funkcji. Jeśli zwróciłem zamknięcie, jest to dokładnie problem, który zostałby rozwiązany przez zamknięcie go na move
. Ale nie wiem, jak zrobić coś podobnego, gdy nie otrzymuję funkcji. Próbowałem zdefiniować niestandardowy typ iteratora zawierający potrzebne dane, ale nie mogłem go uruchomić, a on ciągle robił się coraz brzydszy i bardziej wymyślny (nie rób zbyt dużo tego kodu, włączam go tylko do pokazać ogólny kierunek moich prób):
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 wydaje się dotyczyć tego samego problemu, ale autor odpowiedzi rozwiązuje go poprzez odnośnych danych static
, które nie sądzę, jest alternatywą dla tego pytanie.
Używam Rust 1.10.0, aktualna stabilna wersja z pakietu Arch Linux rust
.
Chciałbym podziękować za zadanie zadawanego pytania. Wielu odwiedzających często nie okazuje zbyt wielu przygotowań, a tym bardziej nie pyta po raz pierwszy. Sława! – Shepmaster
@Shepmaster Dzięki, starałem się jak najlepiej napisać dobre pierwsze pytanie, i wydaje się, że mam na to dobrze wykwalifikowaną odpowiedź! Mimo to, dziękuję za twoje korekty stylistyczne. –