I'm trying to parse a file by stepping through sections of the file, one at a time. I have an iterator on the lines of the file, and the process I'm trying to use is as follows:
Search for the header of a block.
Loop through lines in the block, processing them.
Repeat until the search fails.
This isn't too hard to translate into Rust:
fn main() {
let script = include_str!("../ex.py");
let mut line_it = script.lines();
while let Some(header) = line_it.find(|s| s.starts_with("# /// ")) {
println!("HEADER: {:?}", header);
for line in line_it.take_while(|s| *s != "# ///") {
println!("CONTENT: {} {:?}", &line.len(), &line);
}
}
}
But the borrow checker objects because find borrows line_it, which was moved into take_while in the previous iteration of the loop. I think I can see why this is happening, but I don't understand it well enough to work out how to rework it to do what I want.
Can anyone help clarify for me and/or explain how I can fix this? Thanks.
Wow, really? It's that simple? (Goes away and checks.) Yes, it is! Thanks, that's awesome.
I'm glad it's that simple, but I have to say I really don't understand why that works. I looked at the source of by_ref and it doesn't do anything but return its self. I assume the magic is in the types, but I'm not 100% clear why. Is it the fact that by_ref takes &mut self, and so the iterator gets coerced to a mutable reference to itself, which can then be safely dropped because ownership stays with the original variable?
I thought I'd tried (&mut line_it).take_while(), and got some error that I didn't understand. But I just tried it again, and now it works - presumably because it's the inline version of by_ref.
I'm not sure if I'm more confused or less, at this point. But my code now works, which is the most important thing for now Thanks again.
The key is that if I: Iterator, then &mut I: Iterator as well -- and the implementation just forwards on the next method for example. So you can wrap &mut line_it with iterator adapters (the constructors of which take an iterator by value) without giving away line_it itself.
Similar patterns come up elsewhere: If you run into some
fn foo<W: Write>(writer: Write)
and don't want to give away your File or whatever, you can pass in &mut file because there's an implementation of Write for &mut W where W: Write.
Those re-borrow impls can be kind of difficult to notice, which makes discoverability a challenge. Once you know of them, it's easy to resolve or workaround ownership problems in existing trait APIs. I don't know what to do to make documentation better. I try to look for trait implementations on &T or &mut T when I need to do this kind of type juggling.
One of the most surprising impls IMHO is impl Read for &File. It's surprising because Read::read() receives &mut self! In other words, &mut &File is readable. You don't need exclusive access to the file handle to read from it, regardless of what the Read trait seems to indicate.