Rust: mem::transmute on types of different size (but the type size is known)

Created on 2 Feb 2018  路  9Comments  路  Source: rust-lang/rust

Problem

When I try to compile this method, the compiler reports that mem::transmute is being called on types of different sizes. (Which doesn't make sense because T size should be known in this context).

pub fn partition2<T: Sized>(source: [T; 52]) -> [[T; 13]; 4] {
    use std::mem;
    unsafe { mem::transmute(source) }
}

Compiler Error

error[E0512]: transmute called with types of different sizes
 --> src/main.rs:9:14
  |
9 |     unsafe { mem::transmute(source) }
  |              ^^^^^^^^^^^^^^
  |
  = note: source type: [T; 52] (size can vary because of T)
  = note: target type: [[T; 13]; 4] (size can vary because of T)

Working Example

Replacing T with a struct and it compiles fine (also works with String):

pub struct Card {
    value: u8,
    suit: Suit
}

pub enum Suit {
    Spades,
    Clubs,
    Diamonds,
    Hearts,
}

pub fn partition(source: [Card; 52]) -> [[Card; 13]; 4] {
    use std::mem;
    unsafe { mem::transmute(source) }
}

Meta

rustc 1.24.0-nightly (23032d0af 2017-11-30)
binary: rustc
commit-hash: 23032d0afa2b0e0c60a9b2ae62709f846d90007c
commit-date: 2017-11-30
host: x86_64-apple-darwin
release: 1.24.0-nightly
LLVM version: 4.0
C-enhancement T-compiler

Most helpful comment

The check for equal size is pre-monomorphization (intentionally). So the compiler would have to reason about the size of the type parameter T abstractly to some degree.

All 9 comments

I doubt if we can easily fix this without pushing this E0512 error to post-monomorphization. Allowing this means the compiler has to be able to solve equations to prove that size_of::<T>() * 52 == (size_of::<T>() * 13) * 4.

size_of::<T>() doesn鈥檛 really matter though right? Whatever it is post-monomorphization, it鈥檚 the same on both sides.

The check for equal size is pre-monomorphization (intentionally). So the compiler would have to reason about the size of the type parameter T abstractly to some degree.

I think having a compiler able to solve first degree equations like that is going to be useful once we have const generics.

I believe the correct answer here is "just use pointer casting". I don't think there's a good argument for adding complexity here when the method's use is discouraged anyway.

I'm confident, however, that eventually we'll have const bounds enough to allow things like where const size_of::<T>() == size_of::<U>(), but that's a long way off -- the current RFC doesn't even know that N+1 == N+1, having intentionally left all such equation solving for a future RFC: https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md#equality-of-two-abstract-const-expressions

Triage: two years later, I think we should close this. Maybe it can be done someday, as @scottmcm points out, but it's unclear when that, if ever, will happen.

It would be useful to have a working example of the "just use pointer casting" technique in the interim while waiting for something to be implemented.

I'm arriving at this thread after writing a function which attempts to transmute a [MaybeUninit<T>; const_size]. transmute is the recommended method when initializing that type, and it's unclear how to accomplish this when generics are involved.

There is a crate which allows for this call to be made - transmute

An an update to anyone following this, there's a group working on safe transmutes that may end up being the solution for this desire: https://github.com/rust-lang/lang-team/issues/21

Was this page helpful?
0 / 5 - 0 ratings