Skip to content

Commit 4c37c57

Browse files
committed
Add #[try_from(type[, err, err-constructor])]
1 parent 604dba8 commit 4c37c57

15 files changed

+791
-75
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2525
with an associated item called `Error` or `Err` respectively.
2626
([#410](https://github.com/JelteF/derive_more/pull/410))
2727

28+
### Added
29+
30+
- Add support for custom types in `TryFrom`. So now you can use:
31+
`#[try_from(T)]`, `#[try_from(T, E)]` and `#[try_from(T, E, ErrorValue)]`
32+
alongside and not only `#[try_from(repr)]`.
2833

2934
## 1.0.0 - 2024-08-07
3035

impl/doc/try_from.md

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@ Derive `TryFrom` allows you to convert enum discriminants into their correspondi
77

88
## Enums
99

10-
By default, a `TryFrom<isize>` is generated, matching the [type of the discriminant](https://doc.rust-lang.org/reference/items/enumerations.html#discriminants).
11-
The type can be changed with a `#[repr(u/i*)]` attribute, e.g., `#[repr(u8)]` or `#[repr(i32)]`.
12-
Only field-less variants can be constructed from their variant, therefore the `TryFrom` implementation will return an error for a discriminant representing a variant with fields.
10+
Enums can be generated either from a `repr` discriminant value or a custom type.
11+
12+
### Repr
13+
14+
In the `repr` mode, by default, a `TryFrom<isize>` is generated, matching the
15+
[type of the
16+
discriminant](https://doc.rust-lang.org/reference/items/enumerations.html#discriminants).
17+
The type can be changed with a `#[repr(u/i*)]` attribute, e.g., `#[repr(u8)]` or
18+
`#[repr(i32)]`. Only field-less variants can be constructed from their variant,
19+
therefore the `TryFrom` implementation will return an error for a discriminant
20+
representing a variant with fields.
1321

1422
```rust
1523
# use derive_more::TryFrom;
@@ -31,3 +39,71 @@ assert_eq!(Enum::EmptySeven{}, Enum::try_from(7).unwrap());
3139
// Variants with fields are not supported, as the value for their fields would be undefined.
3240
assert!(Enum::try_from(6).is_err());
3341
```
42+
43+
### Custom Types ("non-repr")
44+
45+
Rather situationally, `TryFrom<T>` can be implemented if all the variant types
46+
have `TryFrom<T>`.
47+
48+
```rust
49+
/// A custom error can be defined or not.
50+
#[derive(Debug, PartialEq, Eq)]
51+
enum Error {
52+
FromEnum,
53+
FromVariant,
54+
}
55+
56+
#[derive(Debug, PartialEq, Eq)]
57+
struct F1;
58+
59+
impl TryFrom<usize> for F1 {
60+
type Error = Error;
61+
62+
fn try_from(value: usize) -> Result<Self, Self::Error> {
63+
if value == 1 {
64+
return Ok(Self);
65+
}
66+
Err(Error::FromVariant)
67+
}
68+
}
69+
70+
#[derive(Debug, PartialEq, Eq)]
71+
struct F2;
72+
73+
impl TryFrom<usize> for F2 {
74+
type Error = Error;
75+
76+
fn try_from(value: usize) -> Result<Self, Self::Error> {
77+
if value == 2 {
78+
return Ok(Self);
79+
}
80+
Err(Error::FromVariant)
81+
}
82+
}
83+
84+
assert_eq!(Err(Error::FromVariant), F2::try_from(3));
85+
86+
#[derive(TryFrom, Debug, PartialEq, Eq)]
87+
#[try_from(
88+
// the type for which all variants have `TryFrom<T>`
89+
usize,
90+
// optional: the error type (default is `()`).
91+
Error,
92+
// optional: the constructor of the type (optional if err is as `struct E;`)
93+
Error::FromEnum
94+
)]
95+
enum Enum {
96+
Field(F1),
97+
Field2 { x: F2 },
98+
}
99+
100+
assert_eq!(Enum::Field(F1), Enum::try_from(1).unwrap());
101+
assert_eq!(Enum::Field2 { x: F2 }, Enum::try_from(2).unwrap());
102+
assert_eq!(Err(Error::FromEnum), Enum::try_from(3));
103+
```
104+
105+
Multi-field variants as in `Enum::Field(F1, F2)` are also supported however may
106+
rarely be used.
107+
108+
Since `TryFrom<T> for ()` is too universal, non-repr conversions do not support
109+
enums with empty (unit or fieldless) variants.

0 commit comments

Comments
 (0)