diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 71b0243..dd3ea98 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -20,6 +20,12 @@ pub struct Config { pub window_height: u16, } +#[non_exhaustive] +pub struct Token; + +#[non_exhaustive] +pub struct Id(pub u64); + #[non_exhaustive] pub enum Error { Message(String), @@ -34,11 +40,13 @@ pub enum Message { // Non-exhaustive structs can be constructed as normal within the defining crate. let config = Config { window_width: 640, window_height: 480 }; +let token = Token; +let id = Id(4); // Non-exhaustive structs can be matched on exhaustively within the defining crate. -if let Config { window_width, window_height } = config { - // ... -} +let Config { window_width, window_height } = config; +let Token = token; +let Id(id_number) = id; let error = Error::Other; let message = Message::Reaction(3); @@ -64,30 +72,49 @@ Non-exhaustive types cannot be constructed outside of the defining crate: - Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed with a [_StructExpression_] \(including with [functional update syntax]). +- The implicitly defined same-named constant of a [unit-like struct][struct], + or the same-named constructor function of a [tuple struct][struct], + has a [visibility] no greater than `pub(crate)`. + That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility + is `pub(crate)`, and otherwise the visibility of the two items is the same + (as is the case without `#[non_exhaustive]`). - [`enum`][enum] instances can be constructed. +The following examples of construction do not compile when outside the defining crate: + ```rust,ignore -// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been -// annotated as `#[non_exhaustive]`. -use upstream::{Config, Error, Message}; +// These are types defined in an upstream crate that have been annotated as +// `#[non_exhaustive]`. +use upstream::{Config, Token, Id, Error, Message}; -// Cannot construct an instance of `Config`, if new fields were added in +// Cannot construct an instance of `Config`; if new fields were added in // a new version of `upstream` then this would fail to compile, so it is // disallowed. let config = Config { window_width: 640, window_height: 480 }; -// Can construct an instance of `Error`, new variants being introduced would +// Cannot construct an instance of `Token`; if new fields were added, then +// it would not be a unit-like struct any more, so the same-named constant +// created by it being a unit-like struct is not public outside the crate; +// this code fails to compile. +let token = Token; + +// Cannot construct an instance of `Id`; if new fields were added, then +// its constructor function signature would change, so its constructor +// function is not public outside the crate; this code fails to compile. +let id = Id(5); + +// Can construct an instance of `Error`; new variants being introduced would // not result in this failing to compile. let error = Error::Message("foo".to_string()); -// Cannot construct an instance of `Message::Send` or `Message::Reaction`, +// Cannot construct an instance of `Message::Send` or `Message::Reaction`; // if new fields were added in a new version of `upstream` then this would // fail to compile, so it is disallowed. let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; let message = Message::Reaction(0); -// Cannot construct an instance of `Message::Quit`, if this were converted to +// Cannot construct an instance of `Message::Quit`; if this were converted to // a tuple-variant `upstream` then this would fail to compile. let message = Message::Quit; ``` @@ -95,16 +122,18 @@ let message = Message::Quit; There are limitations when matching on non-exhaustive types outside of the defining crate: - When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), - a [_StructPattern_] must be used which must include a `..`. Tuple variant constructor visibility - is lowered to `min($vis, pub(crate))`. + a [_StructPattern_] must be used which must include a `..`. A tuple variant's constructor's + [visibility] is reduced to be no greater than `pub(crate)`. - When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not contribute towards the exhaustiveness of the arms. +The following examples of matching do not compile when outside the defining crate: + ```rust, ignore -// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been -// annotated as `#[non_exhaustive]`. -use upstream::{Config, Error, Message}; +// These are types defined in an upstream crate that have been annotated as +// `#[non_exhaustive]`. +use upstream::{Config, Token, Id, Error, Message}; // Cannot match on a non-exhaustive enum without including a wildcard arm. match error { @@ -118,6 +147,13 @@ if let Ok(Config { window_width, window_height }) = config { // would compile with: `..` } +// Cannot match a non-exhaustive unit-like or tuple struct except by using +// braced struct syntax with a wildcard. +// This would compile as `let Token { .. } = token;` +let Token = token; +// This would compile as `let Id { 0: id_number, .. } = id;` +let Id(id_number) = id; + match message { // Cannot match on a non-exhaustive struct enum variant without including a wildcard. Message::Send { from, to, contents } => { }, @@ -147,3 +183,4 @@ Non-exhaustive types are always considered inhabited in downstream crates. [enum]: ../items/enumerations.md [functional update syntax]: ../expressions/struct-expr.md#functional-update-syntax [struct]: ../items/structs.md +[visibility]: ../visibility-and-privacy.md