I'm trying to write a simple text parsing utility working on a &str
. I'd like to implement a function that accepts a closure object and a separator string, which returns a type that implements Iterator
that continuously calls the closure object and eats the separator on each invocation of next()
Since I don't know what type the iterated item is (that is up to the closure), I'd like to generalize this, so it's signature will be FnMut(&mut Parser) -> Option<T>
. Since it may optionally borrow from the string stored by the parser, I need to insert the appropriate lifetimes.
This is what I have so far:
struct Parser<'a> {
string: &'a str,
}
impl<'a> Parser<'a> {
// ...
fn at_end(&self) -> bool {
// returns whether we're at the end of the string
false
}
fn expect(&mut self, _s: &str) -> &mut Self {
// expects the next part to be `s` and advances the string
// ...
self
}
fn parse_iter<'b, T: 'a, F: FnMut(&'a mut Self) -> Option<T>>(
&'a mut self,
separator: &'b str,
f: F,
) -> ParseIter<'a, 'b, T, F> {
ParseIter {
parser: self,
func: f,
sep: separator,
skip: false,
}
}
}
struct ParseIter<'a, 'b, T, F>
where
T: 'a,
F: FnMut(&'a mut Parser<'a>) -> Option<T>
{
parser: &'a mut Parser<'a>,
func: F,
sep: &'b str,
skip: bool,
}
impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
where
T: 'a,
F: FnMut(&'a mut Parser<'a>) -> Option<T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.parser.at_end() {
return None;
}
if std::mem::replace(&mut self.skip, true) {
self.parser.expect(self.sep);
}
(self.func)(self.parser) // error here!
}
}
That last line of code, invoking the closure, produces an error:
error: lifetime may not live long enough
--> util\src\parser.rs:156:9
|
140 | impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F>
| -- lifetime `'a` defined here
...
147 | fn next(&mut self) -> Option<Self::Item> {
| - let's call the lifetime of this reference `'1`
...
156 | (self.func)(self.parser)
| ^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
I mean, I think I don't even understand the essence of the error. Clearly, &mut self
cannot outlive 'a
because it is itself an object that contains references that have a lifetime of 'a
, so how can &mut self
possibly outlive 'a
?
But ok, taking the error at face value, I guess I need to make sure that &mut self
is also 'a
. But I can't explicitly name it, because that's not specified thus by the Iterator
trait.
How can I make my code compile?
And I guess a secondary question, I've been peppering the code with explicit references to try to fix the issue. What's the minimum amount of explicit references I can get away with?
The problem stems from &'a mut Parser<'a>
. This kind of pattern is always wrong. This can work with shared references (&'a Parser<'a>
) because shared reference lifetimes are variant; exclusive reference lifetimes are not.
Just remove the reference lifetime from the &mut Parser
types where possible; introduce a new lifetime parameter where one is required.
struct Parser<'a> {
string: &'a str,
}
impl<'a> Parser<'a> {
// ...
fn at_end(&self) -> bool {
// returns whether we're at the end of the string
false
}
fn expect(&mut self, _s: &str) -> &mut Self {
// expects the next part to be `s` and advances the string
// ...
self
}
fn parse_iter<'b, 'c, T: 'a, F: FnMut(&mut Self) -> Option<T>>(
&'c mut self,
separator: &'b str,
f: F,
) -> ParseIter<'a, 'b, 'c, T, F> {
ParseIter {
parser: self,
func: f,
sep: separator,
skip: false,
}
}
}
struct ParseIter<'a, 'b, 'c, T, F>
where
T: 'a,
F: FnMut(&mut Parser<'a>) -> Option<T>
{
parser: &'c mut Parser<'a>,
func: F,
sep: &'b str,
skip: bool,
}
impl<'a, 'b, 'c, T, F> Iterator for ParseIter<'a, 'b, 'c, T, F>
where
T: 'a,
F: FnMut(&mut Parser<'a>) -> Option<T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.parser.at_end() {
return None;
}
if std::mem::replace(&mut self.skip, true) {
self.parser.expect(self.sep);
}
(self.func)(self.parser) // error here!
}
}
(Playground))