Theory vs. practice lifetime and borrow


#1

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§ 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


#2

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>;
}

#3

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