Avoid Union Except for C Interop

2026-08-30 浏览 (1)


id: union-01 original_id: P.UNS.UNI.01 level: P impact: HIGH

Avoid Union Except for C Interop

Summary

Only use union for FFI with C code. For Rust-only code, use enum with explicit tags.

Rationale

  • Unions require unsafe to read (any field access is unsafe)
  • Easy to read wrong field, causing undefined behavior
  • Enums are type-safe and the compiler tracks the active variant
  • Unions don't run destructors properly

Bad Example

// DON'T: Use union for space optimization in Rust-only code
union IntOrFloat {
    i: i32,
    f: f32,
}

fn bad_usage() {
    let mut u = IntOrFloat { i: 42 };

    // BAD: Reading wrong field is UB
    let f = unsafe { u.f };  // UB if i was the last written field
}

// DON'T: Use union for variant types
union Variant {
    string: std::mem::ManuallyDrop<String>,
    number: i64,
}

// Problems:
// 1. Must manually track which variant is active
// 2. Must manually call drop on String variant
// 3. Easy to have memory leaks or double-free

Good Example

// DO: Use enum for variant types in Rust
enum Variant {
    String(String),
    Number(i64),
}

// Compiler tracks active variant, runs correct destructor

// DO: Use union only for C FFI
#[repr(C)]
union CUnion {
    i: i32,
    f: f32,
}

// When interfacing with C code that uses this union
extern "C" {
    fn c_function_returns_union() -> CUnion;
    fn c_function_takes_union(u: CUnion);
}

// DO: Wrap in safe API with explicit variant tracking
#[repr(C)]
pub struct SafeUnion {
    tag: u8,
    data: CUnion,
}

impl SafeUnion {
    pub fn as_int(&self) -> Option<i32> {
        if self.tag == 0 {
            // SAFETY: Tag indicates integer variant is active
            Some(unsafe { self.data.i })
        } else {
            None
        }
    }
}

When Union Is Appropriate

  1. C FFI: Matching C union layout for interoperability
  2. MaybeUninit: The standard library uses union internally
  3. Very low-level optimization: Only after profiling and careful safety analysis

Alternatives to Union

Use CaseInstead of UnionUse
Variant typesunion + tagenum
Optional valueunion + boolOption<T>
Type punninguniontransmute or from_ne_bytes
Uninitialized memoryunionMaybeUninit<T>

Checklist

  • Is this for C FFI? If not, use enum
  • If union is necessary, is there a tag tracking active variant?
  • Are destructors handled correctly for Drop types?
  • Is the union #[repr(C)] for FFI?

Related Rules

  • union-02: Don't use union variants across lifetimes
  • ffi-13: Ensure consistent data layout

你可能感兴趣的文章

Do Not Expose Raw Pointers in Public APIs

Unsafe Code Authors Must Verify Safety Invariants

Ensure Consistent Data Layout for Custom Types

Add Safety Section in Docs for Public Unsafe Functions

Do Not Use Union Variants Across Different Lifetimes

Use MaybeUninit for Uninitialized Memory

Ensure C-ABI Compatibility for Strings Between Rust and C

Do Not Dereference Pointers Cast to Misaligned Types

Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types

Choose Appropriate Data Layout for Struct/Tuple/Enum

  • 所属分类: AI
  • 本文标签: rust
  • 版权声明: 本文链接 https://seaxiang.com/blog/z6rLbGN2