Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation for arbitrary_enum_discriminant feature #639

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/expressions/operator-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ well as the following additional casts. Here `*T` means either `*const T` or
| Type of `e` | `U` | Cast performed by `e as U` |
|-----------------------|-----------------------|----------------------------------|
| Integer or Float type | Integer or Float type | Numeric cast |
| C-like enum | Integer type | Enum cast |
| Field-less enum | Integer type | Enum cast |
| `bool` or `char` | Integer type | Primitive to integer cast |
| `u8` | `char` | `u8` to `char` cast |
| `*T` | `*V` where `V: Sized` \* | Pointer to pointer cast |
Expand Down
132 changes: 111 additions & 21 deletions src/items/enumerations.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
>
> _EnumItem_ :\
> &nbsp;&nbsp; _OuterAttribute_<sup>\*</sup>\
> &nbsp;&nbsp; [IDENTIFIER]&nbsp;( _EnumItemTuple_ | _EnumItemStruct_
> | _EnumItemDiscriminant_ )<sup>?</sup>
> &nbsp;&nbsp; [IDENTIFIER]&nbsp;( _EnumItemTuple_ | _EnumItemStruct_ )<sup>?</sup>
> _EnumItemDiscriminant_<sup>?</sup>
>
> _EnumItemTuple_ :\
> &nbsp;&nbsp; `(` [_TupleFields_]<sup>?</sup> `)`
Expand Down Expand Up @@ -56,22 +56,68 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
```

In this example, `Cat` is a _struct-like enum variant_, whereas `Dog` is simply
called an enum variant. Each enum instance has a _discriminant_ which is an
integer associated to it that is used to determine which variant it holds. An
opaque reference to this discriminant can be obtained with the
[`mem::discriminant`] function.
called an enum variant.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The re-organization looks fine, but we lose our definition of a field-less enum. This suggestion re-adds it. I don't actually know if the anchor actually works. It should just create an anchor that can be linked too without changing any styles or making it clickable.

Suggested change
called an enum variant.
called an enum variant.
An enum where no constructors contain fields are called a *<a name="field-less-enum">field-less enum</a>*.


## Custom Discriminant Values for Field-Less Enumerations
## Discriminants

If there is no data attached to *any* of the variants of an enumeration,
then the discriminant can be directly chosen and accessed.
Each enum instance has a _discriminant_: an integer logically associated to it
that is used to determine which variant it holds. An opaque reference to this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using mem::discriminant should be in the accessing the discriminant section.

discriminant can be obtained with the [`mem::discriminant`] function.

These enumerations can be cast to integer types with the `as` operator by a
[numeric cast]. The enumeration can optionally specify which integer each
discriminant gets by following the variant name with `=` followed by a [constant
expression]. If the first variant in the declaration is unspecified, then it is
set to zero. For every other unspecified discriminant, it is set to one higher
than the previous variant in the declaration.
Under the [default representation], the discriminant is interpreted as
an `isize` value. However, the compiler is allowed to use a smaller type (or
another means of distinguishing variants) in its actual memory layout.

If the [primitive representation] or the [`C` representation] is used, the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this paragraph is trying to say.

leading bytes of a variant (e.g., two bytes if `#[repr(u16)]` is used), will
jswrenn marked this conversation as resolved.
Show resolved Hide resolved
correspond exactly to the discriminant.

### Assigning Discriminant Values
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this entire section, it only makes sense for field-less enums or enums with a primitive representation (or the C representation?). The way it is written, it seems like explicit discriminants are only for those and implicit applies to all. Instead, it should state that having a non-opaque (feel free to pick a different descriptor) discriminant applies in those situations and in all others, it is opaque and must only be accessed via mem::discriminant.


#### Explicit Discriminants

In two circumstances, the discriminant of a variant may be explicitly set by
following the variant name with `=` and a [constant expression]:

<ol>
<li>

if the enumeration is "C-like" (i.e., it has no tuple or struct variants); e.g.:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if the enumeration is "C-like" (i.e., it has no tuple or struct variants); e.g.:
if the enumeration is field-less. For example:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per #639 (comment), these terms may not be truly interchangeable.


```rust
# #![feature(arbitrary_enum_discriminant)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature flags aren't needed; the prose will only be merged after the feature is stable.

Suggested change
# #![feature(arbitrary_enum_discriminant)]

enum Enum {
Foo = 3,
Bar = 2,
Baz = 1,
}
```
</li>
<li>

if a [primitive representation] is used; e.g.:
jswrenn marked this conversation as resolved.
Show resolved Hide resolved

```rust
# #![feature(arbitrary_enum_discriminant)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature flag not needed.

Suggested change
# #![feature(arbitrary_enum_discriminant)]

#[repr(u8)]
enum Enum {
Unit = 3,
Tuple(u16),
Struct {
a: u8,
b: u16,
} = 1,
}
```
</li>
</ol>

#### Implicit Discriminants

If a discriminant for a variant is not specified, then it is set to one higher
than the discriminant of the previous variant in the declaration. If the
discriminant of the first variant in the declaration is unspecified, then
it is set to zero.

```rust
enum Foo {
Expand All @@ -84,14 +130,11 @@ let baz_discriminant = Foo::Baz as u32;
assert_eq!(baz_discriminant, 123);
```

Under the [default representation], the specified discriminant is interpreted as
an `isize` value although the compiler is allowed to use a smaller type in the
actual memory layout. The size and thus acceptable values can be changed by
using a [primitive representation] or the [`C` representation].
#### Restrictions

It is an error when two variants share the same discriminant.

```rust,ignore
```rust,compile_fail
enum SharedDiscriminantError {
SharedA = 1,
SharedB = 1
Expand All @@ -107,7 +150,7 @@ enum SharedDiscriminantError2 {
It is also an error to have an unspecified discriminant where the previous
discriminant is the maximum value for the size of the discriminant.

```rust,ignore
```rust,compile_fail
#[repr(u8)]
enum OverflowingDiscriminantError {
Max = 255,
Expand All @@ -122,6 +165,53 @@ enum OverflowingDiscriminantError2 {
}
```

### Accessing Discriminant Values

#### Casting

If there is no data attached to *any* of the variants of an enumeration, then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to describe a field-less enum here. Just use the term.

Suggested change
If there is no data attached to *any* of the variants of an enumeration, then
If the enum is a field-less enum, then

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per #639 (comment), these terms may not be completely interchangeable. If "field-less" is meant to be synonymous to "C-like", then this change would be incorrect.

the discriminant can be directly accessed with a [numeric cast]; e.g.:

```rust
enum Enum {
Unit,
Tuple(),
Struct{},
}

assert_eq!(0, Enum::Unit as isize);
assert_eq!(1, Enum::Tuple() as isize);
assert_eq!(2, Enum::Struct{} as isize);
```

#### Pointer Casting

If the enumeration specifies a [primitive representation], then the
discriminant may be reliably accessed via unsafe pointer casting:

```rust
#[repr(u8)]
enum Enum {
Unit,
Tuple(bool),
Struct{a: bool},
}

impl Enum {
fn discriminant(&self) -> u8 {
unsafe { *(self as *const Self as *const u8) }
}
}

let unit_like = Enum::Unit;
let tuple_like = Enum::Tuple(true);
let struct_like = Enum::Struct{a: false};

assert_eq!(0, unit_like.discriminant());
assert_eq!(1, tuple_like.discriminant());
assert_eq!(2, struct_like.discriminant());
```

## Zero-variant Enums

Enums with zero variants are known as *zero-variant enums*. As they have
Expand Down
2 changes: 1 addition & 1 deletion src/type-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ used with any other representation.
[`size_of`]: ../std/mem/fn.size_of.html
[`Sized`]: ../std/marker/trait.Sized.html
[dynamically sized types]: dynamically-sized-types.html
[C-like enumerations]: items/enumerations.html#custom-discriminant-values-for-field-less-enumerations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link change is wrong. We aren't properly defining a field-less enum anywhere after this PR.

[C-like enumerations]: items/enumerations.html#explicit-discriminants
[zero-variant enumerations]: items/enumerations.html#zero-variant-enums
[undefined behavior]: behavior-considered-undefined.html
[27060]: https://github.com/rust-lang/rust/issues/27060
Expand Down