[−][src]Crate auto_enums
A library for to allow multiple return types by automatically generated enum.
This library provides the following attribute macros:
-
#[auto_enum]
Parses syntax, creates the enum, inserts variants, and passes specified traits to
#[enum_derive]
. -
#[enum_derive]
Implements specified traits to the enum.
#[auto_enum]
#[auto_enum]
's basic feature is to wrap the value returned by the obvious branches (match
, if
, return
, etc..) by an enum that implemented the specified traits.
use auto_enums::auto_enum; #[auto_enum(Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => 1..10, _ => vec![5, 10].into_iter(), } }
#[auto_enum]
generates code in two stages.
First, #[auto_enum]
will do the following.
- parses syntax
- creates the enum
- inserts variants
Code like this will be generated:
fn foo(x: i32) -> impl Iterator<Item = i32> { #[::auto_enums::enum_derive(Iterator)] enum __Enum1<__T1, __T2> { __T1(__T1), __T2(__T2), } match x { 0 => __Enum1::__T1(1..10), _ => __Enum1::__T2(vec![5, 10].into_iter()), } }
Next, #[enum_derive]
implements the specified traits.
Code like this will be generated:
fn foo(x: i32) -> impl Iterator<Item = i32> { enum __Enum1<__T1, __T2> { __T1(__T1), __T2(__T2), } impl<__T1, __T2> ::core::iter::Iterator for __Enum1<__T1, __T2> where __T1: ::core::iter::Iterator, __T2: ::core::iter::Iterator<Item = <__T1 as ::core::iter::Iterator>::Item>, { type Item = <__T1 as ::core::iter::Iterator>::Item; #[inline] fn next(&mut self) -> ::core::option::Option<Self::Item> { match self { __Enum1::__T1(x) => x.next(), __Enum1::__T2(x) => x.next(), } } #[inline] fn size_hint(&self) -> (usize, ::core::option::Option<usize>) { match self { __Enum1::__T1(x) => x.size_hint(), __Enum1::__T2(x) => x.size_hint(), } } } match x { 0 => __Enum1::__T1(1..10), _ => __Enum1::__T2(vec![5, 10].into_iter()), } }
Positions where #[auto_enum]
can be used.
#[auto_enum]
can be used in the following three places. However, since stmt_expr_attributes and proc_macro_hygiene are not stabilized, you need to use empty #[auto_enum]
for functions except nightly.
-
functions
#[auto_enum(Iterator)] fn func(x: i32) -> impl Iterator<Item=i32> { if x == 0 { Some(0).into_iter() } else { 0..x } }
-
expressions
#[auto_enum] // Nightly does not need an empty attribute to the function. fn expr(x: i32) -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] match x { 0 => Some(0).into_iter(), _ => 0..x, } }
-
let binding
#[auto_enum] // Nightly does not need an empty attribute to the function. fn let_binding(x: i32) -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] let iter = match x { 0 => Some(0).into_iter(), _ => 0..x, }; iter }
Supported syntax
-
if
andmatch
Wrap each branch with a variant.
// if #[auto_enum(Iterator)] fn expr_if(x: i32) -> impl Iterator<Item=i32> { if x == 0 { Some(0).into_iter() } else { 0..x } } // match #[auto_enum] // Nightly does not need an empty attribute to the function. fn expr_match(x: i32) -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] let iter = match x { 0 => Some(0).into_iter(), _ => 0..x, }; iter }
-
loop
Wrap each
break
with a variant. Nested loops and labeledbreak
are also supported.#[auto_enum(Iterator)] fn expr_loop(mut x: i32) -> impl Iterator<Item = i32> { loop { if x < 0 { break x..0; } else if x % 5 == 0 { break 0..=x; } x -= 1; } }
-
return
(in functions)#[auto_enum]
can parse thereturn
in the scope.This analysis is valid only when the return type is
impl Trait
.// return (in functions) #[auto_enum(Iterator)] fn func(x: i32) -> impl Iterator<Item=i32> { if x == 0 { return Some(0).into_iter(); } if x > 0 { 0..x } else { x..=0 } }
-
return
(in closures)#[auto_enum]
can parse thereturn
in the scope.This analysis is valid only when the following two conditions are satisfied.
#[auto_enum]
must be used directly for that closure (or the let binding of the closure).?
operator not used in the scope.
// return (in closures) #[auto_enum] // Nightly does not need an empty attribute to the function. fn closure() -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] let f = |x| { if x == 0 { return Some(0).into_iter(); } if x > 0 { 0..x } else { x..=0 } }; f(1) }
-
?
operator (in functions)#[auto_enum]
can parse the?
operator in the scope.This analysis is valid only when the return type is
Result<T, impl Trait>
.use std::fmt::{Debug, Display}; // `?` operator (in functions) #[auto_enum(Debug, Display)] fn func(x: i32) -> Result<i32, impl Debug + Display> { if x == 0 { Err("`x` is zero")?; } // The last branch of the function is not parsed. if x < 0 { Err(x)? } else { Ok(x + 1) } }
By default,
?
operator is expanded as follows:match expr { Ok(val) => val, Err(err) => return Err(Enum::Veriant(err)), }
When "try_trait" crate feature is enabled,
?
operator is expanded as follows (note that this uses an unstable feature):match Try::into_result(expr) { Ok(val) => val, Err(err) => return Try::from_error(Enum::Veriant(err)), }
-
?
operator (in closures)#[auto_enum]
can parse the?
operator in the scope.However,
#[auto_enum]
must be used directly for that closure (or the let binding of the closure).use std::fmt::{Debug, Display}; // `?` operator (in closures) #[auto_enum] // Nightly does not need an empty attribute to the function. fn closure() -> Result<i32, impl Debug + Display> { #[auto_enum(Debug, Display)] let f = |x| { if x == 0 { Err("`x` is zero")? } // The last branch of the function is not parsed. if x < 0 { Err(x)? } else { Ok(x + 1) } }; f(1) }
-
Block, unsafe block, method call, parentheses, and type ascription
The following expressions are recursively searched until an
if
,match
,loop
or unsupported expression is found.- blocks
- unsafe blocks
- method calls
- parentheses
- type ascriptions
// block #[auto_enum] // Nightly does not need an empty attribute to the function. fn expr_block(x: i32) -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] { if x == 0 { Some(0).into_iter() } else { 0..x } } } // method call #[auto_enum] // Nightly does not need an empty attribute to the function. fn expr_method(x: i32) -> impl Iterator<Item=i32> { #[auto_enum(Iterator)] match x { 0 => Some(0).into_iter(), _ => 0..x, }.map(|y| y + 1) } // parentheses #[auto_enum(Iterator)] fn expr_parentheses(x: i32) -> impl Iterator<Item=i32> { (if x == 0 { Some(0).into_iter() } else { 0..x }) }
Parse nested branches
You can parse nested branches by #[rec]
attribute.
#[auto_enum(Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => 1..10, #[rec] _ => match x { 1 => vec![5, 10].into_iter(), _ => 0..=x, }, } }
Expression that no value will be returned
If the last expression of a branch is one of the following, it is interpreted that no value will be returned (variant assignment is skipped).
panic!(..)
unreachable!(..)
return
break
continue
None?
Err(..)?
- Expression level marker (
marker!
macro). - An item definition.
#[auto_enum(Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => 1..10, 1 => panic!(), // variant assignment is skipped _ => vec![5, 10].into_iter(), } }
You can also skip that branch explicitly by #[never]
attribute.
#[auto_enum(Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => 1..10, #[never] 1 => loop { panic!() }, _ => vec![5, 10].into_iter(), } }
You can also skip all branches by never
option.
#[auto_enum(never, Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => loop { return marker!(1..10); }, 1 => loop { panic!() }, _ => loop { return marker!(vec![5, 10].into_iter()); }, } }
Expression level marker (marker!
macro)
#[auto_enum]
replaces marker!
macros with variants.
If values of two or more are specified by marker!
macros, #[auto_enum]
can be used for unsupported expressions and statements.
#[auto_enum(Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { if x < 0 { return x..=0; } marker!(1..10) }
The default name of the macro is "marker"
, but you can change it by marker
option.
#[auto_enum(marker(bar), Iterator)] fn foo(x: i32) -> impl Iterator<Item = i32> { if x < 0 { return x..=0; } bar!(1..10) }
Rust Nightly
When using #[auto_enum]
for expressions and statements, #[auto_enum]
for function is unnecessary.
// Add this to your crate root: #![feature(proc_macro_hygiene, stmt_expr_attributes)]
fn foo(x: i32) -> i32 { #[auto_enum(Iterator)] let iter = match x { 0 => 1..10, _ => vec![5, 10].into_iter(), }; iter.fold(0, |sum, x| sum + x) }
You can also return closures.
// Add this to your crate root: #![feature(fn_traits, unboxed_closures)]
#[auto_enum(Fn)] fn foo(x: bool) -> impl Fn(i32) -> i32 { if x { |y| y + 1 } else { |z| z - 1 } }
#[enum_derive]
#[enum_derive]
implements the supported traits and passes unsupported traits to #[derive]
.
If you want to use traits that are not supported by #[enum_derive]
, you can use another crate that provides proc_macro_derive
, or you can define proc_macro_derive
yourself(derive_utils probably can help it).
Basic usage of #[enum_derive]
use auto_enums::enum_derive; // `#[enum_derive]` implements `Iterator`, and `#[derive]` implements `Clone`. #[enum_derive(Iterator, Clone)] enum Foo<A, B> { A(A), B(B), }
#[enum_derive]
adds the dependency of the specified trait if it is not specified.
use auto_enums::enum_derive; // `#[enum_derive]` implements `Iterator` and `ExactSizeIterator`. #[enum_derive(ExactSizeIterator)] enum Foo<A, B> { A(A), B(B), }
Supported traits
[std|core] libraries
Note that some traits have aliases.
[std|core]::ops
Deref
DerefMut
Index
IndexMut
RangeBounds
Fn
(nightly-only)FnMut
(nightly-only)FnOnce
(nightly-only)Generator
(nightly-only)
[std|core]::convert
[std|core]::iter
Iterator
DoubleEndedIterator
ExactSizeIterator
FusedIterator
TrustedLen
(nightly-only)Extend
[std|core]::fmt
Debug
(alias:fmt::Debug
) - generated codeDisplay
(alias:fmt::Display
)fmt::Binary
(requires"fmt"
crate feature)fmt::LowerExp
(requires"fmt"
crate feature)fmt::LowerHex
(requires"fmt"
crate feature)fmt::Octal
(requires"fmt"
crate feature)fmt::Pointer
(requires"fmt"
crate feature)fmt::UpperExp
(requires"fmt"
crate feature)fmt::UpperHex
(requires"fmt"
crate feature)fmt::Write
[std|core]::future
Future
- nightly-only - generated code
std::io
(requires "std"
crate feature)
Read
(alias:io::Read
) - generated codeBufRead
(alias:io::BufRead
) - generated codeWrite
(alias:io::Write
) - generated codeSeek
(alias:io::Seek
) - generated code
std::error
(requires "std"
crate feature)
External libraries
You can add support for external library by activating the each crate feature.
futures(v0.3)
(requires "futures"
crate feature)
futures::Stream
- generated codefutures::Sink
- generated codefutures::AsyncRead
- generated codefutures::AsyncWrite
- generated code
futures(v0.1)
(requires "futures01"
crate feature)
quote
(requires "proc_macro"
crate feature)
rayon
(requires "rayon"
crate feature)
serde
(requires "serde"
crate feature)
Static methods
These don't derive traits, but derive static methods instead.
-
Transpose
(requires"transpose_methods"
crate feature) - this derives the following conversion methods.-
transpose
- convert fromenum<Option<T1>,..>
toOption<enum<T1,..>>
-
transpose
- convert fromenum<Result<T1, E1>,..>
toResult<enum<T1,..>, enum<E1,..>>
-
transpose_ok
- convert fromenum<Result<T1, E>,..>
toOption<enum<T1,..>, E>
Examples:
use std::{fs, io, path::Path}; #[auto_enum(Transpose, Write)] fn output_stream(file: Option<&Path>) -> io::Result<impl io::Write> { match file { Some(f) => fs::File::create(f), None => Ok(io::stdout()), }.transpose_ok() }
-
transpose_err
- convert fromenum<Result<T, E1>,..>
toResult<T, enum<E1,..>>
-
Crate Features
-
std
- Enabled by default.
- Enable to use
std
library's traits.
-
fmt
- Disabled by default.
- Enable to use
[std|core]::fmt
's traits other thanDebug
,Display
andWrite
.
-
type_analysis
-
Disabled by default.
-
Analyze return type of function and
let
binding.Note that this feature is still experimental.
Examples:
#[auto_enum] // there is no need to specify std library's traits fn foo(x: i32) -> impl Iterator<Item = i32> { match x { 0 => 1..10, _ => vec![5, 10].into_iter(), } }
Please be careful if you return another traits with the same name.
-
-
transpose_methods
- Disabled by default.
- Enable to use
transpose*
methods.
-
try_trait
- Disabled by default.
- Make
?
operator support more flexible, and to make iterator implementation more effective. - This requires Rust Nightly and you need to enable the unstable
try_trait
feature gate.
Using external libraries (disabled by default)
-
futures
- futures(v0.3) -
futures01
- futures(v0.1) -
proc_macro
- quote -
rayon
- rayon -
serde
- serde
Enable unstable features of [std|core] libraries (disabled by default, nightly-only)
For these features, you need to enable the unstable feature gate of the same name.
Note that support for these features are unstable and may cause incompatible changes between patch versions.
-
exact_size_is_empty
- ImplementsExactSizeIterator::is_empty
. -
iovec
- Implementsio::Read::read_vectored
andio::Write::write_vectored
. -
read_initializer
- Implementsio::Read::read_initializer
. -
try_trait
- Make iterator implementation more effective.
Known limitations
-
There needs to explicitly specify the trait to be implemented (
type_analysis
crate feature reduces this limitation). -
There needs to be marker macros for unsupported expressions.