Theory vs. practice lifetime and borrow

Hi,

Please excuse if similar questions are already answered multiple times but I'm not able to
apply those tips and answers to my current problems.

The code

extern crate xml;

use std::fs::File;
use self::xml::reader::EventReader;
use self::xml::reader::events::XmlEvent;

pub enum Node {
    XmlStartElement{name: String},
    XmlEndElement{name: String},
}

pub trait Parser {
    fn next(&self) -> Result<Node, String>;
}

pub struct Deserializer {
    parser: Box<Parser>,
}

impl Deserializer {
    pub fn new(parser: Box<Parser>) -> Result<Deserializer, String> {
        let mut deserializer = Deserializer {
            parser: parser,
        };

        Ok(deserializer)
    }
}

#[test]
fn test() {
    struct P<'a> {
        eventReader: &'a mut EventReader<File>,
    };
    
    impl<'a> Parser for P<'a> {
        fn next(&self) -> Result<Node, String> {
            let xmlEvent = self.eventReader.next();
            match xmlEvent {
                XmlEvent::StartElement { name, .. } => {
                    Ok(Node::XmlStartElement{name : name.local_name})
                }
                XmlEvent::EndElement { name } => {
                    Ok(Node::XmlEndElement{name : name.local_name})
                }
                XmlEvent::Error(e) => {
                    Err("error".to_string())
                }
                _ => {
                    Err("error".to_string())
                }
            }
        }
    };
    
    let file = File::open("some.xml").unwrap();    
    let eventReader = EventReader::new(file);        
    let p = P{eventReader: &mut eventReader};
    let de = Deserializer::new(Box::new(p));
}

the compiler complaints:

src/main.rs:58:30: 58:41 error: `eventReader` does not live long enough
src/main.rs:58  let p = P{eventReader: &mut eventReader};
                                            ^~~~~~~~~~~
note: reference must be valid for the static lifetime...
src/main.rs:57:43: 60:2 note: ...but borrowed value is only valid for the block suffix following statement 3 at 57:42
src/main.rs:57  let eventReader = EventReader::new(file);
src/main.rs:58  let p = P{eventReader: &mut eventReader};
src/main.rs:59     let de = Deserializer::new(Box::new(p));
src/main.rs:60 }
src/main.rs:38:19: 38:35 error: cannot borrow data mutably in a `&` reference
src/main.rs:38                  let xmlEvent = self.eventReader.next();
                                               ^~~~~~~~~~~~~~~~

In addition to that I have the feeling that `Box::new(p) is wrong too...

What I'm trying to do here is to be independent of a concrete parser implementation via the trait Parser.

It would be nice if someone could explain me how it is done properly and what may be a good approach to solve such issues. What are your thinkings when you write rust code?

Many thanks for your input,

Marc

What I believe is happening here is that because you are storing a Box<Parser>, rust is assuming you want a Box<Parser + 'static>. The 'static there is inferred because you don't specify any other lifetime.

But, when you create a parser with let p = P{eventReader: &mut eventReader};, p only lives as long as the &mut eventReader reference - it doesn't live for 'static.

I would suggest replacing

pub struct Deserializer {
    parser: Box<Parser>,
}

with

pub struct Deserializer<'a> {
    parser: Box<Parser + 'a>,
}

That will allow any Parser which is boxed for Deserializer to live for a lifetime other than 'static - meaning it will be allowed to store borrowed values like &mut eventReader inside of it.


For the second error, what you're trying to do is use a method which requires mutability (mainly self.eventReader.next()) inside a method which takes an immutable version of self (with &self). eventReader.next() requires mutability mainly because it is changing the state of the event reader - the next time you call next() it will return something different.

For this problem, I would suggest allowing Parser::next() to take &mut self rather than &self, so your implementation can change the inner state of a Parser within the next() method.

pub trait Parser {
    fn next(&self) -> Result<Node, String>;
}

becomes

pub trait Parser {
    fn next(&mut self) -> Result<Node, String>;
}
1 Like

Hi daboross,

Thank you for your valuable help. You were right with both suggestions.
But unfortunatly I got more issues with lifetimes (one lifetime issue solved, ten new appeared ;-)) while
I was trying to go a step further. More or less by accident I found that the following approach seems
to be the way to go:

extern crate xml;

use std::fs::File;
use self::xml::reader::EventReader;
use self::xml::reader::events::XmlEvent;

pub enum Node {
    XmlStartElement{name: String},
    XmlEndElement{name: String},
}

pub trait Parser {
    fn next(&mut self) -> Result<Node, String>;
}

pub struct Deserializer<P: Parser> {
    parser: P,
}

impl<P> Deserializer<P> where P: Parser {
    pub fn new(parser: P) -> Result<Deserializer<P>, String> {
        let mut deserializer = Deserializer {
            parser: parser,
        };

        Ok(deserializer)
    }
}

#[test]
fn test() {
    struct P<'a> {
        eventReader: &'a mut EventReader<File>,
    };
    
    impl<'a> Parser for P<'a> {
        fn next(&mut self) -> Result<Node, String> {
            let xmlEvent = self.eventReader.next();
            match xmlEvent {
                XmlEvent::StartElement { name, .. } => {
                    Ok(Node::XmlStartElement{name : name.local_name})
                }
                XmlEvent::EndElement { name } => {
                    Ok(Node::XmlEndElement{name : name.local_name})
                }
                XmlEvent::Error(e) => {
                    Err("error".to_string())
                }
                _ => {
                    Err("error".to_string())
                }
            }
        }
    };
    
    let file = File::open("some.xml").unwrap();    
    let mut eventReader = EventReader::new(file);        
    let p = P{eventReader: &mut eventReader};
    let de = Deserializer::new(p);
}

Iinstead of Boxing and specifying a lifetime for the Parser I had to declare the generic P on the Deserializer struct. This solved all my issues I had so far with lifetimes.

Thanks,

Marc

1 Like