This looks like a common problem but I could not see the solution myself.
I have parsed value (a struct) that for efficiency reasons internally refers to parts of original input byte stream (represented as a Vec). The parser and parsed structure are part of an external library (GitHub - negamartin/midly: A feature-complete MIDI parser and writer focused on speed.) so I'd prefer not to change that.
The suggested use of the library looks like
let raw_data = std::fs::read("input.mid").unwrap();
let parsed_midi_source = parse(&raw_data);
// Do something with the parsed value here
And here's no problem: input data is always available while parsed values is used.
But in my case I'd like to send the parsed value around (e.g. to another thread). So as I understand for that I have to bundle both source data and parsed value together so the references to the parts of raw data are still valid.
The minimal example of what I tried is
fn parse(raw: &[u8]) -> &u8 {
&raw[1]
}
#[derive(Debug)]
struct Holder<'a> {
source: Vec<u8>,
n: &'a u8,
}
impl Holder<'_> {
fn init(raw: Vec<u8>) -> Holder<'static> {
Holder { source: raw, n: parse(&raw) }
}
}
fn main() {
let file_data = vec![3u8, 4, 5];
let holder = Holder::init(file_data);
println!("Holder {:#?}", holder);
}
The immediate problem is that raw data stays in the function (I thought that it can be moved into the new struct)
Holder { source: raw, n: parse(&raw) }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^
| |
| `raw` is borrowed here
returns a value referencing data owned by the current function
OK, I understand that move may actually copy the value to another place, so I tried to keep the data in an Arc so it stays put somewhere in heap, but then it seems that borrowing value in Arc does not work as I expected: it seems that Arc itself is borrowed instead of the contained value. So that did not work either. So is this the right idea to bundle everything in the same struct, and then how do I tell compiler that the data is still available after the struct is created?
Another question is the declaration of the Holder struct itself. I tried to hint the compiler that parsed value have same lifetime as the containing structure by adding a lifetime annotation that would match the lifetime of the parsed structure. But then the Holder structure itself has explicit lifetime that is not really needed anywhere else as the Holder structure is self-contained (has no dependencies). If that is necessary, how do I hide it from users of Holder structure?