1

Suppose I have a trait representing a pure function; memory has been provided for the outputs, and both inputs and outputs are expected to be serde/bincode encoded. Is it possible to make this do what I want, or restructure things to enable the same end idiomatically?

trait Pure {
    fn pure(inputs: &[&[u8]], outputs: &[&mut [u8]]);
}

struct Identity {}

impl Pure for Identity {
    fn pure(inputs: &[&[u8]], outputs: &[&mut [u8]]) {
        let x: u32 = bincode::deserialize(inputs[0]).expect("input decode failed");
        bincode::serialize_into(outputs[0], &x).unwrap();
    }
}

makes the compiler complain:

error[E0508]: cannot move out of type `[&mut [u8]]`, a non-copy slice
  --> src/main.rs:10:33
   |
10 |         bincode::serialize_into(outputs[0], &x).unwrap();
   |                                 ^^^^^^^^^^
   |                                 |
   |                                 cannot move out of here
   |                                 move occurs because `outputs[_]` has type `&mut [u8]`, which does not implement the `Copy` trait
oddcowboy
  • 73
  • 5

1 Answers1

1

There are two issues with your code. First, if you want to be able to mutate the inner references, then you must have a mutable outer reference. IOW, your pure function should take a &mut[&mut [u8]] for parameter.

Second, in order to pass the mutable reference to deserialize_into, you need to reborrow it, to make it clear to the compiler that you are not permanently taking it out of output:

trait Pure {
    fn pure(inputs: &[&[u8]], outputs: &mut[&mut [u8]]);
                                     // ^^^ add this
}

struct Identity {}

impl Pure for Identity {
    fn pure(inputs: &[&[u8]], outputs: &mut[&mut [u8]]) {
                                     // ^^^ add this
        let x: u32 = bincode::deserialize(inputs[0]).expect("input decode failed");
        bincode::serialize_into(&mut*outputs[0], &x).unwrap();
                             // ^^^^^ add this
    }
}

Playground

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • Thank you for the speedy fix! Adding the outer mut is intuitive in hindsight; but, woah, "reborrowing" is new to me. Is there nice documentation somewhere illustrating in more detail the conditions when this is generally needed? – oddcowboy May 12 '21 at 06:48
  • 1
    I'm not sure about documentation on reborrowing. There is an [open issue](https://github.com/rust-lang/reference/issues/788) about improving the documentation (mainly about the implicit/automatic reborrowing that the compiler is able to do in some cases). Also, [this question](https://stackoverflow.com/questions/66288291/what-is-a-reborrow-and-how-does-it-influence-the-code-the-compiler-generates) has some explanations about explicit reborrows. – Jmb May 12 '21 at 07:01