Skip to content

<[MaybeUninit<T>; N]>::transpose() results in miscompilation #144645

@nazar-pc

Description

@nazar-pc

I tried this code:

#![feature(maybe_uninit_uninit_array_transpose)]

#[derive(Debug)]
struct S {
    a: [[u8; 32]; 2],
}

use std::mem::MaybeUninit;
fn main() {
    let mut s = Box::<S>::new_uninit();
    let s_ptr = s.as_mut_ptr();
    let a_ptr = unsafe { &raw mut (*s_ptr).a };
    let a = unsafe {
        &mut *a_ptr.cast::<[MaybeUninit<[u8; 32]>; 2]>()
    };
    a.transpose().write([[1u8; 32]; 2]);
    dbg!(unsafe { s.assume_init() });
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=262fa5c70ca0cf7b7f8d9e8688bac722

I expected to see this happen: compiles and runs correctly, including under Miri.

Instead, this happened: results in UB, including as detected by Miri.

Variations of the code that do work correctly:

#![feature(maybe_uninit_uninit_array_transpose)]

#[derive(Debug)]
struct S {
    a: [[u8; 32]; 2],
}

use std::mem::MaybeUninit;
fn main() {
    let mut s = Box::<S>::new_uninit();
    let s_ptr = s.as_mut_ptr();
    let a_ptr = unsafe { &raw mut (*s_ptr).a };
    let a = unsafe {
        &mut *a_ptr.cast::<MaybeUninit<[[u8; 32]; 2]>>()
    };
    a.write([[1u8; 32]; 2]);
    dbg!(unsafe { s.assume_init() });
}

The difference is MaybeUninit<[[u8; 32]; 2]> instead of [MaybeUninit<[u8; 32]>; 2], which makes .transpose() unnecessary. My understanding is that both should be perfectly valid.

To verify that theory, I modified original code to this, just to avoid .transpose(), while leaving everything else the same:

#![feature(maybe_uninit_uninit_array_transpose)]

#[derive(Debug)]
struct S {
    a: [[u8; 32]; 2],
}

use std::mem::MaybeUninit;
fn main() {
    let mut s = Box::<S>::new_uninit();
    let s_ptr = s.as_mut_ptr();
    let a_ptr = unsafe { &raw mut (*s_ptr).a };
    let a = unsafe {
        &mut *a_ptr.cast::<[MaybeUninit<[u8; 32]>; 2]>()
    };
    
    for a in a {
        a.write([1u8; 32]);
    }
    
    dbg!(unsafe { s.assume_init() });
}

This compiles and runs correctly, including under Miri.
From this I make a conclusion that .transpose() is to blame here.

Meta

rustc --version --verbose:

rustc 1.90.0-nightly (498ae9fed 2025-07-28)
binary: rustc
commit-hash: 498ae9fed2e7d90821d70a048f3770f91af08957
commit-date: 2025-07-28
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-gubCategory: the reverse of a compiler bug is generally UB

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions