abi_stable/docs/
sabi_nonexhaustive.rs

1/*!
2
3Using the `#[sabi(kind(WithNonExhaustive(...)))]` helper attribute for
4`#[derive(StableAbi)]` allows you to store the enum
5in `NonExhaustive`,
6using it as a non-exhaustive enum across ffi.
7
8The enum can then be wrapped in a
9[`NonExhaustive<>`](../../nonexhaustive_enum/struct.NonExhaustive.html),
10but can only be converted back into it if the discriminant is valid in that context.
11
12Nonexhaustive enums can safely add variants in minor versions,
13giving library authors some flexibility in their design.
14
15# Items
16
17These are the items relevant to nonexhaustive enums:
18
19`Enum`: this is the annotated enum,which does not derive `StableAbi`,
20requiring it to be wrapped in a `NonExhaustive<>` to be passed through ffi.
21
22`Enum_NE`(generated): A type alias for `NonExhaustive<Enum,_,_>`.
23
24`Enum_NEMarker`(generated):
25A marker type which implements StableAbi with the layout of `Enum`,
26used as a phantom field of NonExhaustive.
27
28`Enum_Storage`(generated):
29A type used as storage space by the `NonExhaustive<>` type to store the enum.
30
31`Enum_Bounds`(generated):
32Acts as an alias for the traits that were specified in the `traits(...)` parameter.
33This is only created if the `traits(...)` parameter is specified.
34
35`Enum_Interface`(generated):
36Describes the traits required when constructing a `NonExhaustive<Enum,_,_>`
37and usable with it afterwards
38(this is a type that implements [`InterfaceType`]).
39
40# Parameters
41
42These are the required and optional parameters for the
43`#[sabi(kind(WithNonExhaustive(...)))]` helper attribute.
44
45### Specifying alignment (optional parameter)
46
47Specifies the alignment of Enum_Storage.
48
49With a specific alignemnt.<br>
50Syntax:`align = integer_literal`<br>
51Example:`align = 8`<br>
52A non-literal constant expression can also be used:<br>
53Syntax:`align = { (<statement>;)* <expression> }`<br>
54Example:`align = { foo() }` <br>
55
56With the same alignment is that of another type.<br>
57Syntax:`align = type`<br>
58Example:`align = usize`<br>
59
60### size (required parameter)
61
62Specifies the size of Enum_Storage.
63
64The size of Enum_TE in bytes.<br>
65Syntax:`size=integer_literal`<br>
66Example:`size = 8` <br>
67A non-literal constant expression can also be used:<br>
68Syntax:`size = { (<statement>;)* <expression> }`<br>
69Example:`size = { foo() }` <br>
70
71The size of Enum_TE is that of of another type<br>
72Syntax:`size = type`<br>
73Example:`size = [usize;8]`<br>
74Recommendation:
75Use a type that has a stable layout,generally a concrete type.
76It is a bad idea to use `Enum` since its size is allowed to change.<br>
77
78### Traits (optional parameter)
79
80Specifies the traits required when constructing NonExhaustive from this enum and
81usable after constructing it.
82
83If neither this parameter nor interface are specified,
84no traits will be required in `NonExhaustive<>` and none will be usable.
85
86Syntax:`traits(trait0, trait1 = false, trait2 = true, trait3)`
87
88Example0:`traits(Debug,Display)`<br>
89Example1:`traits(Sync = false, Debug,Display)`<br>
90Example2:`traits(Sync = false, Send = false, Debug, Display)`<br>
91Example3:`traits(Clone, Debug, Display, Error)`<br>
92
93All the traits are optional.
94
95These are the valid traits:
96
97- `Send`: Required by default, must be unrequired with `Send = false`
98
99- `Sync`: Required by default, must be unrequired with `Sync = false`
100
101- `Clone`
102
103- `Debug`
104
105- `Display`
106
107- `Serialize`: `serde::Serialize`.Look below for clarifications on how to use serde.
108
109- `Deserialize`: `serde::Deserialize`.Look below for clarifications on how to use serde.
110
111- `Eq`
112
113- `PartialEq`
114
115- `Ord`
116
117- `PartialOrd`
118
119- `Hash`
120
121- `Error`: `std::error::Error`
122
123### Interface (optional parameter)
124
125This allows using a pre-existing to specify which traits are
126required when constructing `NonExhaustive<>` from this enum and are then usable with it.
127
128The type describes which traits are required using the [`InterfaceType`] trait.
129
130Syntax:`interface=type`
131
132Example0:`interface = ()`.
133This means that no trait is usable/required.<br>
134
135Example1:`interface = CloneInterface`.
136This means that only Clone is usable/required.<br>
137
138Example2:`interface = PartialEqInterface`.
139This means that only Debug/PartialEq are usable/required.<br>
140
141Example3:`interface = CloneEqInterface`.
142This means that only Debug/Clone/Eq/PartialEq are usable/required.<br>
143
144The `*Interface` types from the examples come from the
145`abi_stable::erased_types::interfaces` module.
146
147
148### NonExhaustive assertions
149
150This generates a static assertion that the listed types can be stored within `NonExhaustive`.
151
152Note that this attribute is implicitly added for non-generic enums,
153it is only required for generic enums.
154
155Syntax:`assert_nonexhaustive = type`<br>
156Example:`assert_nonexhaustive = Foo<u8>`<br>
157Example:`assert_nonexhaustive = Foo<RArc<u8>>`<br>
158Example:`assert_nonexhaustive = Foo<RBox<u8>>`<br>
159
160Syntax:`assert_nonexhaustive(type0, type1)`<br>
161Example:`assert_nonexhaustive(Foo<RArc<u8>>)`<br>
162Example:`assert_nonexhaustive(Foo<u8>, Foo<RVec<()>>)`<br>
163
164[full example below](#using_assert_nonexhaustive_example)
165
166# `serde` support
167
168`NonExhaustive<Enum, Storage, Interface>` only implements `serde::{Serialize,Deserialize}`
169if `Interface` allows them in its [`InterfaceType`] implementation,
170and also implements the [`SerializeEnum`] and [`DeserializeEnum`] traits.
171
172# Examples
173
174### Defining a (de)serializable nonexhaustive enum.
175
176This defines a nonexhaustive enum and demonstrates how it is (de)serialized.
177
178For a more realistic example you can look at the
179"examples/2_nonexhaustive/interface" crate in the repository for this crate.
180
181```
182use abi_stable::{
183    external_types::{RawValueBox, RawValueRef},
184    nonexhaustive_enum::{DeserializeEnum, NonExhaustive, SerializeEnum},
185    prefix_type::WithMetadata,
186    sabi_extern_fn,
187    std_types::{RBoxError, RErr, ROk, RResult, RStr, RString},
188    StableAbi,
189};
190
191use serde::{Deserialize, Serialize};
192
193#[repr(u8)]
194#[derive(StableAbi, Debug, Clone, PartialEq, Deserialize, Serialize)]
195#[sabi(kind(WithNonExhaustive(
196    // Determines the maximum size of this enum in semver compatible versions.
197    size = [usize;10],
198    // Determines the traits that are required when wrapping this enum in NonExhaustive,
199    // and are then available with it.
200    traits(Debug,Clone,PartialEq,Serialize,Deserialize),
201)))]
202// The `#[sabi(with_constructor)]` helper attribute here generates constructor functions
203// that look take the fields of the variant as parameters and return a `ValidTag_NE`.
204#[sabi(with_constructor)]
205#[non_exhaustive]
206pub enum ValidTag {
207    Foo,
208    Bar,
209    Tag {
210        name: RString,
211        tag: RString,
212    },
213}
214
215/*
216//This was generated by the StableAbi derive macro on ValidTag.
217pub type ValidTag_NE=
218    NonExhaustive<
219        ValidTag,
220        ValidTag_Storage,
221        ValidTag_Interface,
222    >;
223*/
224
225/// This describes how the enum is serialized.
226impl SerializeEnum<ValidTag> for ValidTag_Interface {
227    /// A type that `ValidTag` is converted into(inside `SerializeEnum::serialize_enum`),
228    /// and then serialized.
229    type Proxy = RawValueBox;
230
231    fn serialize_enum(this: &ValidTag) -> Result<RawValueBox, RBoxError> {
232        match serde_json::value::to_raw_value(this) {
233            Ok(v) => Ok(v.into()),
234            Err(e) => Err(RBoxError::new(e)),
235        }
236    }
237}
238
239/// This describes how the enum is deserialized.
240impl<'a> DeserializeEnum<'a, ValidTag_NE> for ValidTag_Interface {
241    /// A type that is deserialized,
242    /// and then converted into `ValidTag_NE` inside `DeserializeEnum::deserialize_enum`.
243    type Proxy = RawValueRef<'a>;
244
245    fn deserialize_enum(s: RawValueRef<'a>) -> Result<ValidTag_NE, RBoxError> {
246        Module::VALUE.deserialize_tag()(s.get_rstr()).into_result()
247    }
248}
249
250# fn main(){
251
252assert_eq!(
253    serde_json::from_str::<ValidTag_NE>(r#""Foo""#).unwrap(),
254    ValidTag::Foo_NE()
255);
256
257assert_eq!(
258    serde_json::from_str::<ValidTag_NE>(r#""Bar""#).unwrap(),
259    ValidTag::Bar_NE()
260);
261
262assert_eq!(
263    serde_json::from_str::<ValidTag_NE>(
264        r#"
265    {"Tag":{
266        "name":"what",
267        "tag":"the"
268    }}
269"#
270    )
271    .unwrap(),
272    ValidTag::Tag_NE("what".into(), "the".into())
273);
274
275assert_eq!(
276    &serde_json::to_string(&ValidTag::Foo_NE()).unwrap(),
277    r#""Foo""#,
278);
279
280assert_eq!(
281    &serde_json::to_string(&ValidTag::Bar_NE()).unwrap(),
282    r#""Bar""#,
283);
284
285# }
286
287// In this struct:
288//
289// - `#[sabi(kind(Prefix))]`
290// Declares this type as being a prefix-type, generating both of these types:
291//
292//     - Module_Prefix`: A struct with the fields up to (and including) the field with the
293//     `#[sabi(last_prefix_field)]` attribute.
294//
295//     - Module_Ref`: An ffi-safe pointer to a `Module`,with methods to get `Module`'s fields.
296//
297// - `#[sabi(missing_field(panic))]`
298//     makes the field accessors of `ModuleRef` panic when attempting to
299//     access nonexistent fields instead of the default of returning an Option<FieldType>.
300//
301#[repr(C)]
302#[derive(StableAbi)]
303#[sabi(kind(Prefix))]
304#[sabi(missing_field(panic))]
305pub struct Module {
306    /// `#[sabi(last_prefix_field)]`means that it is the last field in the struct
307    /// that was defined in the first compatible version of the library
308    /// (0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
309    /// requiring new fields to always be added below preexisting ones.
310    #[sabi(last_prefix_field)]
311    pub deserialize_tag:
312        extern "C" fn(s: RStr<'_>) -> RResult<ValidTag_NE, RBoxError>,
313}
314
315// This is how you can construct `Module` in a way that allows it to become generic later.
316impl Module {
317    // This macro declares a `StaticRef<WithMetadata<BoxVtable<T>>>` constant.
318    //
319    // StaticRef represents a reference to data that lives forever,
320    // but is not necessarily `'static` according to the type system.
321    //
322    // StaticRef not necessary in this case, it's more useful with generic types..
323    abi_stable::staticref!(const TMP0: WithMetadata<Self> = WithMetadata::new(
324        Self{
325            deserialize_tag,
326        },
327    ));
328
329    const VALUE: Module_Ref = Module_Ref(Self::TMP0.as_prefix());
330}
331
332/////////////////////////////////////////////////////////////////////////////////////////
333////   In implementation crate (the one that gets compiled as a dynamic library)    /////
334/////////////////////////////////////////////////////////////////////////////////////////
335
336#[sabi_extern_fn]
337pub fn deserialize_tag(s: RStr<'_>) -> RResult<ValidTag_NE, RBoxError> {
338    match serde_json::from_str::<ValidTag>(s.into()) {
339        Ok(x) => ROk(NonExhaustive::new(x)),
340        Err(e) => RErr(RBoxError::new(e)),
341    }
342}
343
344```
345
346### Boxing variants of unknown size
347
348This example demonstrates how one can use boxing to store types larger than `[usize;2]`
349(the size of `RBox<_>`),
350because one of the variant contains a generic type.
351
352
353
354```
355use abi_stable::{
356    nonexhaustive_enum::{NonExhaustive, NonExhaustiveFor},
357    sabi_trait,
358    std_types::{RBox, RString},
359    StableAbi,
360};
361
362use std::{
363    cmp::PartialEq,
364    fmt::{self, Debug, Display},
365};
366
367#[repr(u8)]
368#[derive(StableAbi, Debug, Clone, PartialEq)]
369#[sabi(kind(WithNonExhaustive(
370    size = [usize;3],
371    traits(Debug, Display, Clone, PartialEq),
372)))]
373#[non_exhaustive]
374pub enum Message<T> {
375    SaysHello,
376    SaysGoodbye,
377
378    #[sabi(with_boxed_constructor)]
379    Custom(RBox<T>),
380
381    ////////////////////////////////////////
382    // Available since 1.1
383    ////////////////////////////////////////
384    #[sabi(with_boxed_constructor)]
385    SaysThankYou(RBox<SaysThankYou>),
386}
387
388impl<T> Display for Message<T>
389where
390    T: Display,
391{
392    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393        match self {
394            Message::SaysHello => write!(f, "Hello!"),
395            Message::SaysGoodbye => write!(f, "Goodbye!"),
396            Message::Custom(custom) => Display::fmt(&**custom, f),
397            Message::SaysThankYou(x) => writeln!(f, "Thank you,{}!", x.to),
398            _ => unreachable!(),
399        }
400    }
401}
402
403// Only available since 1.1
404#[repr(C)]
405#[derive(StableAbi, Debug, Clone, PartialEq)]
406pub struct SaysThankYou {
407    to: RString,
408}
409
410# fn main(){
411
412// Constructing Message::Custom wrapped in a NonExhaustive
413{
414    let custom_message: Message_NE<RString> = Message::Custom_NE("Hello".into());
415
416    let custom_message_desugar: Message_NE<RString> = {
417        let x = RBox::new("Hello".into());
418        let x = Message::Custom(x);
419        NonExhaustive::new(x)
420    };
421
422    assert_eq!(custom_message, custom_message_desugar);
423}
424
425// Constructing Message::SaysThankYou wrapped in a NonExhaustive
426// This variant is only available since 1.1
427{
428    let says_thank_you: Message_NE<RString> =
429        Message::SaysThankYou_NE(SaysThankYou { to: "Hello".into() });
430
431    let says_thank_you_desugar: Message_NE<RString> = {
432        let x = SaysThankYou { to: "Hello".into() };
433        let x = Message::SaysThankYou(RBox::new(x));
434        NonExhaustive::new(x)
435    };
436
437    assert_eq!(says_thank_you, says_thank_you_desugar);
438}
439
440# }
441
442
443```
444
445
446
447### Generic enum with `RSmallBox`
448
449This example shows how one can use RSmallBox to define a generic nonexhausitve enum.
450
451```
452
453use abi_stable::{
454    reexports::SelfOps,
455    sabi_types::RSmallBox,
456    std_types::{RString, RVec},
457    StableAbi,
458};
459
460#[repr(u8)]
461#[derive(StableAbi, Debug, Clone, PartialEq)]
462#[sabi(kind(WithNonExhaustive(
463    // Determines the maximum size of this enum in semver compatible versions.
464    // This is 11 usize large because:
465    //    - The enum discriminant occupies 1 usize(because the enum is usize aligned).
466    //    - RSmallBox<T,[usize;8]>: is 10 usize large
467    size = [usize;11],
468    // Determines the traits that are required when wrapping this enum in NonExhaustive,
469    // and are then available with it.
470    traits(Debug,Clone,PartialEq),
471)))]
472#[sabi(with_constructor)]
473#[non_exhaustive]
474pub enum SomeEnum<T> {
475    Foo,
476    Bar,
477    Crash {
478        reason: RString,
479        animal: RString,
480    },
481    // This variant was added in a newer (compatible) version of the library.
482    #[sabi(with_boxed_constructor)]
483    Other(RSmallBox<T, [usize; 8]>),
484}
485
486impl<T> SomeEnum<T> {
487    pub fn is_inline(&self) -> bool {
488        match self {
489            SomeEnum::Foo => true,
490            SomeEnum::Bar => true,
491            SomeEnum::Crash { .. } => true,
492            SomeEnum::Other(rsbox) => RSmallBox::is_inline(rsbox),
493            _ => true,
494        }
495    }
496
497    pub fn is_heap_allocated(&self) -> bool {
498        !self.is_inline()
499    }
500}
501
502#[repr(C)]
503#[derive(StableAbi, Debug, Clone, PartialEq)]
504pub struct FullName {
505    pub name: RString,
506    pub surname: RString,
507}
508
509/// A way to represent a frozen `Vec<Vec<T>>`.
510///
511/// This example just constructs NestedVec directly,
512/// realistically it would be constructed in an associated function of NestedVec.
513#[repr(C)]
514#[derive(StableAbi, Debug, Clone, PartialEq)]
515pub struct NestedVec<T> {
516    indices: RVec<usize>,
517    nested: RVec<T>,
518    dummy_field: u32,
519}
520
521# fn main(){
522
523let crash = SomeEnum::<()>::Crash_NE("No reason".into(), "Bandi____".into());
524
525let other_fullname = SomeEnum::Other_NE(FullName {
526    name: "R__e".into(),
527    surname: "L_____e".into(),
528});
529
530let other_nestedlist = {
531    let nestedlist = NestedVec {
532        indices: vec![0, 2, 3, 5].into(),
533        // Each line here is a nested list.
534        nested: vec![false, false, true, true, false, true, true, true].into(),
535        dummy_field: 0,
536    };
537    SomeEnum::Other_NE(nestedlist)
538};
539
540assert!(crash.as_enum().unwrap().is_inline());
541assert!(other_fullname.as_enum().unwrap().is_inline());
542assert!(other_nestedlist.as_enum().unwrap().is_heap_allocated());
543
544# }
545
546
547
548```
549
550### Add variant to "private" enum across versions
551
552Say that we want to define a "private" enum
553(it's exposed to the ABI but it's not public API),
554used internally to send information between instances of the same library,
555of potentially different (compatible) versions.
556
557If one of the variants from newer versions are sent into a library/binary
558that has a previous version of `Event`,
559`Event_NE` (an alias for NonExhaustive wrapping an Event)
560won't be convertible back into `Event`.
561
562```
563use abi_stable::{
564    nonexhaustive_enum::{NonExhaustive, NonExhaustiveFor},
565    sabi_trait,
566    std_types::{RArc, RString},
567    StableAbi,
568};
569
570#[doc(hidden)]
571#[repr(C)]
572#[derive(StableAbi, Debug, Clone, Copy, PartialEq)]
573pub struct ObjectId(pub usize);
574
575#[doc(hidden)]
576#[repr(C)]
577#[derive(StableAbi, Debug, Clone, Copy, PartialEq)]
578pub struct GroupId(pub usize);
579
580#[repr(u8)]
581#[derive(StableAbi, Debug, Clone, PartialEq)]
582#[sabi(kind(WithNonExhaustive(
583    size = [usize;8],
584    traits(Debug, Clone, PartialEq),
585)))]
586#[sabi(with_constructor)]
587#[non_exhaustive]
588pub enum Event {
589    CreatedInstance {
590        object_id: ObjectId,
591    },
592    RemovedInstance {
593        object_id: ObjectId,
594    },
595
596    /////////////////
597    // Added in 1.1
598    /////////////////
599    CreatedGroup {
600        name: RString,
601        group_id: GroupId,
602    },
603    RemovedGroup {
604        name: RString,
605        group_id: GroupId,
606    },
607    AssociatedWithGroup {
608        object_id: ObjectId,
609        group_id: GroupId,
610    },
611
612    /////////////////
613    // Added in 1.2
614    /////////////////
615    RemovedAssociationWithGroup {
616        object_id: ObjectId,
617        group_id: GroupId,
618    },
619    #[sabi(with_boxed_constructor)]
620    DummyVariant {
621        pointer: RArc<()>,
622    },
623}
624
625let objectid_0 = ObjectId(0);
626let objectid_1 = ObjectId(1);
627
628let groupid_0 = GroupId(0);
629let groupid_1 = GroupId(0);
630
631// Constructing a Event::CreatedInstance wrapped in a NonExhaustive
632{
633    let from_ne_constructor: Event_NE = Event::CreatedInstance_NE(objectid_0);
634    let regular = {
635        let ev = Event::CreatedInstance {
636            object_id: objectid_0,
637        };
638        NonExhaustive::new(ev)
639    };
640
641    assert_eq!(from_ne_constructor, regular);
642}
643
644// Constructing a Event::RemovedInstance wrapped in a NonExhaustive
645{
646    let from_ne_constructor = Event::RemovedInstance_NE(objectid_0);
647    let regular = {
648        let ev = Event::RemovedInstance {
649            object_id: objectid_0,
650        };
651        NonExhaustive::new(ev)
652    };
653
654    assert_eq!(from_ne_constructor, regular);
655}
656
657// Constructing a Event::RemovedInstance wrapped in a NonExhaustive
658{
659    let from_ne_constructor = Event::RemovedInstance_NE(objectid_0);
660    let regular = {
661        let ev = Event::RemovedInstance {
662            object_id: objectid_0,
663        };
664        NonExhaustive::new(ev)
665    };
666
667    assert_eq!(from_ne_constructor, regular);
668}
669
670// Constructing a Event::CreatedGroup wrapped in a NonExhaustive
671// This is only available from 1.1
672{
673    let from_ne_constructor = Event::CreatedGroup_NE("hello".into(), groupid_0);
674    let regular = {
675        let ev = Event::CreatedGroup {
676            name: "hello".into(),
677            group_id: groupid_0,
678        };
679        NonExhaustive::new(ev)
680    };
681
682    assert_eq!(from_ne_constructor, regular);
683}
684
685// Constructing a Event::RemovedGroup wrapped in a NonExhaustive
686// This is only available from 1.1
687{
688    let from_ne_constructor = Event::RemovedGroup_NE("hello".into(), groupid_0);
689    let regular = {
690        let ev = Event::RemovedGroup {
691            name: "hello".into(),
692            group_id: groupid_0,
693        };
694        NonExhaustive::new(ev)
695    };
696
697    assert_eq!(from_ne_constructor, regular);
698}
699
700// Constructing a Event::AssociatedWithGroup wrapped in a NonExhaustive
701// This is only available from 1.1
702{
703    let from_ne_constructor = Event::AssociatedWithGroup_NE(objectid_0, groupid_0);
704    let regular = {
705        let ev = Event::AssociatedWithGroup {
706            object_id: objectid_0,
707            group_id: groupid_0,
708        };
709        NonExhaustive::new(ev)
710    };
711
712    assert_eq!(from_ne_constructor, regular);
713}
714
715// Constructing a Event::RemovedAssociationWithGroup wrapped in a NonExhaustive
716// This is only available from 1.2
717{
718    let from_ne_constructor =
719        Event::RemovedAssociationWithGroup_NE(objectid_0, groupid_0);
720    let regular = {
721        let ev = Event::RemovedAssociationWithGroup {
722            object_id: objectid_0,
723            group_id: groupid_0,
724        };
725        NonExhaustive::new(ev)
726    };
727
728    assert_eq!(from_ne_constructor, regular);
729}
730
731// Constructing a Event::DummyVariant wrapped in a NonExhaustive
732// This is only available from 1.2
733{
734    let from_ne_constructor = Event::DummyVariant_NE(());
735    let regular = {
736        let x = RArc::new(());
737        let x = Event::DummyVariant { pointer: x };
738        NonExhaustive::new(x)
739    };
740
741    assert_eq!(from_ne_constructor, regular);
742}
743
744```
745
746
747<span id = "using_assert_nonexhaustive_example"></span>
748### Using `assert_nonexhaustive`
749
750This example demonstrates the `assert_nonexhaustive` helper attribute,
751and the errors produced when the enum is too large or is misaligned for its default storage.
752
753```compile_fail
754use abi_stable::StableAbi;
755
756#[repr(u8)]
757#[derive(StableAbi)]
758#[sabi(kind(WithNonExhaustive(
759    // Determines the maximum size of this enum in semver compatible versions.
760    // maximum size is `size_of::<[u16; 3]>()`
761    size = [u16; 3],
762    // Determines the maximum alignment of this enum in semver compatible versions.
763    // aligned at most `align_of::<u16>()`
764    align = u16,
765    // The below attribute is implied for non-generic enums,
766    // it generates a static assertion checking that `Concrete`
767    // fits within its default storage.
768    // assert_nonexhaustive(Concrete)
769)))]
770#[non_exhaustive]
771pub enum Concrete {
772    Foo,
773    Bar,
774    Tag([u16; 3]),
775}
776
777
778#[repr(u8)]
779#[derive(StableAbi)]
780#[sabi(kind(WithNonExhaustive(
781    // Determines the maximum size of this enum in semver compatible versions.
782    size = 8,
783    // Determines the maximum alignment of this enum in semver compatible versions.
784    // non-literal constants have to be wrapped in braces
785    align = {alignment()},
786    // generic enums don't implicitly assert that the enum is compatible with the
787    // default storage, you must specify the tested concrete types
788    assert_nonexhaustive(Generic<[u16; 4]>, Generic<u32>, Generic<u64>)
789)))]
790#[non_exhaustive]
791pub enum Generic<T> {
792    Foo,
793    Bar,
794    Qux(T),
795}
796
797const fn alignment() -> usize {
798    2
799}
800```
801
802This is the compile-time error for the above code:
803```text
804error[E0080]: evaluation of constant value failed
805 --> src/docs/sabi_nonexhaustive.rs:767:10
806  |
8077 | #[derive(StableAbi)]
808  |          ^^^^^^^^^ the evaluated program panicked at '
809The size of the storage is smaller than the contained type:
810    enum_: "Concrete"
811    enum_size: 8
812    enum_alignment: 2
813    storage_: "Concrete_Storage"
814    storage_size: 6
815    storage_alignment: 2
816', src/docs/sabi_nonexhaustive.rs:7:10
817  |
818  = note: this error originates in the derive macro `StableAbi` (in Nightly builds, run with -Z macro-backtrace for more info)
819
820error[E0080]: evaluation of constant value failed
821  --> src/docs/sabi_nonexhaustive.rs:789:10
822   |
82329 | #[derive(StableAbi)]
824   |          ^^^^^^^^^ the evaluated program panicked at '
825The size of the storage is smaller than the contained type:
826    enum_: "Generic < [u16 ; 4] >"
827    enum_size: 10
828    enum_alignment: 2
829    storage_: "Generic_Storage"
830    storage_size: 8
831    storage_alignment: 2
832', src/docs/sabi_nonexhaustive.rs:29:10
833   |
834   = note: this error originates in the derive macro `StableAbi` (in Nightly builds, run with -Z macro-backtrace for more info)
835
836error[E0080]: evaluation of constant value failed
837  --> src/docs/sabi_nonexhaustive.rs:789:10
838   |
83929 | #[derive(StableAbi)]
840   |          ^^^^^^^^^ the evaluated program panicked at '
841The alignment of the storage is lower than the contained type:
842    enum_: "Generic < u32 >"
843    enum_size: 8
844    enum_alignment: 4
845    storage_: "Generic_Storage"
846    storage_size: 8
847    storage_alignment: 2
848', src/docs/sabi_nonexhaustive.rs:29:10
849   |
850   = note: this error originates in the derive macro `StableAbi` (in Nightly builds, run with -Z macro-backtrace for more info)
851
852error[E0080]: evaluation of constant value failed
853  --> src/docs/sabi_nonexhaustive.rs:789:10
854   |
85529 | #[derive(StableAbi)]
856   |          ^^^^^^^^^ the evaluated program panicked at '
857The alignment and size of the storage is smaller than the contained type:
858    enum_: "Generic < u64 >"
859    enum_size: 16
860    enum_alignment: 8
861    storage_: "Generic_Storage"
862    storage_size: 8
863    storage_alignment: 2
864', src/docs/sabi_nonexhaustive.rs:29:10
865   |
866   = note: this error originates in the derive macro `StableAbi` (in Nightly builds, run with -Z macro-backtrace for more info)
867
868error: aborting due to 4 previous errors
869```
870
871
872[`InterfaceType`]: crate::InterfaceType
873[`SerializeEnum`]: crate::nonexhaustive_enum::SerializeEnum
874[`DeserializeEnum`]: crate::nonexhaustive_enum::DeserializeEnum
875
876*/