Я новичок в Русте и чтение Ржавчина Язык программирования, и в Error Handling раздел there is a "case study" с описанием программы для чтения данных из файла CSV с помощью csv
и rustc-serialize
(с использованием getopts
для анализа аргументов).Возврат ленивый итератор, который зависит от данных, выделенных в рамках функции
Автор пишет функцию search
, которая выполняет шаги в строках csv-файла с использованием объекта csv::Reader
и собирает те записи, чье поле «city» соответствует указанному значению в векторе и возвращает его. Я использовал несколько иной подход, чем автор, но это не должно влиять на мой вопрос. Моя функция (работа) выглядит следующим образом:
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()
}
где DataRow
типа только запись,
#[derive(Debug, RustcDecodable)]
struct DataRow {
country: String,
city: String,
accent_city: String,
region: String,
population: Option<u64>,
latitude: Option<f64>,
longitude: Option<f64>
}
Теперь, автор ставит, как страшные «упражнения для читателя», проблемы изменения этой функции для возврата итератора вместо вектора (исключая вызов collect
). Мой вопрос: как это можно сделать вообще, и каковы самые сжатые и идиоматические способы сделать это?
Простой попытка, что я думаю, что получает тип подпись права является
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))
}
я возвращаю признак объект типа Box<Iterator<Item=DataRow> + 'a>
так, чтобы не надо подвергать внутренний Filter
тип, и где время жизни 'a
введен только для того, чтобы не сделать локальный клон city
. Но это не скомпилируется, потому что reader
не проживает достаточно долго; он выделяется в стеке и поэтому освобождается при возврате функции.
Я предполагаю, что это означает, что reader
должно быть выделено на кучу (то есть в коробке) с самого начала или каким-то образом сдвинуто со стека до завершения функции. Если бы я возвращал закрытие, это именно та проблема, которая была бы решена путем закрытия move
. Но я не знаю, как сделать что-то подобное, когда я не возвращаю функцию. Я попытался определить тип пользовательского итератора, содержащий необходимые данные, но я не мог заставить его работать, и он все более уродливым и более изобретательным (не делайте слишком много этого кода, я включаю его только в показать общее направление моих попыток):
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, кажется, относится с той же проблемой, но автор ответа решает его путем заинтересованных данных static
, которые я не думаю, что альтернатива для этого вопрос.
Я использую Rust 1.10.0, текущую стабильную версию из пакета Arch Linux rust
.
Я хотел бы поблагодарить вас за то, что вы задали хороший вопрос. Многие частые посетители не показывают столько подготовки, а тем более не в первый раз. Престижность! – Shepmaster
@Shepmaster Спасибо, я изо всех сил старался написать хороший первый вопрос, и, похоже, я получил для него квалифицированный ответ! Тем не менее, спасибо за ваши стилистические исправления. –