How to pass variables to hyper service handler?

Hi, I am new to Rust, currently playing around with Hyper, try to build a server which can count the number of incoming requests.

// Build a hyper server, which serves our custom echo service.
let request_counter = Arc::new(AtomicUsize::new(0));
let fut =
    Server::builder(tls).serve(move || service_fn(|req| echo(req, request_counter.clone())));

// Run the future, keep going until an error occurs.
println!("Starting to serve on https://{}.", addr);
let mut rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(fut).map_err(|e| error(format!("{}", e)))?;
Ok(())

And now stuck in the static time issue. After several days of searching, still got nothing that is straight forward, even though I feel like somehow it could be done in an easy way.
I got the following errors

   Compiling tls_server v0.1.0 (file:///home/lc/playground/tls_server)
error[E0597]: `*request_counter` does not live long enough
  --> tls_server/src/main.rs:79:71
   |
79 |         Server::builder(tls).serve(move || service_fn(|req| echo(req, request_counter.clone())));
   |                                                       -----           ^^^^^^^^^^^^^^^ borrowed value does not live long enough
   |                                                       |
   |                                                       capture occurs here
   |
   = note: borrowed value must be valid for the static lifetime...
note: ...but borrowed value is only valid for the lifetime  as defined on the body at 79:36
  --> tls_server/src/main.rs:79:36
   |
79 |         Server::builder(tls).serve(move || service_fn(|req| echo(req, request_counter.clone())));
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
error: Could not compile `tls_server`.

To learn more, run the command again with --verbose.

and full code is the following:
it was adopted from the server example of hyper-rustls.
https://github.com/cong-lian/playground/blob/master/tls_server/src/main.rs

any hint? I think my case is fairly simple but I just can’t make it work. Can someone explain how in general could I pass mutable variables to Hyper service handler?

There’s no need to clone, remove the call .clone().

You’re close:

let fut =
    Server::builder(tls)
     .serve(move || {
          let inner = Arc::clone(&request_counter);
          service_fn(move |req| echo(req, Arc::clone(&inner)))
      }
);
1 Like

Thank you so much! Provided solution is straightforward enough and most importantly It works!
But could you please also give some explanation, just for better understanding.
Why should we need to clone twice? because it smells like redundant overhead.

The clone inside the service_fn call is because echo() takes an Arc<AtomicUsize> by value, and thus consumes that argument on each call. The closure given to service_fn must be an Fn type, which means it must be callable multiple times. In order to keep it callable multiple times, we need to ensure that inner isn’t consumed across the echo() calls - that’s what the second clone achieves.

And in fact, your intuition about seemingly needless clones is right. echo() can take a &AtomicUsize parameter, rather than an Arc<AtomicUsize>. In that case, you can avoid the second clone:

let fut =
    Server::builder(tls)
     .serve(move || {
          let inner = Arc::clone(&request_counter);
          service_fn(move |req| echo(req, &inner))
      }
);
1 Like

Perfect! Everything is clear now.
Thank you!

1 Like