Completely stuck at a project, really need help

Hi All,

I recently started to learn rust by creating this project. Although I did hit a few bumps it generally went ok and I was starting to feel like I'm getting the hang of it, however, in the last 3 days I hit a wall trying to add SSL support and no matter what road I take to work around the problem I can't go through. The entire project in it's current failed state is here.

The first problem is around this code (indentation removed):

   let listener = listener.to_listener().unwrap();
   let http = Http::new();
   let state = Arc::clone(&state);
   let acceptor = tls_acceptor.clone();
   let done = listener.incoming()
       .map_err(|e| error!("{:?}", e))
       .for_each(move |stream| {
           let addr = stream.peer_addr().unwrap();
           let done = acceptor.accept(stream)
               .map_err(|e| error!("{:?}", e))
               .and_then(|stream| {
                   let service = MainService {
                       state: Arc::clone(&state),
                       remote_addr: addr,
                   };
                   let done = http.serve_connection(stream, service)
                   .map_err(|e| error!("{:?}", e));
                   tokio::spawn(done)
               });
           tokio::spawn(done)
       });
       Box::new(done)

When compiling it as it is I get the following error:

error: lifetime may not live long enough
   --> src/web/mod.rs:138:36
    |
136 |                       .for_each(move |stream| {
    |                                 ------------- lifetime `'1` represents this closure's body
137 |                           let addr = stream.peer_addr().unwrap();
138 |                           let done = acceptor.accept(stream)
    |  ____________________________________^
139 | |                             .map_err(|e| error!("{:?}", e))
140 | |                             .and_then(|stream| {
141 | |                                 let service = MainService {
...   |
147 | |                                 tokio::spawn(done)
148 | |                             });
    | |______________________________^ argument requires that `'1` must outlive `'static`
    |
    = note: closure implements `FnMut`, so references to captured variables can't escape the closure

error[E0373]: closure may outlive the current function, but it borrows `addr`, which is owned by the current function
   --> src/web/mod.rs:140:39
    |
140 |                             .and_then(|stream| {
    |                                       ^^^^^^^^ may outlive borrowed value `addr`
...
143 |                                     remote_addr: addr,
    |                                                  ---- `addr` is borrowed here
    |

Looking around for solution I tried to add move to line 140:

.and_then(move |stream| {

... but now I get different error:

error[E0507]: cannot move out of captured variable in an `FnMut` closure
   --> src/web/mod.rs:140:39
    |
132 |                   let state = Arc::clone(&state);
    |                       ----- captured outer variable
...
140 |                               .and_then(move |stream| {
    |  _______________________________________^
141 | |                                 let service = MainService {
142 | |                                     state: Arc::clone(&state),
143 | |                                     remote_addr: addr,
...   |
147 | |                                 tokio::spawn(done)
148 | |                             });
    | |_____________________________^ cannot move out of captured variable in an `FnMut` closure

error[E0507]: cannot move out of captured variable in an `FnMut` closure
   --> src/web/mod.rs:140:39
    |
131 |                   let http = Http::new();
    |                       ---- captured outer variable
...
140 |                               .and_then(move |stream| {
    |  _______________________________________^
141 | |                                 let service = MainService {
142 | |                                     state: Arc::clone(&state),
143 | |                                     remote_addr: addr,
...   |
147 | |                                 tokio::spawn(done)
148 | |                             });
    | |_____________________________^ cannot move out of captured variable in an `FnMut` closure

:slightly_frowning_face:,

While this code contains other directions I took trying to work around this (like the run2 function here - which upon switching the commented Server:Http match branch I get an error about Streams not implemented for TCPListener...), however I want to first focus on the first errors.

Is there someone who care's to put in the time and help me solve this?

Thanks a lot, really :slightly_smiling_face:,

Try cloning the state Arc between the let addr and let done lines instead of doing it when creating the MainService struct.

Hi Again, this is the other side of the problem you helped me yesterday (the run2 function) :smile:.

Sadly, this does not change anything (tried it both with or without the move).

Hmm, really? You tried it like this?

let listener = listener.to_listener().unwrap();
let http = Http::new();
let state = Arc::clone(&state);
let acceptor = tls_acceptor.clone();
let done = listener.incoming()
    .map_err(|e| error!("{:?}", e))
    .for_each(move |stream| {
        let addr = stream.peer_addr().unwrap();
        let state_clone = Arc::clone(&state);
        let done = acceptor.accept(stream)
            .map_err(|e| error!("{:?}", e))
            .and_then(move |stream| {
                let service = MainService {
                    state: state_clone,
                    remote_addr: addr,
                };
                let done = http.serve_connection(stream, service)
                    .map_err(|e| error!("{:?}", e));
                tokio::spawn(done)
            });
        tokio::spawn(done)
    });
    Box::new(done)

What error does it fail with?

1 Like

Ok, while it gave the same error it actually indicated different variable (http). when I moved the creation of http too inside the for_each (not sure what's the implications in terms of load/memory) and did solve it :sunny: :smile:.

Also teaches me something about trusting the error display in vscode' it's much more clear when running cargo check in terminal.

Really, thanks a lot. you saved my day!!!

No problem! I don't know if Http is expensive or cheap, but you could put it in an Arc like state if it turns out to be expensive.

Good idea, thanks :slight_smile:

The reason you had to move the creation of the http and state variables into the for_each is that since the and_then closure outlives the for_each closure, it must own the things it uses. Since for_each can be called several times, the and_then closure can't just take ownership of something stored in the for_each closure without cloning it or otherwise creating a new one in each for_each run.

Aha, and Arc would allow me to just clone the reference, not the object itself.

Great, Thanks.

Actually maybe not, as it will still outlive the current function ..

When using an Arc, it's fine if it outlives the function. It counts the number of clones, and as long as one exists anywhere, the data is kept alive. You're not cloning a reference, but an Arc. An Arc is sorta like a reference (we call it a smart pointer), but it's distinct from an &_.

Cool, thanks.

It could also be done using state.clone() IIRC

        let done = acceptor.accept(stream)
            .map_err(|e| error!("{:?}", e))
            .and_then(move |stream| {
                let service = MainService {
                    state: state.clone(),
                    remote_addr: addr,
                };
                let done = http.serve_connection(stream, service)
                    .map_err(|e| error!("{:?}", e));
                tokio::spawn(done)
            });

If it is not a separate thread (Sync), better to use Rc instead instead of Arc.