Hey guys, I'm really new to Rust and have banged my head for a few hours at this problem.
I'm using Hyper and Tokio, and am trying to write a simple router, but I cannot seem to wrap my head around the ownership going on. I couldn't find any relevant help pages, but that is partially because I don't even know what to search.
I've written a new stripped down version of what I'm trying to do (38 lines):
Playpen: Rust Playground
Code:
use hyper::server::conn::Http;
use hyper::service::service_fn;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio::stream::StreamExt;
use hyper::{Body, Response, StatusCode};
/** A webserver that responds with 200 if its path contains a string. */
#[tokio::main]
async fn main() {
let strings_to_match = Arc::new(vec!["test", "hello", "yes"]);
let http = Arc::new(Http::new());
let mut listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();
while let Some(stream) = listener.incoming().next().await {
let conn = http.serve_connection(
stream.unwrap(),
service_fn(|req| async move {
for string in *strings_to_match {
if req.uri().path().to_string().contains(string) {
let response: Result<Response<Body>, String> = Ok(
Response::builder()
.status(StatusCode::OK)
.body(Body::from("Yes".to_string()))
.unwrap());
return response;
}
}
return Ok(
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("Not found"))
.unwrap());
})
);
conn.await;
}
}
Currently, I'm getting the error:
cannot move out of `strings_to_match`, a captured variable in an `FnMut` closure
move out of `strings_to_match` occurs hererustc(E0507)
main.rs(11, 9): captured outer variable
main.rs(18, 41): move out of `strings_to_match` occurs here
main.rs(19, 32): move occurs because `strings_to_match` has type `std::sync::Arc<std::vec::Vec<&str>>`, which does not implement the `Copy` trait
main.rs(19, 32): move occurs due to use in generator
I understand the error in practice: I have one strings_to_match, so I can't use it in the loop, because the previous iteration will take ownership. However, abstractly, this doesn't make sense to me. strings_to_match is an Arc, which impls Clone, so there should be an easy way to create a clone for every iteration right? I understand there is a difference between Copy and Clone in practice. Is my understanding correct? Is there an easy thing that I'm missing?
I've also tried:
- Removing the "move" doesn't help, as then it complains that it returns a value referencing req. This makes sense, since the async will return a Future, which references req. In that case, it makes sense that "move" is required to get ownership of req.
- I tried adding "let strings_to_match = strings_to_match.clone()" in both the while loop and inside the async closure. Both did not work, as to my understanding I'm still using the strings_to_match reference when calling clone.
- Following this, I tried calling Arc::clone on strings_to_match. This also didn't work for the same reason as above.
Are my understandings of the above correct?
For more context on what I'm trying to do, I'm trying to implement a TLS server with client cert verification that does extra handling of the client cert. I couldn't find any well maintained crates, so I rolled my own.