|
| 1 | +# Type system attributes |
| 2 | + |
| 3 | +The following [attributes] are used for changing how a type can be used. |
| 4 | + |
| 5 | +## The `non_exhaustive` attribute |
| 6 | + |
| 7 | +The *`non_exhaustive` attribute* indicates that a type or variant may have |
| 8 | +more fields or variants added in the future. It can be applied to |
| 9 | +[`struct`s][struct], [`enum`s][enum] and, `enum` variants. |
| 10 | + |
| 11 | +The `non_exhaustive` attribute uses the [_MetaWord_] syntax and thus does not |
| 12 | +take any inputs. |
| 13 | + |
| 14 | +Within the defining crate, `non_exhaustive` has no effect. |
| 15 | + |
| 16 | +```rust |
| 17 | +#[non_exhaustive] |
| 18 | +pub struct Config { |
| 19 | + pub window_width: u16, |
| 20 | + pub window_height: u16, |
| 21 | +} |
| 22 | + |
| 23 | +#[non_exhaustive] |
| 24 | +pub enum Error { |
| 25 | + Message(String), |
| 26 | + Other, |
| 27 | +} |
| 28 | + |
| 29 | +pub enum Message { |
| 30 | + #[non_exhaustive] Send { from: u32, to: u32, contents: String }, |
| 31 | + #[non_exhaustive] Reaction(u32), |
| 32 | + #[non_exhaustive] Quit, |
| 33 | +} |
| 34 | + |
| 35 | +// Non-exhaustive structs can be constructed as normal within the defining crate. |
| 36 | +let config = Config { window_width: 640, window_height: 480 }; |
| 37 | + |
| 38 | +// Non-exhaustive structs can be matched on exhaustively within the defining crate. |
| 39 | +if let Config { window_width, window_height } = config { |
| 40 | + // ... |
| 41 | +} |
| 42 | + |
| 43 | +let error = Error::Other; |
| 44 | +let message = Message::Reaction(3); |
| 45 | + |
| 46 | +// Non-exhaustive enums can be matched on exhaustively within the defining crate. |
| 47 | +match error { |
| 48 | + Error::Message(ref s) => { }, |
| 49 | + Error::Other => { }, |
| 50 | +} |
| 51 | + |
| 52 | +match message { |
| 53 | + // Non-exhaustive variants can be matched on exhaustively within the defining crate. |
| 54 | + Message::Send { from, to, contents } => { }, |
| 55 | + Message::Reaction(id) => { }, |
| 56 | + Message::Quit => { }, |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +Outside of the defining crate, types annotated with `non_exhaustive` have limitations that |
| 61 | +preserve backwards compatibility when new fields or variants are added. |
| 62 | + |
| 63 | +Non-exhaustive types cannot be constructed outside of the defining crate: |
| 64 | + |
| 65 | +- [`struct`s][struct] cannot be constructed with a [struct expression]. |
| 66 | +- [`enum`][enum] instances can be constructed in an [enumeration variant expression]. |
| 67 | + |
| 68 | +```rust,ignore (requires multiple crates) |
| 69 | +// `Config`, `Error` and `Message` are types defined in an upstream crate that have been |
| 70 | +// annotated as `#[non_exhaustive]`. |
| 71 | +use upstream::{Config, Error, Message}; |
| 72 | +
|
| 73 | +// Cannot construct an instance of `Config`, if new fields were added in |
| 74 | +// a new version of `upstream` then this would fail to compile, so it is |
| 75 | +// disallowed. |
| 76 | +let config = Config { window_width: 640, window_height: 480 }; |
| 77 | +
|
| 78 | +// Can construct an instance of `Error`, new variants being introduced would |
| 79 | +// not result in this failing to compile. |
| 80 | +let error = Error::Message("foo".to_string()); |
| 81 | +
|
| 82 | +// Cannot construct an instance of `Message::Send` or `Message::Reaction`, |
| 83 | +// if new fields were added in a new version of `upstream` then this would |
| 84 | +// fail to compile, so it is disallowed. |
| 85 | +let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; |
| 86 | +let message = Message::Reaction(0); |
| 87 | +
|
| 88 | +// Cannot construct an instance of `Message::Quit`, if this were converted to |
| 89 | +// a tuple-variant `upstream` then this would fail to compile. |
| 90 | +let message = Message::Quit; |
| 91 | +``` |
| 92 | + |
| 93 | +There are limitations when matching on non-exhaustive types outside of the defining crate: |
| 94 | + |
| 95 | +- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), |
| 96 | +a [_StructPattern_] or [_TupleStructPattern_] must be used which must include a `..`. |
| 97 | +- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not |
| 98 | + contribute towards the exhaustiveness of the arms. |
| 99 | + |
| 100 | +```rust, ignore (requires multiple crates) |
| 101 | +// `Config`, `Error` and `Message` are types defined in an upstream crate that have been |
| 102 | +// annotated as `#[non_exhaustive]`. |
| 103 | +use upstream::{Config, Error, Message}; |
| 104 | +
|
| 105 | +// Cannot match on a non-exhaustive enum without including a wildcard arm. |
| 106 | +match error { |
| 107 | + Error::Message(ref s) => { }, |
| 108 | + Error::Other => { }, |
| 109 | + // would compile with: `_ => {},` |
| 110 | +} |
| 111 | +
|
| 112 | +// Cannot match on a non-exhaustive struct without a wildcard. |
| 113 | +if let Ok(Config { window_width, window_height }) = config { |
| 114 | + // would compile with: `..` |
| 115 | +} |
| 116 | +
|
| 117 | +match message { |
| 118 | + // Cannot match on a non-exhaustive struct enum variant without including a wildcard. |
| 119 | + Message::Send { from, to, contents } => { }, |
| 120 | + // Cannot match on a non-exhaustive tuple or unit enum variant. |
| 121 | + Message::Reaction(type) => { }, |
| 122 | + Message::Quit => { }, |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +Non-exhaustive types are always considered inhabited in downstream crates. |
| 127 | + |
| 128 | +[attributes]: ../attributes.md |
| 129 | +[_MetaWord_]: ../attributes.md#meta-item-attribute-syntax |
| 130 | +[enum]: ../items/enumerations.md |
| 131 | +[`if let`]: ../expressions/if-expr.md#if-let-expressions |
| 132 | +[`match`]: ../expressions/match-expr.md |
| 133 | +[struct]: ../items/structs.md |
| 134 | +[struct expression]: ../expressions/struct-expr.md |
| 135 | +[enumeration variant expression]: ../expressions/enum-variant-expr.md |
| 136 | +[_StructPattern_]: ../patterns.md#struct-patterns |
| 137 | +[_TupleStructPattern_]: ../patterns.md#tuple-struct-patterns |
0 commit comments