2

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?

KindFrog
  • 358
  • 4
  • 17
  • 1
    Cloning an `Arc` is cheap, that's the whole point, why do you want to avoid the clone? I don't think you can in this case anyways. – cafce25 Dec 15 '22 at 16:20
  • @cafce25 I've read that it's bad practice to overuse `clone()` to satisfy the borrow checker, and I thought that was the case here. So I'm interested if there is a way to move `Arc` instead of cloning. – KindFrog Dec 15 '22 at 16:29
  • 1
    It's also bad to blindly follow advice from the internet without understanding it. It's 'bad' cause you copy the data, you're not copying the data here, just the Atomic Referenc Counted object. – cafce25 Dec 15 '22 at 16:32
  • @cafce25 In any case, it's better to move `Arc` than to copy if possible. – KindFrog Dec 15 '22 at 16:52
  • 1
    It's not possible here though, you need one clone for the closure and one for each invocation. – cafce25 Dec 15 '22 at 16:53

0 Answers0