What should i do to fix the error?

I want to use the content-length in header to get the body message. but it can not pass the compiling.The compiler show me the message :slight_smile:

error[E0502]: cannot borrow `content_length_header` as immutable because it is also borrowed as mutable
  --> src/server/server.rs:60:29
   |
51 |                 .map(|line| if line.clone().contains("Content-Length") {
   |                      ------ mutable borrow occurs here
...
54 |                     content_length_header = header_item.unwrap().1.trim().parse::<usize>().unwrap();
   |                     --------------------- first borrow occurs due to use of `content_length_header` in closure
...
60 |                 .take_while(|line| if line.clone().len()>0 {
   |                  ---------- ^^^^^^ immutable borrow occurs here
   |                  |
   |                  mutable borrow later used by call
...
63 |                     if content_length >= content_length_header && is_body {
   |                                          --------------------- second borrow occurs due to use of `content_length_header` in closure

error[E0502]: cannot borrow `is_body` as immutable because it is also borrowed as mutable
  --> src/server/server.rs:60:29
   |
51 |                 .map(|line| if line.clone().contains("Content-Length") {
   |                      ------ mutable borrow occurs here
...
55 |                     is_body = true;
   |                     ------- first borrow occurs due to use of `is_body` in closure
...
60 |                 .take_while(|line| if line.clone().len()>0 {
   |                  ---------- ^^^^^^ immutable borrow occurs here
   |                  |
   |                  mutable borrow later used by call
...
63 |                     if content_length >= content_length_header && is_body {
   |                                                                   ------- second borrow occurs due to use of `is_body` in closure


i don't know how to fix it.

fn handle_connection(&self, mut stream:TcpStream) {
        thread::spawn(move ||  {
            println!("handle connection");
            let mut buf_reader = BufReader::new(&mut stream);
            let mut is_body=false;
            let mut content_length = 0;
            let mut content_length_header = 0;
            let req_lines:Vec<String> = buf_reader.by_ref().lines()
                .map(|line_wrap| line_wrap.unwrap_or_else(|_| return "".to_string()))
                .map(|line| if line.clone().contains("Content-Length") {
                    let temp = line.clone();
                    let header_item = temp.split_once(":");
                    content_length_header = header_item.unwrap().1.trim().parse::<usize>().unwrap();
                    is_body = true;
                    temp
                } else {
                    line
                })
                .take_while(|line| if line.clone().len()>0 {
                    let temp = line.clone();
                    content_length += temp.len();
                    if content_length >= content_length_header && is_body {
                        true
                    } else {
                        false
                    }
                }else{
                    false
                })
                .collect();
...

How about using map_while instead of map and take_while to combine both closures?

            .map_while(|line| {
                if line.contains("Content-Length") {
                    let temp = line.clone();
                    let header_item = temp.split_once(":");
                    content_length_header = header_item.unwrap().1.trim().parse::<usize>().unwrap();
                    is_body = true;
                    return Some(temp);
                } 
                
                if line.len() > 0 {
                    let temp = line.clone();
                    content_length += temp.len();
                    if content_length >= content_length_header && is_body {
                        return Some(line);
                    }
                }
                
                None
            })

Playground—make sure I didn't mess up your logic!

1 Like

You're trying to share mutable variable between two closures. Rust doesn't allow that. Mutable means exclusive in Rust, and there's always one and only one place that can access a mut thing. The borrow checker just doesn't understand how iterator works, and is worried that one closure could be writing to content_length_header while the other is reading from it at the same time, and that would be a race condition.

You could work around it by using Cell<u64> for the length, which gives you "interior mutability" to tell the type system that this is single-threaded code (Cell implies that) and you allow it to mutate from multiple places. Then you'd be able to change the shared variable through the cell. But having an external shared state in iterators is a bit inelegant and clunky, and unnecessary.

You can pass the length between steps as iterator items. Remove content_length_header from outer scope. Define it inside the map closure, and return (line, content_length_header) from map, and take (line, content_length_header) in take_while.

1 Like

I make a little modification as below, it can get the header info, it will block to get the post body message because it wait the stream from client. I want to get the post body too. how to do?

 let req_lines:Vec<String> = buf_reader.by_ref().lines()
                .map(|line_wrap| line_wrap.unwrap_or_else(|_| return "".to_string()))
                .map_while(|line| {
                    if line.contains("Content-Length") {

                        let temp = line.clone();
                        let header_item = temp.split_once(":");
                        content_length_header = header_item.unwrap().1.trim().parse::<usize>().unwrap();
                        println!("content_length_header {}", content_length_header);
                        Some(temp)
                    } else if line.is_empty(){
                        is_body = true;
                        Some(line)
                    }else  {
                        let temp = line.clone();
                        if is_body {
                            content_length += temp.len();
                        }
                        if content_length >= content_length_header && is_body {
                            println!(">>>>end");
                            None
                        }else{
                            println!("----read data length {}, is_body {}, line {}", content_length, is_body, temp);
                            Some(line)
                        }
                    }
                }).collect();

What exactly do you mean by post body?

I post data like this:
curl -X POST -i 'http://127.0.0.1:8080' --data wwwww

i want to get "wwwww" from the request.

Unless you're writing this for educational purposes, I suggest using hyper or at least httparse.

3 Likes

Yes, i am learning the Rust, and i have finished reading the cook book and want to write a simple HTTP server to practice.

Maybe I can study the httparse source code to get the answer. Thank you.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.