[Solved] How to convert String to &'a String


#1

I am trying to write a simple http server, and I got a problem when parse http request header to a HashMap.
Here is my code:

use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};

#[derive(Debug)]
pub struct Req<'a> {
    method: String,
    version: String,
    path: String,
    header: HashMap<&'a str, &'a str>,
    raw: &'a String
}

impl<'a> Req<'a> {
    pub fn new(mut stream: TcpStream) -> Option<Req<'a>> {
        let raw = Req::get_raw_request(stream);
        let mut header = Req {
            method: String::new(),
            version: String::new(),
            path: String::new(),
            header: HashMap::new(),
            raw: &raw
        };
        header.parse_header(&raw);
        Some(header)
    }

    fn parse_header(&mut self, req_header: &'a String) {
        for l in req_header.lines() {
            let index = match l.find(": ") {
                Some(i) => i,
                None => continue
            };
            self.header.entry(&l[..index]).or_insert(&l[index + 2..]);
        }
    }

    fn get_raw_request(mut stream: TcpStream) -> String {
        //parse tcp stream to string
        String::from("GET /path HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: keep-alive\r\nAccept: */*\r\nmore header.....")
    }
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:3010").unwrap();
    for stream in listener.incoming() {
        let _ = Req::new(stream.unwrap());
    }
}

Error information:

error[E0597]: `raw` does not live long enough
  --> test.rs:21:30
   |
21 |         header.parse_header(&raw);
   |                              ^^^ does not live long enough
22 |         Some(header)
23 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 12:1...
  --> test.rs:12:1
   |
12 | / impl<'a> Req<'a> {
13 | |     pub fn new(mut stream: TcpStream) -> Option<Req<'a>> {
14 | |         let raw = Req::get_raw_request(stream);
15 | |         let mut header = Req {
...  |
38 | |     }
39 | | }
   | |_^

error: aborting due to previous error

I think if I can return a &'a String from get_raw_request the problem can be solved, but I don’t how.


#2

As written, the raw String is a temporary that dies after new returns - you cannot return a reference to it. Is there a reason you don’t want to have raw be an owned String rather than a reference? Do you have cases where you can return a reference? If so, you can use Cow<‘a, str> as the type for the raw field, which allows you to sometimes return a borrowed string and sometimes an owned one.

If you explain why raw is defined as &String then can give you a more concrete answer.

P.S. you almost never want &String - instead use &str


#4

Thanks for your reply :grinning:. I think the parameter req_header of function parse_header need a lifetime annotation, so I defined raw as &String. Now, I realized it’s a wrong way to return &String from function. Is there a way to get the function parse_header works?


#5

The most straightforward approach is to change the header HashMap to store owned String values rather than slices. This means you’ll need to clone() the key and value that you parse out of the raw String. The way your Req struct is defined now, with the lifetime parameter and references, is it expects to be given references to values that outlive the Req itself. But that’s not the case in the code.

Another alternative, made possible with Rust 1.21, is to change Req to:

#[derive(Debug)]
pub struct Req {
    method: String,
    version: String,
    path: String,
    header: HashMap<Rc<str>, Rc<str>>,
    raw: Rc<str> // or Rc<String>
}

Then you can keep raw inside a Rc<str> (or Rc<String>) and parse the key/value pairs out of that into their own Rc<str> and store them in the HashMap as refcounted slices.


#6

May I mention the other “Simple Server” that’s currently making the rounds? Simple Server. Currently pushed by Steve Klabnik and Ashley Dubs, it can use extra eyes, and it is in such an early stage that contributing there is probably as much a learning experience as starting from scratch (but with the benefit of two very experienced mentors :slight_smile: )


#7

Thank you! It works. :grin:


#8

As you can see, my skills are very basic.:sweat_smile: I have downloaded the code and I will read the code to learn how to write code.


#9

Oh, sorry! I didn’t mean it as a critique of your own work! I think it is great that you are trying something to learn from it!
I just wanted to make you aware of this other project, because sometimes learning together is more fun than learning alone, and sometimes learning alone is more practical than together.