The official docs for mem::transmute include a blessed code example containing the following comment:
// This now has three mutable references pointing at the same // memory. `slice`, the rvalue ret.0, and the rvalue ret.1. // `slice` is never used after `let ptr = ...`, and so one can // treat it as "dead", and therefore, you only have two real // mutable slices.
This seems pretty handwavy for official documentation (when can I 'treat a mutable ref as "dead"'? When can I claim that some mutable refs are "real" and some are not?), especially because the callsite might look like this, where I can reasonably claim that slice in fact is used after the specified point.
fn bar<T>(slice: &mut [T]) { ... }
fn foo<T>(slice: &mut [T]) {
{
let (front, back) = split_at_stdlib(slice, 5);
bar(front);
bar(back);
}
bar(slice);
}
The comment is related only to the context of that function's body. The local slice parameter is indeed never used again, and we can see that plainly because split_at_stdlib returns without using slice again.
The caller's input slice is not relevant. This is because Rust's normal lifetime rules will prevent the input slice from being used until the returned subslices are dead. If we desugar the elided lifetimes in the signature, we get this:
fn split_at_stdlib<'a, T>(slice: &'a mut [T], mid: usize) -> (&'a mut [T], &'a mut [T])
This means the returned slices are borrowed from the input slice. Rust simply won't let you use slice again until both of the returned slices are dead either by being explicitly discarded, going out of scope, or NLL reducing their lifetime.
To be absolutely clear, that means the code sample in your question is completely fine, and is sound.
The reason why the comment calls this out is because slice::from_raw_parts_mut lets you do a lot of very unsafe things (similar to transmute), such as converting a pointer to a (slice) reference of any lifetime, as happens in the code sample you're referring to. This is sound only if the preconditions of slice::from_raw_parts_mut are met and if Rust's borrowing model is still upheld. This is why split_at_stdlib needs to be careful to not use slice after using slice::from_raw_parts_mut to obtain the subslices -- that would violate the aliasing rule.
However, once split_at_stdlib returns, Rust's borrowing model is upheld by the signature of that function. That's why the function itself isn't unsafe! The function invokes unsafe under the hood, but it wraps up that unsafe code in a way that is safe to use. In other words, there are no arguments you could provide to split_at_stdlib that would cause it to do something unsound.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With