How to move cloned Arc into Rust Warp callback?

113 Views Asked by At

I'm trying to code a simple Warp server.

type Cache = Arc<RwLock<HashMap<String, String>>>;
pub struct App {
    cache: Cache,
    // other fields
}

impl App {
    pub async fn new() -> App { /* App init */ }

    pub async fn run(self) {
      let filter = warp::path!("cache" / String)
         .and(warp::any().map(move || self.cache.clone()))
         .and_then(handler);
    
      tokio::join!(
            foo(self.cache),
            warp::serve(filter).run(self.server_socket)
      )
      unreachable!()
    }
}

async fn handler(id: String, cache: Cache) -> Result<impl warp::Reply, warp::Rejection> {
  /* does stuff */ 
}
async fn foo(cache: Cache) { /* does stuff */ }

When I try to compile it I get this error

error[E0382]: use of moved value: `self.cache`
foo(self.cache),
    ^^^^^^^^^^ value used here after move

Ok, no problem. Trying cloning self.cache and then moving it to the filter.

pub async fn run(self) {
  let cache = self.cache.clone(); // cloning cache
  let filter = 
    warp::path!("cache" / String)
      .and(warp::any().map(move || cache))  // trying to move cache
      .and_then(handler);
    
  tokio::join!(
     foo(self.cache),
     warp::serve(filter).run(self.server_socket)
  )
  unreachable!()
}

And now I get this error

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`

.and(warp::any().map(move || cache))
                 --- ^^^^^^^------
                 |   |       |
                 |   |       closure is `FnOnce` because it moves the variable `cache` out of its environment
                 |   this closure implements `FnOnce`, not `Fn`
                 |   the requirement to implement `Fn` derives from here
                 required by a bound introduced by this call

I didn't find any ways to move cloned cache into filter. The only way I got this to work was by cloning cache two times.

pub async fn run(self) {
  let cache = self.cache.clone(); // first cloning
  let filter = 
    warp::path!("cache" / String)
      .and(warp::any().map(move || cache.clone()))  // second cloning
      .and_then(handler);

  tokio::join!(
     foo(self.cache),
     warp::serve(filter).run(self.server_socket)
  )
  unreachable!()
}

I clone cache two times but the first clone is used just to perform the second cloning so I think this is wrong way to solve this problem. How to avoid double cloning?

0

There are 0 best solutions below