简体   繁体   中英

Lifetime of a parser struct in Rust

I'm trying to rewrite my parser to allow for strings to be passed into the parse method, instead of being bound to the struct.

Previously, my code looked like this:

use std::collections::HashMap;
use std::str;

#[derive(Debug)]
pub enum ParserError {
    Generic
}

pub struct Resource(
    pub HashMap<String, String>
);

pub struct Parser<'a> {
    source: str::Chars<'a>
}

impl<'a> Parser<'a> {
    pub fn new(source: &str) -> Parser {
        Parser { source: source.chars() }
    }
    pub fn parse(&mut self) -> Result<Resource, ParserError> {
        let entries = HashMap::new();
        Ok(Resource(entries))
    }
}

fn main() {
    let parser = Parser::new("key1 = Value 1");
    let res = parser.parse();
}

and in my new code I'm trying something like this:

use std::collections::HashMap;
use std::str;

#[derive(Debug)]
pub enum ParserError {
    Generic
}

pub struct Resource(
    pub HashMap<String, String>
);

pub struct Parser<'a> {
    source: Option<str::Chars<'a>>
}

impl<'a> Parser<'a> {
    pub fn new() -> Parser<'a> {
        Parser { source: None }
    }
    pub fn parse(&mut self, source: &str) -> Result<Resource, ParserError> {
        self.source = Some(source.chars());

        let entries = HashMap::new();
        Ok(Resource(entries))
    }
}

fn main() {
    let parser = Parser::new();
    parser.parse("key1 = Value 1");
    parser.parse("key2 = Value 2");
}

but it seems like I'm messing with lifetimes in a way that I'm not fully comfortable with. The error I get is:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> test.rs:22:35
   |
22 |         self.source = Some(source.chars());
   |    

What's the canonical way of handling this? How can I take a String and clone it into the lifetime of the Parser struct?

The full error message is:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:22:35
   |
22 |         self.source = Some(source.chars());
   |                                   ^^^^^
   |
help: consider using an explicit lifetime parameter as shown: fn parse(&mut self, source: &'a str) -> Result<Resource, ParserError>
  --> src/main.rs:21:5
   |
21 |     pub fn parse(&mut self, source: &str) -> Result<Resource, ParserError> {
   |     ^

Doing as it suggests:

pub fn parse(&mut self, source: &'a str) -> Result<Resource, ParserError>

Allows the code to compile and run (after fixing the unrelated mismatched mutability in main ).


To understand the difference, you must first understand lifetime elision .

Your original code was:

fn new(source: &str) -> Parser // with elision
fn new<'b>(source: &'b str) -> Parser<'b> // without elision

In words, the generic lifetime parameter 'a of the struct was tied to the lifetime of the incoming string.

Your new code was more complicated:

fn new() -> Parser<'b>

// with elision
fn parse(&mut self, source: &str) -> Result<Resource, ParserError>
// without elision
fn parse<'c, 'd>(&'c mut self, source: &'d str) -> Result<Resource, ParserError>

In words, the generic lifetime parameter 'a of the struct is still defined by the caller of new , but now it's not tied to anything from the constructor. When calling parse , you were attempting to pass in a string of an unrelated lifetime and store a reference to it (through the Chars iterator). Since the two lifetimes were unrelated, you cannot be sure it will last long enough.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM