1

I have a struct, with two fields, one is a String and another is a string slice referencing the previous member. When implementing next I have to omit the lifetime operator and because of this I can't implement and iterator for the described struct.

Here is the code

struct A<'a> {
    a: &'a str,
}

struct B<'a> {
    b: String,
    a: Option<A<'a>>,
}

impl<'a> B<'a> { // this works fine
    fn next_(&'a mut self) -> Option<()> {
        self.a = Some(A { a: &self.b });

        None
    }
}

impl<'a> Iterator for B<'a> {
    type Item = ();
    fn next(&'a mut self) -> Option<()> { // but this fails because Iterator trait don't expect 'a here
        self.a = Some(A { a: &self.b });

        None
    }
}

Here is the error

error[E0308]: method not compatible with trait
   --> src/main.rs:131:9
    |
131 |         fn next(&'a mut self) -> Option<()> {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
    |
    = note: expected fn pointer `fn(&mut test::B<'a>) -> std::option::Option<_>`
               found fn pointer `fn(&'a mut test::B<'a>) -> std::option::Option<_>`
note: the anonymous lifetime #1 defined on the method body at 131:9...
   --> src/main.rs:131:9
    |
131 | /         fn next(&'a mut self) -> Option<()> {
132 | |             self.a = Some(A { a: &self.b });
133 | |
134 | |             None
135 | |         }
    | |_________^
note: ...does not necessarily outlive the lifetime `'a` as defined on the impl at 129:10
   --> src/main.rs:129:10
    |
129 |     impl<'a> Iterator for B<'a> {

I'm trying to iterate over words of a file, avoid copying. Here is my code

use std::fs;
use std::io::{self, BufRead};
use std::path;
fn main() {}

#[derive(Debug)]
struct Input<'a> {
    word: &'a str,
    remaining: &'a str,
}

impl<'a> Input<'a> {
    fn new(s: &str) -> Input {
        Input {
            word: "",
            remaining: s,
        }
    }
}

impl<'a> Iterator for Input<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        // This seems over edge cased
        let s = self.remaining;
        let mut it = s.chars().enumerate();

        while let Some((i, c)) = it.next() {
            if c.is_whitespace() {
                while let Some((j, c)) = it.next() {
                    if !c.is_whitespace() {
                        self.word = &s[..i];
                        self.remaining = &s[j..];
                        return Some(self.word);
                    }
                }
                self.word = &s[..i];
                self.remaining = "";
                return Some(self.word);
            }
        }

        self.word = &s;
        self.remaining = "";

        if self.word.is_empty() {
            return None;
        }

        Some(self.word)
    }
}

struct InputFile<'a> {
    reader: io::BufReader<fs::File>,
    line: String,
    input: Option<Input<'a>>,
}

impl<'a> InputFile<'a> {
    fn open(path: &str) -> io::Result<InputFile> {
        Ok(InputFile {
            reader: io::BufReader::new(fs::File::open(path)?),
            line: String::new(),
            input: None,
        })
    }
}

impl<'a> Iterator for InputFile<'a> {
    type Item = String;
    fn next(&mut self) -> Option<String> {
        while let Ok(readed) = self.reader.read_line(&mut self.line) {
            //self.input = Some(Input::new(&self.line));
            self.line.clear();
            if readed == 0 {
                break;
            }
        }

        None
    }
}

But I can't implement InputFile iterator as I was expecting to.

geckos
  • 5,687
  • 1
  • 41
  • 53
  • Yeah, I'm seeing that I hit the self referential struct case in Rust, I'll look for alternative using offsets. It happens that we (or me) are so used with higher (and unsafe) abstractions that forget about the good and old integer – geckos Aug 09 '20 at 10:06
  • 1
    Although the question in the title is slightly different (the answer being that the `Iterator` was not designed to support a bounded lifetime of `&mut self`), the source of the problem is the one in the linked question. – E_net4 Aug 09 '20 at 10:09
  • 1
    I would rather say this is a duplicate of [How do I write an iterator that returns references to itself?](https://stackoverflow.com/questions/30422177/how-do-i-write-an-iterator-that-returns-references-to-itself), although the other one is also relevant. – trent Aug 09 '20 at 16:51
  • Thanks trentcl, a very instructive answer. I end up using an usize offset value in my iterator so it doesn't need a self reference – geckos Aug 09 '20 at 17:54

0 Answers0