How to do application for a mutable reference of FnOnce

Hi I am new to Rust, I like its elegance but I really could't figure out this. Any help would be appreciated.
Here's the problem: I have a mutable reference of a FnOnce fun, and I want to apply it. But I will get the error:

error[E0507]: cannot move out of `*fun` which is behind a mutable reference
   --> src\future.rs:176:13
    |
176 |             fun(s).poll()
    |             ^^^ move occurs because `*fun` has type `Fun`, which does not implement the `Copy` trait

According to the error, I guess it's because application of FnOnce would take its ownership.
Maybe take_mut would be helpful, but it requires to get a value back later; However, after the application, i.e. doing fun(s), fun moved and I could provide a valid value later.
Here's part of the code, I have added the comment at the most important line

pub enum AndThen<Fut1, Fut2, Fun> {
  First(Fut1, Fun),
  Second(Fut2),
  Done,
}

impl<Fut1, Fut2, Fun> Future for AndThen<Fut1, Fut2, Fun>
where
  Fut1: Future,
  Fut2: Future,
  Fun: FnOnce(Fut1::Item) -> Fut2 + Send, //restriction for Fun
{
  type Item = Fut2::Item;

  fn poll(&mut self) -> Poll<Self::Item> {
    match self {
      AndThen::First(fut, fun) => { // fun has type Fun, and is a FnOnce
        match fut.poll() {
          Poll::NotReady => Poll::NotReady,
          Poll::Ready(s) => {
            fun(s).poll() //need to do application here
          }
        }
      },
      AndThen::Second(fut2) => fut2.poll(),
      _ => Poll::NotReady
    }
  }
}

Try for using take_mut as a fix (but it fails since f moved then I can't provide with a valid return value for take_mut::take)

let mut res = Poll::NotReady;
take_mut::take(fun, |f| {
              res = f(s).poll();
              f
            });
res

Is there a fix other than adding Copy restrictions for Fun?
Thanks!

An FnOnce can only be called once, and calling it will consume it, but you don't have ownership, so it's not possible to consume it. Use an FnMut instead.

Alternatively you can store it in an Option and use Option::take.

3 Likes

Adding to what @alice said, you can do that with the unsafe std::ptr::read() (but don't!)

Also, take_mut is intended for cases where the new value is generated from the old value; if you just want to put another value inside, use std's std::mem::replace() or std::mem::take(). In fact, Option::take() uses std::mem::take():

    pub fn take(&mut self) -> Option<T> {
        mem::take(self)
    }

option.rs - source (rust-lang.org)

1 Like