Skip to content

Commit 0ecb9d2

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

File tree

1 file changed

+45
-63
lines changed

1 file changed

+45
-63
lines changed

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

+45-63
Original file line numberDiff line numberDiff line change
@@ -351,90 +351,72 @@ 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+
# }
414389
```
415390

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)):
391+
the single-variant data-carrying enum `SingleVariantDataCarrying` has the same
392+
layout as `SomeStruct`.
393+
394+
### Layout of multi-variant enums with one inhabited variant
395+
396+
The layout of **multi-variant** enums with **one inhabited variant** is the same
397+
as that of the single-variant enum containing that same inhabited variant.
398+
399+
Here:
420400

421401
```rust
422-
# enum Void { }
402+
# use std::mem::{size_of, align_of};
423403
struct SomeStruct { x: u32 }
424-
enum SomeEnum {
425-
SomeVariant { x: u32 },
426-
UninhabitedVariant { y: Void },
404+
enum SingleVariantDataCarrying {
405+
DataCarryingVariant(SomeStruct),
406+
}
407+
enum Void0 {}
408+
enum Void1 {}
409+
enum MultiVariantSingleHabited {
410+
DataCarryingVariant(SomeStruct),
411+
Uninhabited0(Void0),
412+
Uninhabited1(Void1),
427413
}
428-
429414
# fn main() {
430415
# 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));
416+
# assert_eq!(size_of::<SingleVariantDataCarrying>(), size_of::<MultiVariantSingleHabited>());
417+
# assert_eq!(align_of::<SingleVariantDataCarrying>(), align_of::<MultiVariantSingleHabited>());
439418
# }
440419
```
420+
421+
the `MultiVariantSingleHabited` enum has the same layout as
422+
`SingleVariantDataCarrying`.

0 commit comments

Comments
 (0)