tl;dr in Rust, I want a struct to provide an Iterator on a contained iterable struct member but to interrogate the iterated values before returning them
General Overview
- A "wrapper" struct contains a "specialized container".
- the "wrapper" should implement an Iterator function
next
- within
next
the "wrapper" will call the "specialized container"next
, - the "wrapper" will analyze the value returned from the "specialized container"
- the "wrapper" will return the value to the caller
Specific Example
My specific case deals with rust crate evtx
. I would like to do the following:
- my "wrapper" struct,
RecordsReader
, contains anEvtxParser
. - the
RecordsReader
implements Iterator functioninto_iter
andnext
which returns astd::result::Result<SerializedEvtxRecord<String>, EvtxError>
- within function
RecordsReader::next
a. it callsself.evtxparser.records()
, creates an iterator
b. the result ofself.evtxparser.records().next()
is aSerializedEvtxRecord<String>
. That instance is analyzed and various statistics about it stored withinRecordsReader
instance.
c. theSerializedEvtxRecord<String>
instance is then returned to the caller ofrecordsreader.next()
.
d. the state of the iterator created byself.evtxparser.records()
is maintained between calls torecordsreader.next()
. (how to do this part?)
Partial rust code (rust playground (will not compile because evtx crate is not available)):
use ::evtx::{
err::EvtxError,
EvtxParser,
SerializedEvtxRecord,
};
use std::fs::File;
use std::path::PathBuf;
pub type EvtxRS = SerializedEvtxRecord<String>;
pub type ResultEvtxRS = std::result::Result<EvtxRS, EvtxError>;
pub type EParser = EvtxParser<File>;
struct RecordsReader {
pub eparser: EParser,
}
pub struct RecordsReaderIterator<'a> {
recordsreader: &'a mut RecordsReader,
index: usize, // hack iterator state
}
impl<'a> Iterator for RecordsReaderIterator<'a> {
type Item = ResultEvtxRS;
fn next(&mut self) -> Option<ResultEvtxRS> {
// ----------------------------------------------
// HOW TO AVOID RECREATING THE self.recordsreader.parser.records()
// ON EVERY CALL OF next() ?
// Or, at least, make next() into *O(n)* time?
// ----------------------------------------------
// Hack iterator approach; O(n^2)
let result =
self.recordsreader.eparser.records().nth(self.index);
self.index += 1;
// ... store other statistics about `result` before returning it ...
result
}
}
impl<'a> IntoIterator for &'a mut RecordsReader {
type Item = ResultEvtxRS;
type IntoIter = RecordsReaderIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
RecordsReaderIterator {
recordsreader: self,
index: 0,
}
}
}
fn main() {
let pathb = PathBuf::from("file.evtx");
let mut eparser: EParser = EParser::from_path(pathb).unwrap();
let mut recordsreader: RecordsReader = RecordsReader {
eparser,
};
for recordopt in recordsreader.into_iter() {
match recordopt {
ResultEvtxRS::Ok(record) => {
eprintln!("record[{:?}] {:?}", record.event_record_id, record.timestamp);
}
ResultEvtxRS::Err(err) => {
eprintln!("ERROR: {}", err);
}
}
}
}
The prior code functions BUT next()
is O(n^2) time!
That's because the state of the self.recordsreader.eparser.records()
iterator cannot be saved. That "inner iterator" must be recreated on every call to next()
.
How can I implement an Iterator function for RecordsReader
that does not have to reset the "inner iterator" state of eparser.records()
on every call?
Barring that question, how can next()
become O(n) time?
Similar Questions
I read the Answers to these Questions. As far as I could determine, these are similar but not quite the same as this Question. (I tried to implement many of these Answers but was unable)