rokdocs
v0.1

2.5. union

union 은 여러 필드가 같은 메모리를 공유하는 비태그 컨테이너입니다. 한 시점에 어느 필드의 값이 의미 있는지를 언어가 추적하지 않으며, 그 책임은 사용자에게 있습니다.

union_decl      ::= "union" identifier generics? where_clause? "{" named_field_list? "}"

2.5.1. 기본 형태

가장 단순한 형태의 union 은 다음과 같이 선언합니다.

union EpollData {
    ptr: usize;
    fd: i32;
    val: u64;
}

본체와 필드 문법은 struct 의 named struct 와 동일합니다. 다만 키워드가 union 이라는 점, 그리고 모든 필드가 같은 메모리 위치를 공유한다는 의미론에서 차이가 납니다.

제네릭 union

union 은 struct 처럼 bound, where 절, 기본값을 가진 타입 매개변수 (2.10) 를 가질 수 있습니다. 각 인스턴스는 자기 레이아웃을 가진 별개의 타입이며, 그 크기와 정렬은 그 인자에 대한 필드 타입을 따릅니다.

union Bits<T> { v: T; raw: u64; }
union Small<T = u16> { v: T; raw: u8; }

let a = Bits::<f64> { raw: 0x3FF0000000000000u64 };
let b = Bits { v: 7i32 };              // 준 필드에서 추론한 Bits<i32>
let x = unsafe { a.v };                // 1.0 (the bits of 1.0f64)

union 리터럴의 인자는 주어진 필드 하나에서 추론합니다. bound 는 인스턴스마다 검사하며, 진단은 인스턴스를 이름으로 부릅니다 (reading field `v` of union `Bits<i32>` needs an `unsafe` block). union 은 적어도 필드 하나를 선언합니다.

2.5.2. 의미론

다음 예시를 통해 union 의 동작 방식을 살펴보겠습니다.

let x = EpollData { fd: 5i32 };
x.fd = 7i32;                    // 쓰기 - 안전
let y = unsafe { x.val };       // 읽기 - unsafe 필요. x 의 비트 패턴을 u64 로 재해석

한 인스턴스를 어떤 필드로 생성하면 그 필드의 비트가 메모리에 쓰입니다. 이후 다른 필드로 읽으면 같은 메모리의 비트를 그 필드 타입으로 재해석합니다. 필드 쓰기는 안전하지만, 필드 읽기는 unsafe 안에서만 할 수 있습니다. 읽은 비트가 그 타입의 유효한 값인지 (예: bool, char, 포인터) 언어가 보장하지 않기 때문입니다.

2.5.3. 안전성의 위치

union 의 비트 재해석은 언어가 정의한 동작이지만, 그 결과의 의미는 프로그램 도메인의 책임입니다. 예를 들어 f32 필드에 1.0 을 쓰고 i32 필드로 읽으면 1065353216 (0x3F800000) 이 되고, 반대로 i32 비트를 f32 로 읽으면 IEEE 754 의 비트 표현에 따른 실수값이 되며, 이 값이 도메인적으로 의미 있는지는 언어가 검증하지 않습니다.

이 모델은 C 의 union 과 다릅니다. C 에서는 read-from-different-member 가 미정의 동작이지만, rok 에서는 비트 재해석으로 정의됩니다. 다만 결과가 유효한 값이라는 보장이 없으므로 읽기는 unsafe 로 표시합니다.

2.5.4. 기본 레이아웃은 컴파일러 자유

기본 union 레이아웃은 컴파일러 자유입니다. 가장 큰 필드의 크기와 정렬만 만족하면 되며, 필드 정렬과 패딩의 정확한 결정은 컴파일러가 담당합니다.

만약 C ABI 호환 union 이 필요하다면 #[repr(C)] 을 부착하면 됩니다.

#[repr(C)]
union CUnion {
    a: i32;
    b: f32;
}

#[repr(C)] 가 부착된 union 은 C 의 union 과 비트 수준에서 호환되며, FFI 인터페이스에서 사용합니다. 정확한 의미는 내장 속성 에서 정의합니다.

2.5.5. union vs #[repr(C)] union 차이

두 형태의 차이는 다음과 같습니다.

형태사용처레이아웃비트 호환
union T { ... }일반 코드컴파일러 자유rok 내부에서만 보장
#[repr(C)] union T { ... }FFIC ABI 동일C 코드와 호환

2.5.6. 가시성 / 중복 정의

struct 와 같은 규칙을 따릅니다.

경고

union 필드 읽기는 비트 재해석이며 unsafe 안에서만 할 수 있습니다 (쓰기는 안전). 읽은 비트가 그 타입의 유효한 값인지, 도메인적으로 의미 있는지는 작성자의 책임입니다.

2.5.7. union 과 합 타입의 차이

union 과 합 타입은 겉보기에 비슷해 보일 수 있으나, 실제로는 전혀 다른 도구입니다.

union  Foo { a: i32; b: f32; }    // 같은 메모리, 태그 없음
type   Bar = i32 | f32;            // 별개 메모리, 태그 있음

각 측면에서의 차이는 다음 표에 정리됩니다.

측면union합 타입 (A | B)
어느 필드가 활성인지 추적하지 않습니다태그로 추적
필드 사이 변환비트 재해석변형 변경 (값 재구성)
메모리 사용가장 큰 필드 크기태그 + 가장 큰 변형 크기
필드 읽기unsafe 필요 (비트 재해석)패턴 / is 로 안전하게

이처럼 두 도구의 용도는 완전히 다릅니다. union 은 비트 수준의 다른 해석을 제공하는 도구이고, 합 타입은 값 수준의 다른 종류를 표현하는 도구입니다. 따라서 둘을 혼동하지 않도록 주의해야 합니다.

GitHub에서 이 페이지 고치기