Search code examples
rustclosuresownership

How to force borrow a variable for closure? Encoding an f32 in bendy


While implementing an encoding trait for bendy I tried the following:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Copy, Default, Clone)]
pub struct MyFloot {
    pub density: f32,
}

use bendy::encoding::{Error, ToBencode};

impl ToBencode for MyFloot {
    const MAX_DEPTH: usize = 1;

    fn encode(&self, encoder: bendy::encoding::SingleItemEncoder) -> Result<(), Error> {
        self.density
            .to_be_bytes()
            .iter()
            .map(|b| encoder.emit_int(*b));
        Ok(())
    }
}

For which the following error is produced:

error[E0507]: cannot move out of `encoder`, a captured variable in an `FnMut` closure
   --> src\main.rs:17:22
    |
13  |     fn encode(&self, encoder: bendy::encoding::SingleItemEncoder) -> Result<(), Error> {
    |                      ------- captured outer variable
...
17  |             .map(|b| encoder.emit_int(*b));
    |                  --- ^^^^^^^ ------------ `encoder` moved due to this method call
    |                  |   |
    |                  |   move occurs because `encoder` has type `SingleItemEncoder<'_>`, which does not implement the `Copy` trait
    |                  captured by this `FnMut` closure
    |
note: this function takes ownership of the receiver `self`, which moves `encoder`
   --> .cargo\registry\src\github.com-1ecc6299db9ec823\bendy-0.3.3\src\encoding\encoder.rs:257:42
    |
257 |     pub fn emit_int<T: PrintableInteger>(self, value: T) -> Result<(), Error> {
    |                                          ^^^^

But I cannot wrap my head around the move semantics with this..

  • Why would the closure take ownership of encoder here, despite the ommited move ? ( I assume it does )
  • Why does it need to move it out of context?
  • What does that even mean?

Solution

  • I got it!

    Firstly, it was not the closure that consumed encoder, it was the emit* call. The first loop iteration would have consumed it, so that's why the compiler was throwing an error.

    Secondly, encoder in bendy has the emit_bytes function in trait, which can be used here:

    use serde::{Deserialize, Serialize};
    
    #[derive(Serialize, Deserialize, Copy, Default, Clone)]
    pub struct MyFloot {
        pub density: f32,
    }
    
    use bendy::encoding::{Error, ToBencode};
    
    impl ToBencode for MyFloot {
        const MAX_DEPTH: usize = 1;
    
        fn encode(&self, encoder: bendy::encoding::SingleItemEncoder) -> Result<(), Error> {
            encodert.emit_bytes(self.density.to_be_bytes());
            Ok(())
        }
    }