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

Document custom error types in README #125

Merged
merged 1 commit into from
Aug 13, 2023
Merged
Changes from all commits
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
139 changes: 78 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
num_enum
========
# num_enum

Procedural macros to make inter-operation between primitives and enums easier.
This crate is no_std compatible.
Expand All @@ -8,8 +7,7 @@ This crate is no_std compatible.
[![Documentation](https://docs.rs/num_enum/badge.svg)](https://docs.rs/num_enum)
[![Build Status](https://travis-ci.org/illicitonion/num_enum.svg?branch=master)](https://travis-ci.org/illicitonion/num_enum)

Turning an enum into a primitive
--------------------------------
## Turning an enum into a primitive

```rust
use num_enum::IntoPrimitive;
Expand All @@ -29,8 +27,7 @@ fn main() {

`num_enum`'s `IntoPrimitive` is more type-safe than using `as`, because `as` will silently truncate - `num_enum` only derives `From` for exactly the discriminant type of the enum.

Attempting to turn a primitive into an enum with try_from
----------------------------------------------
## Attempting to turn a primitive into an enum with try_from

```rust
use num_enum::TryFromPrimitive;
Expand All @@ -55,8 +52,7 @@ fn main() {
}
```

Variant alternatives
---------------
### Variant alternatives

Sometimes a single enum variant might be representable by multiple numeric values.

Expand Down Expand Up @@ -122,43 +118,34 @@ fn main() {
}
```

Default variant
---------------
### Custom error types

Sometimes it is desirable to have an `Other` variant in an enum that acts as a kind of a wildcard matching all the value not yet covered by other variants.

The `#[num_enum(default)]` attribute (or the stdlib `#[default]` attribute) allows you to mark variant as the default.
`TryFromPrimitive` by default will use `num_enum::TryFromPrimitiveError` as its `Error` type.

(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.)
If you want to use a different type, you can use an annotation for this:

```rust
use num_enum::FromPrimitive;
use std::convert::TryFrom;
use num_enum::TryFromPrimitive;

#[derive(Debug, Eq, PartialEq, FromPrimitive)]
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(default)]
NonZero = 1,
enum FirstNumber {
Zero,
One,
Two,
}

fn main() {
let zero = Number::from(0u8);
assert_eq!(zero, Number::Zero);

let one = Number::from(1u8);
assert_eq!(one, Number::NonZero);
struct CustomError {}

let two = Number::from(2u8);
assert_eq!(two, Number::NonZero);
impl CustomError {
fn new(value: u8) -> CustomError {
CustomError {}
}
}
```

Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them.

Safely turning a primitive into an exhaustive enum with from_primitive
-------------------------------------------------------------
## Safely turning a primitive into an exhaustive enum with from_primitive

If your enum has all possible primitive values covered, you can derive `FromPrimitive` for it (which auto-implement stdlib's `From`):

Expand Down Expand Up @@ -191,8 +178,41 @@ fn main() {
}
```

Catch-all variant
-----------------
### Default variant

Sometimes it is desirable to have an `Other` variant in an enum that acts as a kind of a wildcard matching all the value not yet covered by other variants.

The `#[num_enum(default)]` attribute (or the stdlib `#[default]` attribute) allows you to mark variant as the default.

(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.)

```rust
use num_enum::FromPrimitive;
use std::convert::TryFrom;

#[derive(Debug, Eq, PartialEq, FromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,
#[num_enum(default)]
NonZero = 1,
}

fn main() {
let zero = Number::from(0u8);
assert_eq!(zero, Number::Zero);

let one = Number::from(1u8);
assert_eq!(one, Number::NonZero);

let two = Number::from(2u8);
assert_eq!(two, Number::NonZero);
}
```

Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them.

### Catch-all variant

Sometimes it is desirable to have an `Other` variant which holds the otherwise un-matched value as a field.

Expand Down Expand Up @@ -224,34 +244,13 @@ fn main() {

As this is naturally exhaustive, this is only supported for `FromPrimitive`, not also `TryFromPrimitive`.

Unsafely turning a primitive into an enum with unchecked_transmute_from
-----------------------------------------------------------------------
## Unsafely turning a primitive into an enum with unchecked_transmute_from

If you're really certain a conversion will succeed (and have not made use of `#[num_enum(default)]` or `#[num_enum(alternatives = [..])]`
for any of its variants), and want to avoid a small amount of overhead, you can use unsafe code to do this conversion.
Unless you have data showing that the match statement generated in the `try_from` above is a bottleneck for you,
you should avoid doing this, as the unsafe code has potential to cause serious memory issues in your program.

Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.

- This means, for instance, that the following is UB:

```rust,no_run
use num_enum::UnsafeFromPrimitive;

#[derive(UnsafeFromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,

// Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]`
#[num_enum(default)]
One = 1,
}
let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) };
```

```rust
use num_enum::UnsafeFromPrimitive;

Expand All @@ -278,8 +277,27 @@ unsafe fn undefined_behavior() {
}
```

Optional features
-----------------
Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.

This means, for instance, that the following is undefined behaviour:

```rust,no_run
use num_enum::UnsafeFromPrimitive;

#[derive(UnsafeFromPrimitive)]
#[repr(u8)]
enum Number {
Zero = 0,

// Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]`
#[num_enum(default)]
One = 1,
}
let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) };
```

## Optional features

Some enum values may be composed of complex expressions, for example:

Expand All @@ -293,7 +311,6 @@ enum Number {
To cut down on compile time, these are not supported by default, but if you enable the `complex-expressions`
feature of your dependency on `num_enum`, these should start working.

License
-------
## License

num_enum may be used under your choice of the BSD 3-clause, Apache 2, or MIT license.
Loading