I'm currently trying to implement a mutable slice/view into a buffer that supports taking subslices safely for in-memory message traversal. A minimal example would be
struct MutView<'s> {
data: &'s mut [u8]
}
impl<'s> MutView<'s> {
pub fn new(data: &'s mut [u8]) -> Self {
MutView { data }
}
pub fn subview<'a>(&'a mut self, start: usize, end: usize) -> MutView<'a> {
MutView::new(&mut self.data[start..end])
}
pub fn get<'a>(&'a mut self) -> &'a mut [u8] {
self.data
}
}
However there is a problem with this design, in that if I have a MutView<'s> that is created locally in a function and 's is a longer than the scope of the function (say 's = 'static) I have no way of returning a subview from the function. Something like
fn get_subview(data: &'s mut [u8]) -> MutView<'s> {
MutView::new(data).subview(0, 5)
}
Gives a compile error since MutView::new(data) is a local temporary, so clearly MutView<'a> returned by subview() cannot be returned from the function.
Changing the signature of subview() to
pub fn subview<'a>(&'a mut self, start: usize, end: usize) -> MutView<'s> {
MutView::new(&mut self.data[start..end])
}
Isn't possible since the borrow checker complains that the lifetime of the returned object must be longer than the lifetime of &'a mut self.
The problem I see is that the borrow checker is set up to handle cases where subview() is returning data owned by the &'a mut self, whereas in this case I am returning data owned by some underlying buffer that outlives lifetime 'a, so for safety reasons I still want to do a mutable borrow of &'a mut self for the duration of the lifetime of returned object while also allowing the &'a mut self to be dropped without shortening the lifetime of the returned subview.
The only option I see is to add an into_subview and into_slice method that consumes self. However for my specific case I am generating get_/set_ methods for reading messages with a given schema, meaning I have get/set methods per field in the message body and adding an extra into_* method means a lot of extra code to generate/compile, as well as additional usage complexity. Therefore I would like to avoid this if possible.
Is there a good way of handling this kind of dependency currently in Rust?