MaybeUninit と配列

// 著作権 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0

use std::mem::MaybeUninit;
use std::ptr;

fn main() {
    let input = b"RUST";

    let mut buf = [const { MaybeUninit::<u8>::uninit() }; 2048];

    // メモリに値を書き込んで要素を初期化する
    for (i, input_byte) in input.iter().enumerate() {
        unsafe {
            let dst = buf.as_mut_ptr().add(i);
            ptr::write((*dst).as_mut_ptr(), *input_byte);
        }
    }

    // 配列の一部が初期化されている場合は、
    // unsafe を使ってその部分を切り出せる
    let ptr_to_init_subslice = buf.as_ptr() as *const u8;
    let init =
        unsafe { std::slice::from_raw_parts(ptr_to_init_subslice, input.len()) };
    let text = std::str::from_utf8(init).unwrap();
    println!("{text}");

    // 初期化済みの要素は手動でドロップしなければならない
    for element in &mut buf[0..input.len()] {
        unsafe {
            element.assume_init_drop();
        }
    }
}

未初期化メモリの配列を作成するには、::uninit() コンストラクタを const コンテキスト内で使用できます。

通常どおり、ptr::write を使って値を初期化します。

.assume_init() は配列ではそれほど簡単には使えません。これにはすべての値が 初期化されている必要がありますが、バッファを再利用する場合はそうならないことがあります。この例では、 初期化済みのバイトだけをポインタで切り出して文字列スライスを作成しています。

部分的に初期化された配列のサブスライスを作成する際は、 所有権と drop の正しい実装に注意してください。注意: MaybeUninit<T> は その T に対して drop を呼び出しません。

MaybeUninit<[u8;2048]>[MaybeUninit::<u8>; 2048] とは異なります。これは、 未初期化メモリの配列と、 未初期化の要素を含む配列の違いです。

  • MaybeUninit<[u8;2048]> は「すべてか無か」です。配列全体を完全に初期化してから assume_init を呼び出すか、 MaybeUninit<[u8; 2048]> のまま保持して [u8; 2048] として触れないようにしなければなりません。
  • [MaybeUninit<u8>; 2048] では要素を 1 つずつ初期化でき、その後、 初期化済みの先頭部分だけのサブスライスを取り出し、 std::slice::from_raw_parts を通してそれを [u8] として扱えます。
  • slice_assume_init_ref が安全なのは、スライス内のすべての要素が 初期化されている場合だけです。この例では、ちょうどそれらのバイトを書き込んだ後にのみ &buf[..input.len()] を渡しています。
  • T に drop が必要な場合は、初期化済みの要素に対して assume_init_drop() を 手動で呼び出さなければなりません。これを省くとメモリリークになります。ただし、 未初期化の要素に対して呼び出すのは未定義動作です。