I'm not sure what you want. If you want to create several structs I would do something like this.
I think it should be possible to create the struct's name from something like format!("ArrayIter{}", $size) but I don't now how.
The use case is to try to write an parser without any allocation.
The program should work like this:
read a bunch of data from a file to an array
search for some specific parts in the array (copy the data between "start" and "end" in a second array)
2b. if the array is processed, read new data
return what I found
For the search part I like to iterate over the array, but I found no easy way to store the Iterator in a struct an now tried to build that part on my own.
Well if you want to omit allocations I would try to borrow the specific parts, e.g.:
let data = [...];
let specific = &data[start..pos]; // specific is now a slice
for element in specific.iter() {
// ...
}
I think this should solve the problem. However, if you are going to write a parser I highly recommend using a parser library like nom which allows you exactly what you are trying to do.
Thanks for your advice. I will have a look at nom.
The problem with the borrow of specific parts starts when I try to store it in a struct. I always get crazy with the lifetimes. Never found a solution. And as far as I understood, it's not that easy to solve.
With some experience handling lifetimes loses its smell of magic.
The big thing you have to consider is why Rust needs lifetimes. Rust guarantees that a borrow can not outlive its owner. So if you store a borrow somewhere rustc must be able to track this. Rust is designed to be able to track lifetimes solely by reading the type and function signatures.
You may be going about this in the wrong way. If you want to create a parser that doesn't do any allocations then you need to consume the parsed data the moment it is parsed. This is normally done by returning an iterator or via callbacks which are invoked during the parsing process. Storing the results in an array and then processing the results can become tricky and problematic (e.g. how big should I make my buffer?).
I'd break the searching process up into two parts. In the first you'll create some custom Parser struct which borrows from the original input source.
pub struct Parser<'input> {
src: &'input str,
current_location: usize,
}
impl<'input> Iterator for Parser<'input> {
type Item = (Span, Foo<'input>);
fn next(&mut self) -> Option<Self::Item> {
// parse the next item, moving `current_location` further along the buffer
// and using it to generate a `Span` for that item (handy for reporting errors)
}
}
pub enum Foo<'input> {
Bar(usize),
Baz(&'input str),
}
/// The byte indexes defining where a particular item lies in the original source.
pub struct Span {
pub start: usize,
pub end: usize,
}
Then you define your search as a function that takes any iterator over Foo<'input>s.
This is the approach I took when writing the gcode crate, a #[no_std] crate which parses gcode programs designed for use on microcontrollers. Feel free to have a look at the repo on GitHub if you want to see how I did this.