1

On an embedded device without a heap, I want to parse input data and store the found results in an output slice. I have a reader which allows me to get the currently available data and a parser which matches the input data to the expected format and stores the results in a slice of Parsed::Values which could possibly hold references to the input data.

Since the Rust compiler sees that there could be references to the input data, I can not mutate the data buffer more than once. Since I can ensure that there are no references anymore by manually resetting the output slice to Parsed::Unused, I think that it should be safe to reuse the buffer. How can I achieve to mutate the input buffer in every iteration of the loop?

The following is my minimal example which illustrates my problem:

trait Reader {
    fn peek<'a>(&self, data: &'a mut [u8]) -> &'a [u8]; // copy currently available bytes from ringbuffer
    fn consume(&self, num: usize);                      // drop at the head of input queue
}

trait Parser {
    fn parse<'a>(&self, input: &'a [u8], parsed_value: &mut [Parsed<'a>]) -> Result<(), ()>;
}

enum Parsed<'a> {
    Unused,
    Value(&'a [u8]),
}

fn read_and_parse<'a>(
    reader: impl Reader,
    parser: impl Parser,
    data_buffer: &'a mut [u8],
    values: &mut [Parsed<'a>],
) {
    loop {
        for v in values.iter_mut() {
                                 // This block should ensure that no more
            *v = Parsed::Unused; // references are held into the buffer
        }                        // used in the previous iteration.
        let rx = reader.peek(data_buffer);
        if let Ok(()) = parser.parse(rx, values) {
            return;
        }
        reader.consume(1); // this could be replaced with smarter logic how to drop input bytes
    }
}

playground

error[E0499]: cannot borrow `*data_buffer` as mutable more than once at a time
  --> src/lib.rs:26:30
   |
15 | fn read_and_parse<'a>(
   |                   -- lifetime `'a` defined here
...
26 |         let rx = reader.peek(data_buffer);
   |                  ------------^^^^^^^^^^^-
   |                  |           |
   |                  |           mutable borrow starts here in previous iteration of loop
   |                  argument requires that `*data_buffer` is borrowed for `'a`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Binabik
  • 1,013
  • 11
  • 24
  • It looks like your question might be answered by the answers of [Is this safe? Logically splitting a borrow](https://stackoverflow.com/q/64902078/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Nov 20 '20 at 15:47
  • See also [How to update-or-insert on a Vec?](https://stackoverflow.com/q/47395171/155423); [Double mutable borrow error in a loop happens even with NLL on](https://stackoverflow.com/q/50519147/155423); [Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?](https://stackoverflow.com/q/38023871/155423); [When is it necessary to circumvent Rust's borrow checker?](https://stackoverflow.com/q/50440074/155423) – Shepmaster Nov 20 '20 at 15:47
  • The problem stated in https://stackoverflow.com/questions/50519147/double-mutable-borrow-error-in-a-loop-happens-even-with-nll-on seems to be the closest to my problem. Unfortunately, the answer there reveals that this does not work with the current borrow checker anyways. Still, my problem is different because I was trying to reuse the `values` slice in the second iteration, just after making sure manually that no references to the previous buffer are kept. I found that this can not be compiled successfully, even when using Polonius. So I have to find a different approach or use transmute? – Binabik Nov 20 '20 at 19:09

1 Answers1

-1

One thing that seems to work in many cases is to transmute the slice which can also be used to change lifetimes. Since this is "incredibly unsafe" in general, I personally try to avoid this function and to find different approaches to my problem instead. In many cases, those have even turned out to be cleaner.

Nevertheless, the example here compiles successfully (playground) with transmute:

trait Reader {
    fn peek<'a>(&self, data: &'a mut [u8]) -> &'a [u8];
    fn consume(&self, num: usize);
}

trait Parser {
    fn parse<'a>(&self, input: &'a [u8], parsed_value: &mut [Parsed<'a>]) -> Result<(),()>;
}

enum Parsed<'a> {
    Unused,
    Value(&'a [u8])
}

fn read_and_parse<'a>(reader: impl Reader, parser: impl Parser, data_buffer: &'a mut [u8], values: &mut [Parsed<'a>]) {
    loop {
        for v in values.iter_mut() { // This block should ensure that no more
            *v = Parsed::Unused;     // references are held into the buffer
        }                            // used in the previous iteration.
        let data_buffer = unsafe { core::mem::transmute(&mut *data_buffer) };
        let rx = reader.peek(data_buffer);
        if let Ok(()) = parser.parse(rx, values) {
            return;
        }
        reader.consume(1);
    }
}
Binabik
  • 1,013
  • 11
  • 24