Skip to content

Commit 82e66dc

Browse files
committed
Resolve layout of single variant enums
Closes rust-lang#79.
1 parent 97cbda4 commit 82e66dc

File tree

1 file changed

+47
-64
lines changed

1 file changed

+47
-64
lines changed

Diff for: reference/src/layout/enums.md

+47-64
Original file line numberDiff line numberDiff line change
@@ -351,90 +351,73 @@ enum Enum1<T> {
351351
}
352352
```
353353

354-
## Unresolved questions
355-
356354
### Layout of single variant enums
357355

358-
[Issue #79.](https://github.com/rust-rfcs/unsafe-code-guidelines/issues/79)
359-
360-
Enums that contain a **single variant** and which do not have an
361-
explicit `#[repr]` annotation are an important special case. Since
362-
there is only a single variant, the enum must be instantiated with
363-
that variant, which means that the enum is in fact equivalent to a
364-
struct. The question then is to what extent we should **guarantee**
365-
that the two share an equivalent layout, and also how to define the
366-
interaction with uninhabited types.
356+
**Single variant data-carrying*** enums without a `repr()` annotation have the
357+
same layout as the variant field. **Single variant fieldless** enums have the
358+
same layout as a unit struct.
367359

368-
As presently implemented, the compiler will use the same layout for
369-
structs and for single variant enums (as long as they do not have a
370-
`#[repr]` annotation that overrides that choice). So, for example, the
371-
struct `SomeStruct` and the enum `SomeEnum` would have an equivalent
372-
layout ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3697ac684c3d021892694956df957653)):
360+
Here:
373361

374362
```rust
375-
struct SomeStruct;
376-
enum SomeEnum {
377-
SomeVariant,
378-
}
379-
363+
# use std::mem::{size_of, align_of};
364+
struct UnitStruct;
365+
enum SingleVariantFieldless { FieldlessVariant }
380366
# fn main() {
381-
# use std::mem;
382-
let x = SomeStruct;
383-
let y = SomeEnum::SomeVariant;
384-
assert_eq!(
385-
mem::size_of_val(&x),
386-
mem::size_of_val(&y),
387-
"types have different sizes",
388-
);
389-
println!("types both have size {}", std::mem::size_of_val(&x));
367+
assert_eq!(size_of::<SingleVariantFieldless>(), size_of::<UnitStruct>());
368+
assert_eq!(align_of::<SingleVariantFieldless>(), align_of::<UnitStruct>());
369+
// assert_eq!(call_abi_of::<SingleVariantFieldless>(), call_abi_of::<UnitStruct>());
370+
// assert_eq!(niches_of::<SingleVariantFieldless>(), niches_of::<UnitStruct>());
390371
# }
391372
```
392373

393-
Similarly, the struct `SomeStruct` and the enum `SomeVariant` in this
394-
example would also be equivalent in their layout
395-
([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=924724764419f846c788a8763da45992)):
374+
the single-variant fieldless enum `SingleVariantFieldless` has the same layout
375+
as `UnitStruct`.
376+
377+
Here:
396378

397379
```rust
380+
# use std::mem::{size_of, align_of};
398381
struct SomeStruct { x: u32 }
399-
enum SomeEnum {
400-
SomeVariant { x: u32 },
382+
enum SingleVariantDataCarrying {
383+
DataCarryingVariant(SomeStruct),
401384
}
402-
403385
# fn main() {
404-
# use std::mem;
405-
let x = SomeStruct { x: 22 };
406-
let y = SomeEnum::SomeVariant { x: 22 };
407-
assert_eq!(
408-
mem::size_of_val(&x),
409-
mem::size_of_val(&y),
410-
"types have different sizes",
411-
);
412-
println!("types both have size {}", mem::size_of_val(&x));
413-
}
386+
assert_eq!(size_of::<SingleVariantDataCarrying>(), size_of::<SomeStruct>());
387+
assert_eq!(align_of::<SingleVariantDataCarrying>(), align_of::<SomeStruct>());
388+
// assert_eq!(call_abi_of::<SingleVariantDataCarrying>(), call_abi_of::<SomeStruct>());
389+
// assert_eq!(niches_of::<SingleVariantDataCarrying>(), niches_of::<SomeStruct>());
390+
# }
414391
```
415392

416-
In fact, the compiler will use this optimized layout even for enums
417-
that define multiple variants, as long as all but one of the variants
418-
is uninhabited
419-
([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3cc1484c5b91097f3dc2015b7c207a0e)):
393+
the single-variant data-carrying enum `SingleVariantDataCarrying` has the same
394+
layout as `SomeStruct`.
395+
396+
### Layout of multi-variant enums with one inhabited variant
397+
398+
The layout of **multi-variant** enums with **one inhabited variant** is the same
399+
as that of the single-variant enum containing that same inhabited variant.
400+
401+
Here:
420402

421403
```rust
422-
# enum Void { }
423-
struct SomeStruct { x: u32 }
424-
enum SomeEnum {
425-
SomeVariant { x: u32 },
426-
UninhabitedVariant { y: Void },
404+
# use std::mem::{size_of, align_of};
405+
# struct SomeStruct { x: u32 }
406+
# enum SingleVariantDataCarrying {
407+
# DataCarryingVariant(SomeStruct),
408+
# }
409+
enum Void0 {}
410+
enum Void1 {}
411+
enum MultiVariantSingleHabited {
412+
DataCarryingVariant(SomeStruct),
413+
Uninhabited0(Void0),
414+
Uninhabited1(Void1),
427415
}
428-
429416
# fn main() {
430417
# use std::mem;
431-
let x = SomeStruct { x: 22 };
432-
let y = SomeEnum::SomeVariant { x: 22 };
433-
assert_eq!(
434-
mem::size_of_val(&x),
435-
mem::size_of_val(&y),
436-
"types have different sizes",
437-
);
438-
println!("types both have size {}", mem::size_of_val(&x));
418+
assert_eq!(size_of::<SingleVariantDataCarrying>(), size_of::<MultiVariantSingleHabited>());
419+
assert_eq!(align_of::<SingleVariantDataCarrying>(), align_of::<MultiVariantSingleHabited>());
420+
// assert_eq!(call_abi_of::<SingleVariantDataCarrying>(), call_abi_of::<MultiVariantSingleHabited>());
421+
// assert_eq!(niches_of::<SingleVariantDataCarrying>(), niches_of::<MultiVariantSingleHabited>());
439422
# }
440423
```

0 commit comments

Comments
 (0)