Skip to content

Memory layout change for #[repr(C)] struct which contains Option<T> #155934

@aureliar8

Description

@aureliar8

Code

Given this type FwRuleSet

#[repr(C)]
#[derive(Clone, PartialEq, Debug)]
pub struct FwRuleSet {
    pub rules: [Option<FwRule>; MAX_FW_RULES],
}


pub const MAX_FW_RULES: usize = 10;
pub const MAX_PREDICATES_PER_FW_RULE: usize = 2;

#[repr(C)]
#[derive(Default, Debug, PartialEq, Clone)]
pub struct FwRule {
    pub predicates: [Option<FwPredicate>; MAX_PREDICATES_PER_FW_RULE],
}

#[repr(C, u32)]
#[derive(Debug, PartialEq, Clone)]
pub enum FwPredicate {
    // We manually set the enum discriminant to facilitate interop
    InnerDstIpv4(u32) = 1,
    InnerSrcIpv4(u32) = 2,
    InnerDstIpv6(u64) = 3,
    InnerSrcIpv6(u64) = 4,
    InnerIpProto(u8) = 5,
    InnerDstUdp(u32) = 6,
    InnerSrcUdp(u32) = 7,
    InnerDstTcp(u32) = 8,
    InnerSrcTcp(u32) = 9,
    InnerIcmpv4(u32) = 10,
}

The memory representation of the values which contains None changed

let rs = FwRuleSet {
        rules: [const { None }; MAX_FW_RULES],
    };

From

0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

To

FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

In nightly-2026-04-27 and latter

Version it worked on

It most recently worked on: nightly-2026-04-26 or the current stable version

Version with regression

nightly-2026-04-27

More details

I think this is due to #155473
But I'm surprised the memory layout of #[repr(C)] struct can change like that between compiler version

To quickly reproduce you can run the following

git clone git@github.com:aureliar8/memory-layout-bug-reproduction.git
cd memory-layout-bug-reproduction
./run.sh cargo +nightly-2026-04-26 run
./run.sh cargo +nightly-2026-04-27 run
./run.sh cargo  run

@rustbot modify labels: +regression-from-stable-to-nightly -regression-untriaged

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-enumArea: Enums (discriminated unions, or more generally ADTs (algebraic data types))A-layoutArea: Memory layout of typesA-reprArea: the `#[repr(stuff)]` attributeT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions