Description
This design is prototyped in #1924. An older, more limited design is prototyped in transmute-slice-experiment.
Progress
- Support slice DST transmutations that preserve referent size exactly
- Support sized-to-slice DST transmutations that preserve referent size exactly
- Can build on @jswrenn's size specialization autoref prototype
- Support transmutations that permit shrinking size using a macro symbol (e.g.,
transmute_ref!(@allow_shrink src)
)
Outline
- Expand
transmute_ref!
et al to support&Src -> &Dst
whereSrc
andDst
are both?Sized + KnownLayout
and theirKnownLayout::LAYOUT.size_info
s are identical - Same as above, but where
LAYOUT.size_info
s are not identical, but support a runtime metadata fix-up operation
Design
Currently, transmute_ref!
validates:
Src
andDst
have the same sizeSrc
has alignment at least as large asDst
However, the more general condition is that Src
and Dst
have the same size as defined by KnownLayout::LAYOUT.size_info
. Since these macros already only support being called in a const context, we can check this condition via PME without creating a footgun (namely, errors will always be surfaced immediately at the usage site).
It should be trivial to enforce this via a const fn
, although it will require Src: KnownLayout
and Dst: KnownLayout
bounds, meaning it won't work on our MSRV, so we'll have to gate it on Rust toolchain version.
We may also be able to use the same technique to detect when LAYOUT.size_info
s are not identical, but when reference transmutes can be performed infallibly via a metadata fix-up operation. For example, &[u16] -> &[u8]
is possible by doubling the slice length.
Old design
Here is an old design that only supported slices, not arbitrary slice DSTs
For slices, when transmuting [Src]
into [Dst]
, the conditions are identical:
- The fat pointer cast from
[Src]
to[Dst]
preserves length ifSrc
andDst
have the same size [T]
has the same alignment asT
If we could figure out a way to get transmute_ref!
to infer Src
and Dst
when those are the transmuted types and Src
and Dst
when the transmuted types are [Src]
and [Dst]
, then we could get it to support both sized and slice transmutations.
As a bonus, we could also support size_of::<Src>()
being a multiple of size_of::<Dst>()
, although this would require runtime code to update the slice metadata.
Design
We could do this by introducing a trait like the following:
trait TransmuteRefHelper {
type Elem;
}
impl<T: Sized> TransmuteRefHelper for T {
type Elem = T;
}
impl<T: Sized> TransmuteRefHelper for [T] {
type Elem = T;
}
We'd need to figure out some way of coaxing the correct inference. Currently we do the following:
Lines 214 to 217 in d204727
Instead we need to make this more generic. Naively, we could do:
fn from_elem<T: TransmuteRefHelper>(e: T::Elem) -> &'static T
Then we could modify the code in transmute_ref!
something like:
// Infers `t` as either equal to the sized source value or
// the element type of the source slice.
let mut t = loop {};
e = from_elem(t);
This works, but it means we can't support const fn
on our MSRV, which doesn't permit trait bounds in const fn
. If we can figure out another way to accomplish the same inference, then we could avoid breaking const support.