Here's a tricky Rust question. Maybe you have an exampl ethat shows that this is unsound.
Here's the pattern: Why don't we allow borrowing &[Box<T>]
as &[&T]
? It seems useful and sound enough. What about the mutable version though?
The code is (playpen link)
#![feature(box_syntax)]
use std::mem;
use std::fmt::Debug;
/// This is convenient
fn sliceboxes<'a, T: ?Sized>(xs: &'a [Box<T>]) -> &'a [&'a T]
{
unsafe {
mem::transmute(xs)
}
}
/// NOTE: Mutable version is unsound(?)
///
/// We might have an opportunity to swap a box for another non-boxed &mut T,
/// is that a disaster? Not sure.
fn sliceboxes_mut<'a, T: ?Sized>(xs: &'a mut [Box<T>]) -> &'a mut [&'a mut T]
{
unsafe {
mem::transmute(xs)
}
}
fn main() {
let xs = [box 1, box 2];
println!("{:?}", sliceboxes(&xs));
let ys: &[Box<Debug>] = &[box 1, box "hi"];
println!("{:?}", sliceboxes(&ys));
let zs: &mut [Box<Debug>] = &mut [box 1, box "hi"];
{
let zsl = sliceboxes_mut(zs);
zsl.swap(0, 1)
}
println!("{:?}", sliceboxes(&zs));
// If it is unsound, how do we demonstrate it?
let (ws, mut string): (&mut [Box<Debug>], _) = (
&mut [box 1, box 3],
"hi".to_string());
{
let wsl = sliceboxes_mut(ws);
wsl[1] = &mut string;
}
println!("{:?}", ws);
}
I wonder if the mutable version is sound. Intuition says it's maybe not so good to swap an allocated-T box-T with a non-allocated-U box-U like swapping &mut 1 with Box<String>
.
On the other hand, the type compatibility is very strict. We can only assign &mut T
values of the exact same lifetime. Is this enough to make it sound?
-
Is the
&[&T]
version sound (looks easy -- should be yes) -
Is the mutable version sound?
-
Is it only sound, if we restrict the return value to
-> &'a mut [&mut T]
-
Is the mutable version no good at all?