Struggling with lifetimes

Hi all,

I can get out of this error:

  --> src/reader.rs:80:52
   |
80 |         let rec = match self.reader.layout.rec_map.get_mut(rec_id) {
   |                                                    ^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 65:45...
  --> src/reader.rs:65:46
   |
65 |     fn next(&mut self) -> Option<Self::Item> {
   |                                              ^
note: ...so that reference does not outlive borrowed content


for this code:


//! Define a data field with its name, description, type and length

use std::error::Error;
use std::io::{BufReader,BufRead,Result};
use std::fs::File;
use std::slice::{Iter, IterMut};

use record::Record;
use layout::Layout;

// function to  get the record ID from the whole line read from the target file
type RecordMapper = fn(&str) -> &str;

pub struct Reader {
    pub rbf_file: String,
    pub layout: Layout,
    pub mapper: RecordMapper,
}

impl Reader {
    // ctor
    pub fn new(rbf_file: &str, layout: Layout, mapper: RecordMapper) -> Reader 
    {
        Reader {
            rbf_file: rbf_file.to_string(),
            layout: layout,
            mapper: mapper,
        }

    }
    
}

// used to loop on records
pub struct ReaderIterator<'a> {
    reader: &'a mut Reader,
    bufreader: BufReader<File>,
    line: String,
}

impl<'a> IntoIterator for &'a mut Reader {
    type IntoIter = ReaderIterator<'a>;
    type Item = &'a mut Record;
    
    // just initialize the reader iterator structure
    fn into_iter(self) -> Self::IntoIter {
        // open file for reading
        let mut bufreader = match File::open(&self.rbf_file) {
            // if ok, create a new BufReader to read the file line by line
            Ok(f) => BufReader::new(f),
            // The `description` method of `io::Error` returns a string that
            // describes the error            
            Err(why) => panic!("couldn't open {}: {}", self.rbf_file, why.description()),            
        };

        // this struct will handle all data for iteration
        ReaderIterator { reader: self, bufreader: bufreader, line: String::new() }
    }
}

impl<'a> Iterator for ReaderIterator<'a> {
    type Item = &'a mut Record;

    fn next(&mut self) -> Option<Self::Item> {
        // read one line of text
        match self.bufreader.read_line(&mut self.line) {
            // No bytes read? This is EOF and we must end the iteration
            Ok(bytes_read) => if bytes_read == 0 {
                return None;
            },
            // error reading bytes
            Err(why) => panic!("error {} when reading file {}", why.description(), self.reader.rbf_file),
        };

        // get the record ID from line
        let rec_id = (self.reader.mapper)(&self.line);

        // get the record ref from layout
        let rec = match self.reader.layout.rec_map.get_mut(rec_id) {
            None => panic!("couldn't find record ID {} in file {}", rec_id, self.reader.rbf_file),
            Some(v) => v,
        };

        // set all fields
        rec.set_value(&self.line);

        Some(rec)

    }
}

I need to return a Record ref but I cannot write the right lifetime. Any hint?

Thanks a lot for any help.

This won't work in safe Rust because you're trying to return mutable references. Compiler forbids this as otherwise the caller of your iterator could, in theory, hold multiple mutable references to the same Record (Rust doesn't know whether your iterator produces a different Record each time).

Do you need to return a mutable ref?

Edit: if you search for "rust streaming iterator" you'll see more discussion on this topic.

With iterators you have to keep in mind that this is valid:

let a = iter.next();
let b = iter.next();

use(a, b);

So Rust wants to have a guarantee that whatever you return won't be able to change for the entire duration of the iteration, and calling next() won't invalidate any other result from any previous next() call.

@sfackler has a crate for streaming iterators, https://crates.io/crates/streaming-iterator, that @dandyvica may find useful.

3 Likes

Thanks all for your replies.

I had a look to StreamingIterator, but I found it quite cumbersome to use. I just implemented a next() method in my Reader struct, which is usable with a simple while loop.

Not the most idiomatic way I guess, but much simpler.