rokdocs
v0.1

1.3. MaybeUninit<T> - 미초기화 메모리

rok::bare::mem 모듈은 미초기화 메모리를 안전하게 다루기 위한 wrapper 와 그에 따르는 ops 를 정의합니다. 일반적인 T 가 항상 초기화되어 있다고 보장되는 반면, MaybeUninit<T> 는 초기화 여부를 type 차원에서 명시적으로 표현합니다.

1.3.1. 동기 - 미초기화 read 는 UB

LLVM 의 freeze 의미론 (2022 년 명확화) 에 따라, 미초기화 메모리에 대한 read 는 정수형이라 하더라도 UB 입니다. 단순히 "garbage 값" 이 흘러나오는 수준이 아니라, 컴파일러가 그 값에 대해 임의의 가정을 깔고 코드를 변형할 수 있다는 뜻입니다.

// C
int x;            // uninit
if (x == 0) { ... }   // 컴파일러가 x 의 값 임의 추정 - true / false 양쪽 dead-code 가능

따라서 T 자체로는 미초기화 상태를 표현할 수 없으며, 이를 위한 별도의 wrapper 가 필요해집니다.

1.3.2. MaybeUninit<T>

struct MaybeUninit<T> { /* T 와 같은 size + align, 단 미초기화 가능 */ }

impl<T> MaybeUninit<T> {
    fn uninit() -> MaybeUninit<T>;          // 미초기화 슬롯
    fn zeroed() -> MaybeUninit<T>;          // 0 으로 채움
    fn new(value: T) -> MaybeUninit<T>;     // 초기화된 슬롯

    fn write(ref self, value: T);           // value 쓰기

    fn as_ptr(self) -> ConstPtr<T>;
    fn as_mut_ptr(ref self) -> Ptr<T>;

    unsafe fn assume_init(self) -> T;
    // SAFETY: write / 외부 mechanism 으로 초기화 보장됐을 때만

}

as_ptr / as_mut_ptr 의 receiver 는 place 여야 합니다: 포인터는 사본이 아니라 그 변수를 가리킵니다. uninit() 은 zeroed() 로 구현될 수 있습니다.

사용 예 - 초기화 단계 분리

다음은 FFI 가 buffer 를 채워주기를 기다렸다가 값을 꺼내는 전형적인 패턴입니다.

let buf: MaybeUninit<[u8; 1024]> = MaybeUninit::uninit();
let p: Ptr<u8> = buf.as_mut_ptr().cast::<u8>();

unsafe {
    read_from_kernel(p, 1024);              // FFI 가 buf 채움
    let initialized: [u8; 1024] = buf.assume_init();
}

write 후 사용

write 는 값을 기록할 뿐 참조를 돌려주지 않습니다 (rok 에는 ref 반환이 없음). 기록한 뒤에는 assume_init 으로 값을 꺼냅니다.

let slot: MaybeUninit<Big> = MaybeUninit::uninit();
slot.write(big_value());
let v: Big = unsafe { slot.assume_init() };
process(v);

1.3.3. Pod 트레잇

trait Pod { /* marker */ }

Plain Old Data 의 약자로, 모든 bit pattern 이 valid 한 type 을 가리킵니다. 1.2 cast 의 reinterpret 가 바로 이 marker 를 요구합니다.

자동 impl적용 안 됨
모든 정수 (i*, u*)Ptr<T> (provenance)
모든 부동소수 (f*)사용자 struct (#[derive(Pod)] 명시)
[T; N] (T:Pod)enum (variant tag - niche)
(T, U, ...) (모두 Pod)
c_char
bool, char (bit invalid 자동 niche 처리)

Pod 가 보장하는 것

Pod 는 다음 세 가지를 한꺼번에 보장합니다.

  1. 모든 bit pattern read 가 안전하다 - uninit 노출이 없습니다.
  2. layout 결정론적: #[repr(C)] 또는 #[repr(transparent)] 가 강제됩니다.
  3. no Drop: Drop 이 구현돼 있으면 Pod 는 자동으로 거부됩니다.
#[derive(Pod)]
#[repr(C)]
struct Vec3 { x: f32; y: f32; z: f32; }
// → reinterpret OK, ffi 안전, etc.

1.3.4. raw mem ops - read / write

저수준 메모리 조작용 free function 들은 다음과 같이 갖춰져 있습니다.

unsafe fn read<T>(src: ConstPtr<T>) -> T;
unsafe fn write<T>(dst: Ptr<T>, value: T);
unsafe fn read_volatile<T>(src: ConstPtr<T>) -> T;
unsafe fn write_volatile<T>(dst: Ptr<T>, value: T);

unsafe fn copy<T>(src: ConstPtr<T>, dst: Ptr<T>, count: usize);
                                               // memmove (overlap OK)
unsafe fn copy_nonoverlapping<T>(src: ConstPtr<T>, dst: Ptr<T>, count: usize);
                                               // memcpy (overlap UB - 더 빠름)

unsafe fn write_bytes<T>(dst: Ptr<T>, value: u8, count: usize);
                                               // memset (모든 byte = value)

unsafe fn swap<T>(a: Ptr<T>, b: Ptr<T>);
unsafe fn replace<T>(dst: Ptr<T>, value: T) -> T;     // 기존 read + write
unsafe fn drop_in_place<T>(p: Ptr<T>);               // drop glue 실행 (메모리 free X)

이들 대부분은 Ptr<T> 의 메서드 형태로도 노출돼 있으며 (1.1.2), 본 module 의 free function 은 그에 대한 ergonomic 대안으로 자리합니다.

drop_in_place 는 drop glue 를 실행합니다: 그 타입 자신의 drop, 그 다음 필드들의 drop 을 선언 역순으로 (2.13.4).

1.3.5. transmute_unchecked - escape hatch

1.2 cast 와 동일한 동작을 가지며, 본 module 에서도 alias 형태로 함께 노출됩니다.

unsafe fn transmute_unchecked<A, B>(a: A) -> B;        // size check 없음

1.3.6. align / size util

fn align_of<T>() -> usize;                  // T::align()
fn size_of<T>() -> usize;                   // T::size()
fn align_of_val<T>(val: T) -> usize;
fn size_of_val<T>(val: T) -> usize;         // 값으로 받음 - unsized 타입 없음, size_of::<T>() 와 같음

fn min_align_of<T>() -> usize;              // align_of 와 같음 - Rust 호환

이들은 unsafe 가 아닙니다. 사실상 Reflect 트레잇 (2.12 reflect) 의 free function 형태이며, 편의를 위해 rok::bare::mem module 에도 함께 노출돼 있습니다.

1.3.7. intrinsic 매핑

각 항목과 컴파일러 intrinsic 의 대응 관계는 다음과 같습니다.

항목intrinsic
MaybeUninit<T>rok.mem.assume_init (no-op)
Pod(marker - derive 자동)
read / writerok.mem.read.T
copy / copy_nonoverlappingrok.mem.memcpy / memmove
write_bytesrok.mem.memset

1.3.8. 다른 언어와의 비교

측면CRustZigrok
미초기화 표현T x; (silent)MaybeUninit<T>undefinedMaybeUninit<T>
Pod / 안전 transmute없음bytemuck::Pod (lib)@bitCastPod + reinterpret
미초기화 read UB허용UBUBUB
memcpy / memmovememcpyptr::copy*@memcpyrok::bare::mem::copy*

GitHub에서 이 페이지 고치기