-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Const generics: Generic array transmutes do not work #61956
Comments
Transmuting a Yes, |
Aha, I didn't check that generally speaking To be honest, this particular rule seems bogus, given that layout-compatibility between non-wrapped Considering the lengths that were taken to ensure maximal layout-compatibility between And arrays should not be put in this category, given that each item of Further, not providing a reliable transmute path between As an aside, while your observation is highly relevant to the target use case that motivated this issue, I should point out that it is not sufficient to explain why the reduced example from the OP failed to compile, as transmuting from |
I totally agree with you.
But, going even deeper on transmute You can't even write this today:
Clearly T is the same size as T, but compiler isn't sure about it. Transmute only seems to work if the types you feed into it are known, and you cant really use it generically. |
This is incorrect:
This is incorrect: -- cc @eddyb I think the |
Isn't ABI compatibility a superset of layout compatibility? If, for example, the fields of a struct On the other hand, I can see how ABI compatibility could encompass other things in addition to layout compatibility, such as a guarantee that
Are you sure about this? My understanding so far was that for example, although |
|
I think you are right. For some reason I thought that this issue had already been resolved to allow this, but that has not happened yet. rust-lang/unsafe-code-guidelines#73 The TL;DR: is that, if have an |
To quote @RalfJung 5 minutes ago on the topic you linked to:
I would propose continuing this part of the discussion there. |
Yep, that kind of followed from this Zulip discussion that we just had: https://rust-lang.zulipchat.com/#narrow/stream/136281-t-lang.2Fwg-unsafe-code-guidelines/topic/validity.20of.20unions Feel free to also chime in there. Sorry for the noise, I completely misremembered what we wanted to do there. |
The hope is to eventually have APIs for Also, to state the obvious, the transmute you mentioned has the same restrictions and |
@gnzlbg We do have some special-cases already, e.g. you can transmute between rust/src/librustc/ty/layout.rs Lines 1586 to 1604 in 10deeae
We could add a general case, to replace I'd argue it's kind of broken already, this should check that no rust/src/librustc/ty/layout.rs Lines 1643 to 1647 in 10deeae
And this should check for alignments larger than 1 (which can make a ZST have an effect on size): rust/src/librustc/ty/layout.rs Lines 1659 to 1660 in 10deeae
|
This should be aloud. My experimental const generic crate aljabar is blocked by this issue and currently has to rely on What is especially annoying about this is that |
@maplant Note that as was figured out by @DutchGhost, the error message for arrays is actually a misleading red herring. Even a plain identity transmute from T to T doesn't work. /// Fails to build with a weird message about type size issues
fn identity<T: Sized>(x: T) -> T { unsafe { core::mem::transmute(x) } } Yields
I also added a pointer-based workaround for the lack of array transmute in the opening post if it can be of any use to you. |
|
@HadrienG2 right, as another person pointed out to me pointers can easily transmute between arrays. Thanks for the help. |
@maplant The transmute check is performed on the generic definition, like all the type-checking in Rust. If you emulate it with |
There is another way to convert (using unions), e.g. like this. I would be in favour of adding a new assume_init function for arrays of MaybeUninit, that way one can reduce the amount of unsafe code needed to initialise an array to just one single line. |
What would be the signature of that function? |
Something like: unsafe fn MaybeUninit::array_assume_init<T, const N: usize>(array: [MaybeUninit<T>; N]) -> [T; N]; Matching the style of |
Yeah, there are definitely some const generic functions that should be added. Const generics weren't ready for that yet when |
…me_init, r=dtolnay Add `MaybeUninit` method `array_assume_init` When initialising an array element-by-element, the conversion to the initialised array is done through `mem::transmute`, which is both ugly and does not work with const generics (see rust-lang#61956). This PR proposes the associated method `array_assume_init`, matching the style of `slice_assume_init_*`: ```rust unsafe fn array_assume_init<T, const N: usize>(array: [MaybeUninit<T>; N]) -> [T; N]; ``` Example: ```rust let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array(); array[0].write(0); array[1].write(1); array[2].write(2); // SAFETY: Now safe as we initialised all elements let array: [i32; 3] = unsafe { MaybeUninit::array_assume_init(array) }; ``` Things I'm unsure about: * Should this be a method of array instead? * Should the function be const?
I believe this is by-design right now from a lang perspective (see #86281 (comment)). The answer for the "array initialization example" is |
I was looking back on #61956 (comment) and and ended up opening #88290 for that aspect - which is what I should've done back then, my bad. |
Alternatively, the generic transmute workaround can be written as a single expression:
This version may be less error-prone because you can't forget to |
…ferences * Changes: - Add implementation of GhostBorrow for arrays of references to GhostCell - Add implementation of GhostBorrowMut for arrays of references to GhostCell. - Various comments and documentation nits. * Motivation: More flexibility. * Implementation Note: Due to `mem::transmute` not working well with const generics (as per rust-lang/rust#61956), `ptr::read` is used instead of `mem::transmute`. This is expected to be safe, if verbose.
I think the same problem also applies to tuples Giving a generic tuple Is this mapped or even correlated? edit: playground |
The example Was this lifted by #101520? |
I know this is a comment from ages ago, but remember you can use mem::transmute_copy(&ManuallyDrop::new(arr)) |
Will hopefully enable a lot of these platforms back in future Rust versions rust-lang/rust#61956
* Add recipe for libwinit * Fix typo * Disable some platforms Will hopefully enable a lot of these platforms back in future Rust versions rust-lang/rust#61956
I used this relatively readable workaround for const generic arrays. let arr: [T; N] = arr.map(|elem: MaybeUninit<T>| unsafe { elem.assume_init() }); I haven't checked into how optimized it is, but I'd imagine it's likely slower than every other solution here. Still, the readability is a big plus for me. |
This trivial transmute should work:
Unfortunately, it refuses to build with the following error:
This means that the array initialization example from the
core::mem::MaybeUninit
documentation currently cannot be generalized to arrays of arbitrary size, as transmutes from[MaybeUnit<T>; N]
to[T; N]
won't compile.A horribly error-prone workaround for generic array transmute is:
The text was updated successfully, but these errors were encountered: