enum_dispatch-0.3.13/.cargo_vcs_info.json0000644000000001360000000000100137430ustar { "git": { "sha1": "c20b482de9bcfd320ff5d0ad6a69e8c379094d9f" }, "path_in_vcs": "" }enum_dispatch-0.3.13/.gitignore000064400000000000000000000000361046102023000145220ustar 00000000000000/target **/*.rs.bk Cargo.lock enum_dispatch-0.3.13/CHANGELOG.md000064400000000000000000000035031046102023000143450ustar 00000000000000# Changelog ## 0.3.13 - Fix namespace collision with imports named `core` (!35) ## 0.3.12 - Update to syn 2.0 (#69) ## 0.3.11 - Add support for trait methods that return `Self` (#59, !29) ## 0.3.10 - Add support for async trait methods (#62) ## 0.3.9 - Add support for const generics (#51, !25) ## 0.3.8 - Preserve attributes from inner fields of enum variants (!27) ## 0.3.7 - Support trait methods with late bound lifetime arguments (#34) ## 0.3.6 - Remove `extra-traits` feature from `syn` dependency (!24) - Support trait methods with pattern arguments (#44) ## 0.3.5 - Compatibility with `syn >= 1.0.58` (#37, !21) ## 0.3.4 - Support enum variants named `Error` (#36) ## 0.3.3 ### Compatibility warning Users who had previously used an `#[enum_dispatch(...)]` attribute containing the name of a generic trait will need to update the attribute to include matching generic arguments. See #35 for details. - Support trait methods with generic type parameters (#28) - Officially support linking generic traits (#26) ## 0.3.2 - Support `cfg` attributes on enum variants and trait items (#24) ## 0.3.1 - Support multiple comma separated traits or enums in `enum_dispatch` attributes (#3, !14) - Pass attributes from trait methods to the generated implementations (!15) ## 0.3.0 Rerelease of 0.2.3 to undo unintentional semver incompatibility (#16) ## 0.2.4 Rerelease of 0.2.2 to undo unintentional semver incompatibility (#16) ## 0.2.3 - Support identical method names across traits and base structs (!12) ## 0.2.2 - Support multiple trait implementations per enum (!13) ## 0.2.1 - Pass attributes from enum variants to the generated enums (#14) - Support enum variants with generic parameters (!4) ## 0.2.0 - Generate implementations of `TryInto` instead of `TryFrom`, which cannot be implemented on foreign types (#10) enum_dispatch-0.3.13/Cargo.toml0000644000000030040000000000100117360ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "enum_dispatch" version = "0.3.13" authors = ["Anton Lazarev "] exclude = ["benches"] description = "Near drop-in replacement for dynamic-dispatched method calls with up to 10x the speed" readme = "README.md" keywords = [ "speed", "performance", "traits", "dynamic", "optimization", ] categories = [ "rust-patterns", "development-tools::procedural-macro-helpers", ] license = "MIT OR Apache-2.0" repository = "https://gitlab.com/antonok/enum_dispatch" [lib] proc-macro = true [dependencies.once_cell] version = "^1.0.1" [dependencies.proc-macro2] version = "^1.0" [dependencies.quote] version = "^1.0" [dependencies.syn] version = "^2.0" features = ["full"] [dev-dependencies.custom_derive] version = "= 0.1.7" [dev-dependencies.enum_derive] version = "= 0.1.7" [dev-dependencies.rand] version = ">= 0.5.5, <= 0.6.1" [dev-dependencies.serde] version = "= 1.0.136" features = ["derive"] [dev-dependencies.serde_json] version = "= 1.0.78" [dev-dependencies.smol] version = "1.3.0" enum_dispatch-0.3.13/Cargo.toml.orig000064400000000000000000000015111046102023000154200ustar 00000000000000[package] name = "enum_dispatch" version = "0.3.13" authors = ["Anton Lazarev "] edition = "2018" description = "Near drop-in replacement for dynamic-dispatched method calls with up to 10x the speed" repository = "https://gitlab.com/antonok/enum_dispatch" readme = "README.md" keywords = ["speed", "performance", "traits", "dynamic", "optimization"] categories = ["rust-patterns", "development-tools::procedural-macro-helpers"] license = "MIT OR Apache-2.0" exclude = ["benches"] [lib] proc-macro = true [dependencies] once_cell = "^1.0.1" quote = "^1.0" proc-macro2 = "^1.0" syn = { version = "^2.0", features = ["full"] } [dev-dependencies] rand = ">= 0.5.5, <= 0.6.1" enum_derive = "= 0.1.7" custom_derive = "= 0.1.7" serde = { version = "= 1.0.136", features = ["derive"] } serde_json = "= 1.0.78" smol = "1.3.0" enum_dispatch-0.3.13/LICENSE000064400000000000000000000020561046102023000135430ustar 00000000000000MIT License Copyright (c) 2019 Anton Lazarev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. enum_dispatch-0.3.13/README.md000064400000000000000000000253651046102023000140250ustar 00000000000000# enum_dispatch [![crates.io](https://img.shields.io/crates/v/enum_dispatch.svg)](https://crates.io/crates/enum_dispatch) [![Docs](https://docs.rs/enum_dispatch/badge.svg)](https://docs.rs/enum_dispatch/) [![License](https://img.shields.io/badge/license-MIT-blue.svg)]() `enum_dispatch` transforms your trait objects into concrete compound types, increasing their method call speed up to 10x. ## example If you have the following code... ```rust // We already have defined MyImplementorA and MyImplementorB, and implemented MyBehavior for each. trait MyBehavior { fn my_trait_method(&self); } // Any pointer type -- Box, &, etc. let a: Box = Box::new(MyImplementorA::new()); a.my_trait_method(); //dynamic dispatch ``` ...then you can improve its performance using `enum_dispatch` like this: ```rust #[enum_dispatch] enum MyBehaviorEnum { MyImplementorA, MyImplementorB, } #[enum_dispatch(MyBehaviorEnum)] trait MyBehavior { fn my_trait_method(&self); } let a: MyBehaviorEnum = MyImplementorA::new().into(); a.my_trait_method(); //no dynamic dispatch ``` Notice the differences: 1. The new enum, `MyBehaviorEnum`, whose variants are simply types implementing the trait `MyBehavior`. 2. The new `enum_dispatch` attributes applied to the enum and trait, linking the two together. 3. The removal of the `Box` allocation. 4. Faster trait method calls! ## how to use 0. Add `enum_dispatch` as a Cargo.toml dependency, and `use enum_dispatch::enum_dispatch` in your code. 1. Create a new enum whose variants are any in-scope trait implementors you've defined. 2. Add an `#[enum_dispatch]` attribute to either the enum or trait definition. This will "register" it with the `enum_dispatch` library. Take note of the name of the enum or trait it was applied to -- we'll call it `FirstBlockName`. 3. Add an `#[enum_dispatch(FirstBlockName)]` attribute to the remaining definition. This will "link" it with the previously registered definition. 4. Update your dynamic types to use the new enum instead. You can use `.into()` from any trait implementor to automatically turn it into an enum variant. ## performance More information on performance can be found in the [docs](https://docs.rs/enum_dispatch/), and benchmarks are available in the `benches` directory. The following benchmark results give a taste of what can be achieved using `enum_dispatch`. They compare the speed of repeatedly accessing method calls on a `Vec` of 1024 trait objects of randomized concrete types using either `Box`ed trait objects, `&` referenced trait objects, or `enum_dispatch`ed enum types. ```text test benches::boxdyn_homogeneous_vec ... bench: 5,900,191 ns/iter (+/- 95,169) test benches::refdyn_homogeneous_vec ... bench: 5,658,461 ns/iter (+/- 137,128) test benches::enumdispatch_homogeneous_vec ... bench: 479,630 ns/iter (+/- 3,531) ``` ## bonus features ### serialization compatibility While `enum_dispatch` was built with performance in mind, the transformations it applies make all your data structures much more visible to the compiler. That means you can use [`serde`](https://crates.io/crates/serde) or other similar tools on your trait objects! ### automatic `From` and `TryInto` implementations `enum_dispatch` will generate a `From` implementation for all inner types to make it easy to instantiate your custom enum. In addition, it will generate a `TryInto` implementation for all inner types to make it easy to convert back into the original, unwrapped types. ### attribute support You can use use `#[cfg(...)]` attributes on `enum_dispatch` variants to conditionally include or exclude their corresponding `enum_dispatch` implementations. Other attributes will be passed straight through to the underlying generated enum, allowing compatibility with other procedural macros. ### `no_std` support `enum_dispatch` is supported in `no_std` environments. It's a great fit for embedded devices, where it's super useful to be able to allocate collections of trait objects on the stack. ## tweaks and options ### custom variant names By default, `enum_dispatch` will expand each enum variant into one with a single unnamed field of the same name as the internal type. If for some reason you'd like to use a custom name for a particular type in an `enum_dispatch` variant, you can do so as shown below: ```rust #[enum_dispatch] enum MyTypes { TypeA, CustomVariantName(TypeB), } let mt: MyTypes = TypeB::new().into(); match mt { TypeA(a) => { /* `a` is a TypeA */ }, CustomVariantName(b) => { /* `b` is a TypeB */ }, } ``` Custom variant names are required for enums and traits with generic type arguments, which can also be optimized by `enum_dispatch`. Check out [this generics example](tests/generics.rs) to see how that works. ### specify multiple enums at once If you want to use `enum_dispatch` to implement the same trait for multiple enums, you may specify them all in the same attribute: ```rust #[enum_dispatch(Widgets, Tools, Gadgets)] trait CommonFunctionality { // ... } ``` ### specify multiple traits at once Similarly to above, you may use a single attribute to implement multiple traits for a single enum: ```rust #[enum_dispatch(CommonFunctionality, WidgetFunctionality)] enum Widget { // ... } ``` ### generic enums and traits `enum_dispatch` can operate on enums and traits with generic parameters. When linking these, be sure to include the generic parameters in the attribute argument, like below: ```rust #[enum_dispatch] trait Foo { /* ... */ } #[enum_dispatch(Foo)] enum Bar { /* ... */ } ``` The names of corresponding generic parameters should match between the definition of the enum and trait. [This example](tests/complex_generics.rs) demonstrates this in more detail. ## troubleshooting ### no impls created? Be careful not to forget an attribute or mistype the name in a linking attribute. If parsing is completed before a linking attribute is found, no implementations will be generated. Due to technical limitations of the macro system, it's impossible to properly warn the user in this scenario. ### can't parse enum? Types must be fully in scope to be usable as an enum variant. For example, the following will fail to compile: ```rust #[enum_dispatch] enum Fails { crate::A::TypeA, crate::B::TypeB, } ``` This is because the enum must be correctly parsable before macro expansion. Instead, import the types first: ```rust use crate::A::TypeA; use crate::B::TypeB; #[enum_dispatch] enum Succeeds { TypeA, TypeB, } ``` ## technical details `enum_dispatch` is a procedural macro that implements a trait for a fixed set of types in the form of an enum. This is faster than using dynamic dispatch because type information will be "built-in" to each enum, avoiding a costly vtable lookup. Since `enum_dispatch` is a procedural macro, it works by processing and expanding attributed code at compile time. The folowing sections explain how the example above might be transformed. ### enum processing There's no way to define an enum whose variants are actual concrete types. To get around this, `enum_dispatch` rewrites its body by generating a name for each variant and using the provided type as its single tuple-style argument. The name for each variant isn't particularly important for most purposes, but `enum_dispatch` will currently just use the name of the provided type. ```rust enum MyBehaviorEnum { MyImplementorA(MyImplementorA), MyImplementorB(MyImplementorB), } ``` ### trait processing `enum_dispatch` doesn't actually process annotated traits! However, it still requires access to the trait definition so it can take note of the trait's name, as well as the function signatures of any methods inside it. ### trait impl creation Whenever `enum_dispatch` is able to "link" two definitions together, it will generate an `impl` block, implementing the trait for the enum. In the above example, the linkage is completed by the `MyBehavior` trait definition, so `impl` blocks will be generated directly below that trait. The generated impl block might look something like this: ```rust impl MyBehavior for MyBehaviorEnum { fn my_trait_method(&self) { match self { MyImplementorA(inner) => inner.my_trait_method(), MyImplementorB(inner) => inner.my_trait_method(), } } } ``` Additional trait methods would be expanded accordingly, and additional enum variants would correspond to additional match arms in each method definition. It's easy to see how quickly this can become unmanageable in manually written code! ### 'From' impl creation Normally, it would be impossible to initialize one of the new enum variants without knowing its name. However, with implementations of `From` for each variant, that requirement is alleviated. The generated implementations could look like the following: ```rust impl From for MyBehaviorEnum { fn from(inner: MyImplementorA) -> MyBehaviorEnum { MyBehaviorEnum::MyImplementorA(inner) } } impl From for MyBehaviorEnum { fn from(inner: MyImplementorB) -> MyBehaviorEnum { MyBehaviorEnum::MyImplementorB(inner) } } ``` As with above, having a large number of possible type variants would make this very difficult to maintain by hand. ### registry and linkage Anyone closely familiar with writing macros will know that they must be processed locally, with no context about the surrounding source code. Additionally, parsed syntax items in `syn` are `!Send` and `!Sync`. This is for good reason -- with multithreaded compilation and macro expansion, there are no guarantees on the order or lifetime of a reference to any given block of code. Unfortunately, it also prevents referencing syntax between separate macro invocations. In the interest of convenience, `enum_dispatch` circumvents these restrictions by converting syntax into a `String` and storing it in `once_cell` lazily initialized `Mutex>`s whose keys are either the trait or enum names. There is also a similar `HashMap` dedicated to "deferred" links, since definitions in different files could be encountered in arbitrary orders. If a linking attribute (with one argument) occurs before the corresponding registry attribute (with no arguments), the argument will be stored as a deferred link. Once that argument's definition is encountered, impl blocks can be created as normal. Because of the link deferral mechanism, it's not an error to encounter a linking attribute without being able to implement it. `enum_dispatch` will simply expect to find the corresponding registry attribute later in parsing. However, there's no way to insert a callback to check that all deferred links have been processed once all the original source code has been parsed, explaining the impossibility of warning the user of unlinked attributes. enum_dispatch-0.3.13/enum_dispatch.svg000064400000000000000000000067541046102023000161130ustar 00000000000000 image/svg+xml enum_dispatch-0.3.13/src/attributed_parser.rs000064400000000000000000000014531046102023000174160ustar 00000000000000//! Contains helper utilities for parsing items that have been annotated with the `enum_dispatch` //! procedural macro attribute. use crate::enum_dispatch_item; /// Enumerates all successful results of parsing an `enum_dispatch` annotated syntax block. #[derive(Clone)] pub enum ParsedItem { Trait(syn::ItemTrait), EnumDispatch(enum_dispatch_item::EnumDispatchItem), } /// Parses any syntax item that was annotated with the `enum_dispatch` attribute and returns its /// itemized results. pub fn parse_attributed(item: proc_macro2::TokenStream) -> Result { if let Ok(enumdef) = syn::parse2(item.clone()) { Ok(ParsedItem::EnumDispatch(enumdef)) } else if let Ok(traitdef) = syn::parse2(item) { Ok(ParsedItem::Trait(traitdef)) } else { Err(()) } } enum_dispatch-0.3.13/src/cache.rs000064400000000000000000000127051046102023000147400ustar 00000000000000//! Procedural macros don't offer a good way to store information between macro invocations. In //! addition, all syntax-related structures implement `!Send` and `!Sync`, making it impossible to //! keep them in any sort of static storage. This module uses some workarounds to add that //! functionality. //! //! Fortunately, `TokenStream`s can be converted to and from `String`s, which can be stored //! statically. Unfortunately, doing so strips any related `Span` information, preventing error //! messages from being as informative as they could be. For now, it seems this is the best option //! available. use quote::ToTokens; use once_cell::sync::Lazy; use std::collections::{HashMap, HashSet}; use std::sync::Mutex; use crate::enum_dispatch_item; /// Uniquely identifies a trait or an enum. This is based on its name and number of arguments. #[derive(PartialEq, Eq, Hash, Clone)] struct UniqueItemId { item_name: String, num_generics: usize, } impl UniqueItemId { /// Convenience constructor for UniqueItemId. pub fn new(item_name: String, num_generics: usize) -> Self { Self { item_name, num_generics, } } } // Magical storage for trait definitions so that they can be used when parsing other syntax // structures. static TRAIT_DEFS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); static ENUM_DEFS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); static DEFERRED_LINKS: Lazy>>> = Lazy::new(|| Mutex::new(HashMap::new())); static ENUM_CONVERSION_IMPLS_DEFS: Lazy>> = Lazy::new(|| Mutex::new(HashSet::new())); /// Store a trait definition for future reference. pub fn cache_trait(item: syn::ItemTrait) { let num_generics = crate::supported_generics::num_supported_generics(&item.generics); let uid = UniqueItemId::new(item.ident.to_string(), num_generics); TRAIT_DEFS .lock() .unwrap() .insert(uid, item.into_token_stream().to_string()); } /// Store an enum definition for future reference. pub fn cache_enum_dispatch(item: enum_dispatch_item::EnumDispatchItem) { let num_generics = crate::supported_generics::num_supported_generics(&item.generics); let uid = UniqueItemId::new(item.ident.to_string(), num_generics); ENUM_DEFS .lock() .unwrap() .insert(uid, item.into_token_stream().to_string()); } /// Store whether a From/TryInto definition has been defined once for an enum. pub fn cache_enum_conversion_impls_defined(item: syn::Ident, num_generics: usize) { let uid = UniqueItemId::new(item.to_string(), num_generics); ENUM_CONVERSION_IMPLS_DEFS.lock().unwrap().insert(uid); } /// Cache a "link" to be fulfilled once the needed definition is also cached. /// /// The number of generic arguments is also cached and must be equal in order to fulfill a link, /// however the actual generic arguments themselves may have different names. pub fn defer_link( (needed, needed_num_generics): (&::proc_macro2::Ident, usize), (cached, cached_num_generics): (&::proc_macro2::Ident, usize), ) { use std::collections::hash_map::Entry; let (needed, cached) = ( UniqueItemId::new(needed.to_string(), needed_num_generics), UniqueItemId::new(cached.to_string(), cached_num_generics), ); let mut deferred_links = DEFERRED_LINKS.lock().unwrap(); if deferred_links.contains_key(&needed) { deferred_links .get_mut(&needed) .unwrap() .push(cached.to_owned()); } else { deferred_links.insert(needed.to_owned(), vec![cached.to_owned()]); } if let Entry::Vacant(e) = deferred_links.entry(cached.clone()) { e.insert(vec![needed]); } else { deferred_links.get_mut(&cached).unwrap().push(needed); } } /// Returns a list of all of the trait definitions that were previously linked to the supplied enum /// name. pub fn fulfilled_by_enum( defname: &::proc_macro2::Ident, num_generic_args: usize, ) -> Vec { let idents = match DEFERRED_LINKS .lock() .unwrap() .remove_entry(&UniqueItemId::new(defname.to_string(), num_generic_args)) { Some((_, links)) => links, None => vec![], }; idents .iter() .filter_map( |ident_string| TRAIT_DEFS.lock().unwrap().get(ident_string).map(|entry| syn::parse(entry.parse().unwrap()).unwrap()) ) .collect() } /// Returns a list of all of the enum definitions that were previously linked to the supplied trait /// name. pub fn fulfilled_by_trait( defname: &::proc_macro2::Ident, num_generic_args: usize, ) -> Vec { let idents = match DEFERRED_LINKS .lock() .unwrap() .remove_entry(&UniqueItemId::new(defname.to_string(), num_generic_args)) { Some((_, links)) => links, None => vec![], }; idents .iter() .filter_map( |ident_string| ENUM_DEFS.lock().unwrap().get(ident_string).map(|entry| syn::parse(entry.parse().unwrap()).unwrap()) ) .collect() } /// Returns true if From/TryInto was already defined for this enum pub fn conversion_impls_def_by_enum(item: &syn::Ident, num_generics: usize) -> bool { ENUM_CONVERSION_IMPLS_DEFS .lock() .unwrap() .contains(&UniqueItemId::new(item.to_string(), num_generics)) } enum_dispatch-0.3.13/src/enum_dispatch_arg_list.rs000064400000000000000000000010221046102023000203720ustar 00000000000000//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing the //! list of arguments that can be passed to an `#[enum_dispatch(...)]` attribute. pub struct EnumDispatchArgList { pub arg_list: syn::punctuated::Punctuated, } impl syn::parse::Parse for EnumDispatchArgList { fn parse(input: &syn::parse::ParseBuffer) -> Result { let arg_list = syn::punctuated::Punctuated::parse_terminated(input)?; Ok(Self { arg_list }) } } enum_dispatch-0.3.13/src/enum_dispatch_item.rs000064400000000000000000000105331046102023000175330ustar 00000000000000//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing the //! shortened enum form used by `enum_dispatch`. //! //! The syntax is *mostly* identical to that of standard enums. The only difference is the //! specification of enum variants -- in the custom `EnumDispatchItem` type, each variant must be //! specified as a `syn::Type` rather than a `syn::Variant`. In the case of basic unit fields named //! after existing scoped types, a normal Rust enum can be parsed as an EnumDispatchItem without //! issue. use quote::TokenStreamExt; use crate::enum_dispatch_variant::EnumDispatchVariant; use crate::filter_attrs::FilterAttrs; /// A structure that can be used to store syntax information about an `enum_dispatch` enum. /// /// Mostly identical to `syn::ItemEnum`. #[derive(Clone)] pub struct EnumDispatchItem { pub attrs: Vec, pub vis: syn::Visibility, enum_token: syn::token::Enum, pub ident: syn::Ident, pub generics: syn::Generics, brace_token: syn::token::Brace, pub variants: syn::punctuated::Punctuated, } /// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s. impl syn::parse::Parse for EnumDispatchItem { fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { let attrs = input.call(syn::Attribute::parse_outer)?; let vis: syn::Visibility = input.parse()?; let enum_token = input.parse::()?; let ident: syn::Ident = input.parse()?; let generics: syn::Generics = input.parse()?; let where_clause = input.parse()?; let content; let brace_token = syn::braced!(content in input); let variants = content.parse_terminated(EnumDispatchVariant::parse, syn::Token![,])?; Ok(Self { attrs, vis, enum_token, ident, generics: syn::Generics { where_clause, ..generics }, brace_token, variants, }) } } /// Allows `EnumDispatchItem`s to be converted into `TokenStream`s. impl quote::ToTokens for EnumDispatchItem { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { tokens.append_all(self.attrs.outer()); self.vis.to_tokens(tokens); self.enum_token.to_tokens(tokens); self.ident.to_tokens(tokens); self.generics.to_tokens(tokens); self.generics.where_clause.to_tokens(tokens); self.brace_token.surround(tokens, |tokens| { self.variants.to_tokens(tokens); }); } } /// Custom conversion implementation that expands the shorthand `enum_dispatch` enum syntax into a /// standard Rust enum syntax. impl ::std::convert::From for syn::ItemEnum { fn from(item: EnumDispatchItem) -> syn::ItemEnum { use ::std::iter::FromIterator; let variants: Vec = item .variants .iter() .map(|variant: &EnumDispatchVariant| syn::Variant { attrs: variant.attrs.to_owned(), ident: variant.ident.to_owned(), fields: syn::Fields::Unnamed(syn::FieldsUnnamed { paren_token: Default::default(), unnamed: { let mut punctuated = syn::punctuated::Punctuated::new(); punctuated.push(syn::Field { attrs: variant.field_attrs.to_owned(), vis: syn::Visibility::Inherited, ident: None, colon_token: Default::default(), ty: variant.ty.to_owned(), mutability: syn::FieldMutability::None, }); punctuated }, }), discriminant: None, }) .collect(); syn::ItemEnum { attrs: item.attrs, vis: item.vis, enum_token: item.enum_token, ident: item.ident, generics: syn::Generics { where_clause: item.generics.where_clause, ..item.generics }, brace_token: item.brace_token, variants: syn::punctuated::Punctuated::from_iter(variants), } } } enum_dispatch-0.3.13/src/enum_dispatch_variant.rs000064400000000000000000000063071046102023000202450ustar 00000000000000//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing a single //! variant for the shortened enum form used by `enum_dispatch`. //! //! Each variant can be either just a type, or a name with a single associated tuple type //! parameter. In the first form, the name is simply the same as the type. In the second, the name //! is explicitly specified. use std::iter::FromIterator; use quote::TokenStreamExt; use crate::filter_attrs::FilterAttrs; /// A structure that can be used to store syntax information about an `enum_dispatch` enum variant. #[derive(Clone)] pub struct EnumDispatchVariant { pub attrs: Vec, pub ident: syn::Ident, pub field_attrs: Vec, pub ty: syn::Type, } /// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s. impl syn::parse::Parse for EnumDispatchVariant { fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { let attrs = input.call(syn::Attribute::parse_outer)?; let ident: syn::Ident = input.parse()?; let (field_attrs, ty) = if input.peek(syn::token::Brace) { unimplemented!("enum_dispatch variants cannot have braces for arguments"); } else if input.peek(syn::token::Paren) { let input: syn::FieldsUnnamed = input.parse()?; let mut fields = input.unnamed.iter(); let field_1 = fields .next() .expect("Named enum_dispatch variants must have one unnamed field"); if fields.next().is_some() { panic!("Named enum_dispatch variants can only have one unnamed field"); } (field_1.attrs.clone(), field_1.ty.clone()) } else { (vec![], into_type(ident.clone())) }; Ok(EnumDispatchVariant { attrs, ident, field_attrs, ty, }) } } /// Allows `EnumDispatchVariant`s to be converted into `TokenStream`s. impl quote::ToTokens for EnumDispatchVariant { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { tokens.append_all(self.attrs.outer()); self.ident.to_tokens(tokens); syn::token::Paren::default().surround(tokens, |tokens| { tokens.append_all(self.field_attrs.iter()); self.ty.to_tokens(tokens); }); } } /// When expanding shorthand `enum_dispatch` enum syntax, each specified, unnamed type variant must /// acquire an associated identifier to use for the name of the standard Rust enum variant. /// /// Note that `proc_macro_attribute`s cannot provide custom syntax parsing. Unless using a /// function-style procedural macro, each type must already be parseable as a unit enum variant. /// This rules out, for example, generic types with lifetime or type parameters. For these, an /// explicitly named variant must be used. fn into_type(ident: syn::Ident) -> syn::Type { syn::Type::Path(syn::TypePath { path: syn::Path { leading_colon: None, segments: syn::punctuated::Punctuated::from_iter(vec![syn::PathSegment { arguments: syn::PathArguments::None, ident, }]), }, qself: None, }) } enum_dispatch-0.3.13/src/expansion.rs000064400000000000000000000443211046102023000157000ustar 00000000000000//! Provides a utility for generating `enum_dispatch` impl blocks given `EnumDispatchItem` and //! `syn::ItemTrait` definitions. use crate::cache; use quote::{quote, ToTokens}; use syn::spanned::Spanned; use crate::enum_dispatch_item::EnumDispatchItem; use crate::enum_dispatch_variant::EnumDispatchVariant; use crate::syn_utils::plain_identifier_expr; /// Name bound to the single enum field in generated match statements. It doesn't really matter /// what this is, as long as it's consistent across the left and right sides of generated match /// arms. For simplicity's sake, the field is bound to this name everywhere it's generated. const FIELDNAME: &str = "inner"; /// Implements the specified trait for the given enum definition, assuming the trait definition is /// already present in local storage. pub fn add_enum_impls( enum_def: EnumDispatchItem, traitdef: syn::ItemTrait, ) -> proc_macro2::TokenStream { let traitname = traitdef.ident; let traitfns = traitdef.items; let (generic_impl_constraints, enum_type_generics, where_clause) = enum_def.generics.split_for_impl(); let (_, trait_type_generics, _) = traitdef.generics.split_for_impl(); let enumname = &enum_def.ident.to_owned(); let trait_impl = quote! { impl #generic_impl_constraints #traitname #trait_type_generics for #enumname #enum_type_generics #where_clause { } }; let mut trait_impl: syn::ItemImpl = syn::parse(trait_impl.into()).unwrap(); trait_impl.unsafety = traitdef.unsafety; let variants: Vec<&EnumDispatchVariant> = enum_def.variants.iter().collect(); for trait_fn in traitfns { trait_impl.items.push(create_trait_match( trait_fn, &trait_type_generics, &traitname, &enum_def.ident, &variants, )); } let mut impls = proc_macro2::TokenStream::new(); // Only generate From impls once per enum_def if !cache::conversion_impls_def_by_enum( &enum_def.ident, enum_def.generics.type_params().count(), ) { let from_impls = generate_from_impls(&enum_def.ident, &variants, &enum_def.generics); for from_impl in from_impls.iter() { from_impl.to_tokens(&mut impls); } let try_into_impls = generate_try_into_impls(&enum_def.ident, &variants, &trait_impl.generics); for try_into_impl in try_into_impls.iter() { try_into_impl.to_tokens(&mut impls); } cache::cache_enum_conversion_impls_defined( enum_def.ident.clone(), enum_def.generics.type_params().count(), ); } trait_impl.to_tokens(&mut impls); impls } /// Returns whether or not an attribute from an enum variant should be applied to other usages of /// that variant's identifier. fn use_attribute(attr: &&syn::Attribute) -> bool { attr.path().is_ident("cfg") } /// Generates impls of core::convert::From for each enum variant. fn generate_from_impls( enumname: &syn::Ident, enumvariants: &[&EnumDispatchVariant], generics: &syn::Generics, ) -> Vec { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); enumvariants .iter() .map(|variant| { let variant_name = &variant.ident; let variant_type = &variant.ty; let attributes = &variant.attrs.iter().filter(use_attribute).collect::>(); let impl_block = quote! { #(#attributes)* impl #impl_generics ::core::convert::From<#variant_type> for #enumname #ty_generics #where_clause { fn from(v: #variant_type) -> #enumname #ty_generics { #enumname::#variant_name(v) } } }; syn::parse(impl_block.into()).unwrap() }).collect() } /// Generates impls of core::convert::TryInto for each enum variant. fn generate_try_into_impls( enumname: &syn::Ident, enumvariants: &[&EnumDispatchVariant], generics: &syn::Generics, ) -> Vec { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); enumvariants .iter() .enumerate() .map(|(i, variant)| { let variant_name = &variant.ident; let variant_type = &variant.ty; let attributes = &variant.attrs.iter().filter(use_attribute).collect::>(); // Instead of making a specific match arm for each of the other variants we could just // use a catch-all wildcard, but doing it this way means we get nicer error messages // that say what the wrong variant is. It also degrades nicely in the case of a single // variant enum so we don't get an unsightly "unreachable pattern" warning. let other = enumvariants .iter() .enumerate() .filter_map( |(j, other)| if i != j { Some(other) } else { None }); let other_attributes = other .clone() .map(|other| { let attrs = other.attrs.iter().filter(use_attribute); quote! { #(#attrs)* } }); let other_idents = other .map(|other| other.ident.clone()); let from_str = other_idents.clone().map(|ident| ident.to_string()); let to_str = core::iter::repeat(variant_name.to_string()); let repeated = core::iter::repeat(&enumname); let impl_block = quote! { #(#attributes)* impl #impl_generics ::core::convert::TryInto<#variant_type> for #enumname #ty_generics #where_clause { type Error = &'static str; fn try_into(self) -> ::core::result::Result<#variant_type, >::Error> { match self { #enumname::#variant_name(v) => {Ok(v)}, #( #other_attributes #repeated::#other_idents(v) => { Err(concat!("Tried to convert variant ", #from_str, " to ", #to_str))} ),* } } } }; syn::parse(impl_block.into()).unwrap() }).collect() } /// Used to keep track of the 'self' arguments in a trait's function signature. /// Static -> no 'self' arguments /// ByReference -> &self, &mut self /// ByValue -> self, mut self enum MethodType { Static, ByReference, ByValue, } /// Parses the arguments of a trait method's signature, returning all non-self arguments as well as /// a MethodType enum describing the self argument, if present. fn extract_fn_args( trait_args: syn::punctuated::Punctuated, ) -> ( MethodType, syn::punctuated::Punctuated, ) { let mut method_type = MethodType::Static; let new_args: Vec = trait_args .iter() .filter_map(|arg| match arg { syn::FnArg::Receiver(syn::Receiver { reference: Some(_), .. }) => { method_type = MethodType::ByReference; None } syn::FnArg::Receiver(syn::Receiver { reference: None, .. }) => { method_type = MethodType::ByValue; None } syn::FnArg::Typed(syn::PatType { pat, .. }) => { if let syn::Pat::Ident(syn::PatIdent { ident, .. }) = &**pat { Some(ident.to_owned()) } else { // All non-ident fn args are replaced in `identify_signature_arguments`. unreachable!() } } }) .collect(); let args = { let mut args = syn::punctuated::Punctuated::new(); new_args.iter().for_each(|arg| { args.push(syn::parse_str(arg.to_string().as_str()).unwrap()); }); args }; (method_type, args) } /// Creates a method call that can be used in the match arms of all non-static method /// implementations. fn create_trait_fn_call( trait_method: &syn::TraitItemFn, trait_generics: &syn::TypeGenerics, trait_name: &syn::Ident, ) -> syn::Expr { let trait_args = trait_method.to_owned().sig.inputs; let (method_type, mut args) = extract_fn_args(trait_args); // Insert FIELDNAME at the beginning of the argument list for UCFS-style method calling let explicit_self_arg = syn::Ident::new(FIELDNAME, trait_method.span()); args.insert(0, plain_identifier_expr(explicit_self_arg)); let mut call = syn::Expr::from(syn::ExprCall { attrs: vec![], func: { if let MethodType::Static = method_type { // Trait calls can be created when the inner type is known, like this: // // syn::parse_quote! { #type::#trait_method_name } // // However, without a concrete enum to match on, it's impossible to tell // which variant to call. unimplemented!( "Static methods cannot be enum_dispatched (no self argument to match on)" ); } else { let method_name = &trait_method.sig.ident; let trait_turbofish = trait_generics.as_turbofish(); // It's not allowed to specify late bound lifetime arguments for a function call. // Theoretically, it should be possible to determine from a function signature // whether or not it has late bound lifetime arguments. In practice, it's very // difficult, requiring recursive visitors over all the types in the signature and // inference for elided lifetimes. // // Instead, it appears to be safe to strip out any lifetime arguments altogether. let mut generics_without_lifetimes = trait_method.sig.generics.clone(); generics_without_lifetimes.params = generics_without_lifetimes .params .into_iter() .filter(|param| !matches!(param, syn::GenericParam::Lifetime(..))) .collect(); let method_type_generics = generics_without_lifetimes.split_for_impl().1; let method_turbofish = method_type_generics.as_turbofish(); Box::new( syn::parse_quote! { #trait_name#trait_turbofish::#method_name#method_turbofish }, ) } }, paren_token: Default::default(), args, }); if trait_method.sig.asyncness.is_some() { call = syn::Expr::from(syn::ExprAwait { attrs: Default::default(), base: Box::new(call), dot_token: Default::default(), await_token: Default::default(), }); } call } /// Constructs a match expression that matches on all variants of the specified enum, creating a /// binding to their single field and calling the provided trait method on each. fn create_match_expr( trait_method: &syn::TraitItemFn, trait_generics: &syn::TypeGenerics, trait_name: &syn::Ident, enum_name: &syn::Ident, enumvariants: &[&EnumDispatchVariant], ) -> syn::Expr { let trait_fn_call = create_trait_fn_call(trait_method, trait_generics, trait_name); let is_self_return = if let syn::ReturnType::Type(_, returntype) = &trait_method.sig.output { match returntype.as_ref() { syn::Type::Path(p) => { if let Some(i) = p.path.get_ident() { i.to_string() == "Self" } else { false } } _ => false, } } else { false }; // Creates a Vec containing a match arm for every enum variant let match_arms = enumvariants .iter() .map(|variant| { let mut call = trait_fn_call.to_owned(); if is_self_return { let variant_type = &variant.ty; let from_call: syn::ExprCall = syn::parse_quote! { >::from(#call) }; call = syn::Expr::from(from_call); } let variant_name = &variant.ident; let attrs = variant .attrs .iter() .filter(use_attribute) .cloned() .collect::>(); syn::Arm { attrs, pat: { let fieldname = syn::Ident::new(FIELDNAME, variant.span()); syn::parse_quote! {#enum_name::#variant_name(#fieldname)} }, guard: None, fat_arrow_token: Default::default(), body: Box::new(call), comma: Some(Default::default()), } }) .collect(); // Creates the match expression syn::Expr::from(syn::ExprMatch { attrs: vec![], match_token: Default::default(), expr: Box::new(plain_identifier_expr(syn::Ident::new( "self", proc_macro2::Span::call_site(), ))), brace_token: Default::default(), arms: match_arms, }) } /// Builds an implementation of the given trait function for the given enum type. fn create_trait_match( trait_item: syn::TraitItem, trait_generics: &syn::TypeGenerics, trait_name: &syn::Ident, enum_name: &syn::Ident, enumvariants: &[&EnumDispatchVariant], ) -> syn::ImplItem { match trait_item { syn::TraitItem::Fn(mut trait_method) => { identify_signature_arguments(&mut trait_method.sig); let match_expr = create_match_expr( &trait_method, trait_generics, trait_name, enum_name, enumvariants, ); let mut impl_attrs = trait_method.attrs.clone(); // Inline impls - #[inline] is never already specified in a trait method signature impl_attrs.push(syn::Attribute { pound_token: Default::default(), style: syn::AttrStyle::Outer, bracket_token: Default::default(), meta: syn::Meta::Path(syn::parse_str("inline").unwrap()), }); syn::ImplItem::Fn(syn::ImplItemFn { attrs: impl_attrs, vis: syn::Visibility::Inherited, defaultness: None, sig: trait_method.sig, block: syn::Block { brace_token: Default::default(), stmts: vec![syn::Stmt::Expr(match_expr, None)], }, }) } _ => panic!("Unsupported trait item"), } } /// All method arguments that appear in trait method signatures must be passed through to the /// underlying dispatched method calls, so they must have unique identifiers. That means we need to /// give names to wildcard arguments (`_`), tuple-style arguments, and a bunch of other argument /// types you never knew were valid Rust syntax. /// /// Since there is no way to generate hygienic identifiers, we just use a special underscored /// string followed by an incrementing counter. We do this for *every* argument, including ones /// that are already named, in case somebody clever decides to name their arguments similarly. fn identify_signature_arguments(sig: &mut syn::Signature) { let mut arg_counter = 0; /// Generates a new argument identifier named `__enum_dispatch_arg_` followed by an /// incrementing counter. fn new_arg_ident(span: proc_macro2::Span, arg_counter: &mut usize) -> syn::Ident { let ident = proc_macro2::Ident::new(&format!("__enum_dispatch_arg_{}", arg_counter), span); *arg_counter += 1; ident } sig.inputs.iter_mut().for_each(|arg| match arg { syn::FnArg::Typed(ref mut pat_type) => { let span = pat_type.span(); *pat_type.pat = match &*pat_type.pat { syn::Pat::Ident(ref pat_ident) => syn::Pat::Ident(syn::PatIdent { ident: new_arg_ident(pat_ident.span(), &mut arg_counter), ..pat_ident.clone() }), // Some of these aren't valid Rust syntax, but why not support all of them anyways! syn::Pat::Lit(syn::PatLit { attrs, .. }) | syn::Pat::Macro(syn::PatMacro { attrs, .. }) | syn::Pat::Or(syn::PatOr { attrs, .. }) | syn::Pat::Path(syn::PatPath { attrs, .. }) | syn::Pat::Range(syn::PatRange { attrs, .. }) | syn::Pat::Reference(syn::PatReference { attrs, .. }) | syn::Pat::Rest(syn::PatRest { attrs, .. }) | syn::Pat::Slice(syn::PatSlice { attrs, .. }) | syn::Pat::Struct(syn::PatStruct { attrs, .. }) | syn::Pat::Tuple(syn::PatTuple { attrs, .. }) | syn::Pat::TupleStruct(syn::PatTupleStruct { attrs, .. }) | syn::Pat::Type(syn::PatType { attrs, .. }) | syn::Pat::Const(syn::PatConst { attrs, .. }) | syn::Pat::Paren(syn::PatParen { attrs, .. }) | syn::Pat::Wild(syn::PatWild { attrs, .. }) => syn::Pat::Ident(syn::PatIdent { attrs: attrs.to_owned(), by_ref: None, mutability: None, ident: new_arg_ident(span, &mut arg_counter), subpat: None, }), // This can occur for `box foo` syntax, which is no longer supported by syn 2.0. syn::Pat::Verbatim(_) => syn::Pat::Ident(syn::PatIdent { attrs: Default::default(), by_ref: None, mutability: None, ident: new_arg_ident(span, &mut arg_counter), subpat: None, }), _ => panic!("Unsupported argument type"), } } // `self` arguments will never need to be renamed. syn::FnArg::Receiver(..) => (), }); } enum_dispatch-0.3.13/src/filter_attrs.rs000064400000000000000000000014211046102023000163700ustar 00000000000000//! Convenience traits for splitting inner and outer attributes. These were originally private to //! the syn crate. /// Private trait copied from syn::attr.rs for convenience when implementing ToTokens pub trait FilterAttrs<'a> { type Ret: Iterator; fn outer(self) -> Self::Ret; } /// Private trait impl copied from syn::attr.rs for convenience when implementing ToTokens impl<'a, T> FilterAttrs<'a> for T where T: IntoIterator, { type Ret = ::std::iter::Filter bool>; fn outer(self) -> Self::Ret { fn is_outer(attr: &&syn::Attribute) -> bool { matches!(attr.style, syn::AttrStyle::Outer) } self.into_iter().filter(is_outer) } } enum_dispatch-0.3.13/src/lib.rs000064400000000000000000000443731046102023000144510ustar 00000000000000//! `enum_dispatch` provides a set of macros that can be used to easily refactor dynamically //! dispatched trait accesses to improve their performance by up to 10x. //! //! Accessing structures through dynamic dispatch is known to have a high runtime cost. Dynamic //! dispatch is traditionally used to hide unnecessary type information, improving encapsulation //! and making it trivial to add new implementations. However, this hiding of information means //! that each time a structure is dynamically accessed, the program must perform a lookup of the //! type's information in a virtual table. The extra round-trips to the vtable quickly add up. //! //! In Rust, dynamic dispatch is done using traits. Rust 2018 adds the `impl` and `dyn` keywords to //! make it easier to keep track of instances of dynamic dispatch, but it's not always easy to //! avoid it entirely. //! //! # Feature documentation //! //! For full documentation of features like generic support, custom variant names, and more, please //! check the repository's //! [README](https://gitlab.com/antonok/enum_dispatch/-/blob/master/README.md). //! //! # How it works //! //! Observe the following example of code describing a user interface with knobs. Each knob can //! hold a value between 0.0 and 1.0. Some knobs provide a *linear* range, whereas other knobs //! provide a *logarithmic* range. //! //! ``` //! trait KnobControl { //! fn set_position(&mut self, value: f64); //! fn get_value(&self) -> f64; //! } //! //! struct LinearKnob { //! position: f64, //! } //! //! struct LogarithmicKnob { //! position: f64, //! } //! //! impl KnobControl for LinearKnob { //! fn set_position(&mut self, value: f64) { //! self.position = value; //! } //! //! fn get_value(&self) -> f64 { //! self.position //! } //! } //! //! impl KnobControl for LogarithmicKnob { //! fn set_position(&mut self, value: f64) { //! self.position = value; //! } //! //! fn get_value(&self) -> f64 { //! (self.position + 1.).log2() //! } //! } //! //! fn main() { //! let v: Vec> = vec![ //! //set the knobs //! ]; //! //! //use the knobs //! } //! ``` //! //! There are other ways to keep an arbitrarily ordered list of different knob types, but none of //! them are quite as simple or easy to maintain. Unfortunately, this implementation uses both heap //! allocated `Box`es and dynamic dispatch, which will have performance implications. //! //! One alternative is to use introduce a new enum type that can hold either a `LinearKnob` or a //! `LogarithmicKnob` as a variant, and also implements `KnobControl` by matching on itself and //! delegating calls to its variants. This would look like the following: //! //! ``` //! # trait KnobControl { //! # fn set_position(&mut self, value: f64); //! # fn get_value(&self) -> f64; //! # } //! # //! # struct LinearKnob { //! # position: f64, //! # } //! # //! # struct LogarithmicKnob { //! # position: f64, //! # } //! # //! # impl KnobControl for LinearKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # self.position //! # } //! # } //! # //! # impl KnobControl for LogarithmicKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # (self.position + 1.).log2() //! # } //! # } //! # //! enum Knob { //! Linear(LinearKnob), //! Logarithmic(LogarithmicKnob), //! } //! //! impl KnobControl for Knob { //! fn set_position(&mut self, value: f64) { //! match self { //! Knob::Linear(inner_knob) => inner_knob.set_position(value), //! Knob::Logarithmic(inner_knob) => inner_knob.set_position(value), //! } //! } //! //! fn get_value(&self) -> f64 { //! match self { //! Knob::Linear(inner_knob) => inner_knob.get_value(), //! Knob::Logarithmic(inner_knob) => inner_knob.get_value(), //! } //! } //! } //! ``` //! //! Performance with this implementation is significantly improved, since all the information the //! program could possibly need to know about each knob can be deduced at compile time. Besides //! avoiding heap allocations and vtable lookups, this allows the compiler to squeeze out even more //! optimization through function inlining. //! //! However, it's easy to see that the cost of maintaining the source code for this extra structure //! is quite high. What happens when we add more knob types? What happens when we add more trait //! methods? Even worse, what happens when we do both! //! //! The resulting code is very repetitive, but that makes it a great target for automatic //! generation. The `enum_dispatch` macro can do the automatic generation for you. Examine the code //! to generate the same implementation when using `enum_dispatch`. //! //! ``` //! # use enum_dispatch::enum_dispatch; //! # //! #[enum_dispatch] //! trait KnobControl { //! //... //! # fn set_position(&mut self, value: f64); //! # fn get_value(&self) -> f64; //! } //! # //! # struct LinearKnob { //! # position: f64, //! # } //! # //! # struct LogarithmicKnob { //! # position: f64, //! # } //! # //! # impl KnobControl for LinearKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # self.position //! # } //! # } //! # //! # impl KnobControl for LogarithmicKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # (self.position + 1.).log2() //! # } //! # } //! //! #[enum_dispatch(KnobControl)] //! enum Knob { //! LinearKnob, //! LogarithmicKnob, //! } //! ``` //! //! That's it. `enum_dispatch` will also automatically generate implementations of //! `std::convert::From` for each enum variant, so that new `Knob`s can be created without concern //! for the names of each enum variant. //! //! ``` //! # use enum_dispatch::enum_dispatch; //! # //! # #[enum_dispatch] //! # trait KnobControl { //! # fn set_position(&mut self, value: f64); //! # fn get_value(&self) -> f64; //! # } //! # //! # struct LinearKnob { //! # position: f64, //! # } //! # //! # struct LogarithmicKnob { //! # position: f64, //! # } //! # //! # impl KnobControl for LinearKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # self.position //! # } //! # } //! # //! # impl KnobControl for LogarithmicKnob { //! # fn set_position(&mut self, value: f64) { //! # self.position = value; //! # } //! # //! # fn get_value(&self) -> f64 { //! # (self.position + 1.).log2() //! # } //! # } //! # //! # #[enum_dispatch(KnobControl)] //! # enum Knob { //! # LinearKnob, //! # LogarithmicKnob, //! # } //! # //! # fn some_existing_knobs() -> (LinearKnob, LogarithmicKnob) { //! # (LinearKnob { position: 0.5 }, LogarithmicKnob { position: 0.5 }) //! # } //! # //! let (a_linear_knob, a_logarithmic_knob) = some_existing_knobs(); //! //! let knob = Knob::from(a_linear_knob); //! let knob = Knob::from(a_logarithmic_knob); //! ``` //! //! # Performance //! //! The `benches` directory contains three benchmarks of different natures, each comparing four //! different methods of accessing a traited struct of an arbitrary type. The four methods are as //! follows: //! //! | test name | explanation | //! |--------------|-----------------------------------------------------------------------------------------| //! | boxdyn | The easiest way to access a struct, using a heap allocation and dynamic dispatch. | //! | refdyn | Accesses the struct by reference, but still using dynamic dispatch. No heap allocation. | //! | customderive | Uses a similar macro approach from the external [`enum_derive`](https://github.com/DanielKeep/rust-custom-derive) crate, which implements a method that returns an inner type as a dynamic trait object. | //! | enumdispatch | Implemented using this crate. | //! //! ## The benchmarks //! //! The following benchmark results were measured on a Ryzen 7 2700x CPU. //! //! ### compiler_optimized //! //! The first set of benchmarks creates trait objects and measures the speed of accessing a method //! on them. //! //! ```text //! test benches::boxdyn_compiler_optimized ... bench: 2,135,418 ns/iter (+/- 12,575) //! test benches::customderive_compiler_optimized ... bench: 2,611,860 ns/iter (+/- 18,644) //! test benches::enumdispatch_compiler_optimized ... bench: 0 ns/iter (+/- 0) //! test benches::refdyn_compiler_optimized ... bench: 2,132,591 ns/iter (+/- 22,114) //! ``` //! //! It's easy to see that `enum_dispatch` is the clear winner here! //! //! Ok, fine. This wasn't a fair test. The compiler is able to "look through" the trait method call //! in the enum_dispatch case, notices that the result is unused, and removes it as an //! optimization. However, this still highlights an important property of `enum_dispatch`ed types: //! the compiler is able to infer much better optimizations when possible. //! //! ### blackbox //! //! The next set of benchmarks uses the `test::black_box` method to hide the fact that the result //! of the method is unused. //! //! ```text //! test benches::boxdyn_blackbox ... bench: 2,131,736 ns/iter (+/- 24,937) //! test benches::customderive_blackbox ... bench: 2,611,721 ns/iter (+/- 23,502) //! test benches::enumdispatch_blackbox ... bench: 471,740 ns/iter (+/- 1,439) //! test benches::refdyn_blackbox ... bench: 2,131,978 ns/iter (+/- 21,547) //! ``` //! //! The competitors faced virtually no impact, whereas `enum_dispatch` takes the full force of the //! `black_box` call. This test shows the power that avoiding dynamic dispatch gives to the //! compiler in the context of the previous test, but also demonstrates how much faster //! `enum_dispatch` is in real code: almost 5 times faster than the closest alternative. //! //! ### homogenous_vec //! //! The final set of benchmarks puts 1024 traited structs of arbitrary types at random into a `Vec` //! and measures the time it takes to successively iterate over the entire `Vec`, calling //! `black_box`ed methods on each element. //! //! ```text //! test benches::boxdyn_homogeneous_vec ... bench: 5,900,191 ns/iter (+/- 95,169) //! test benches::customderive_homogeneous_vec ... bench: 4,831,808 ns/iter (+/- 140,437) //! test benches::enumdispatch_homogeneous_vec ... bench: 479,630 ns/iter (+/- 3,531) //! test benches::refdyn_homogeneous_vec ... bench: 5,658,461 ns/iter (+/- 137,128) //! ``` //! //! This might be one of the most likely use cases for traited structs of arbitrary types, and it's //! where `enum_dispatch` really shines. Since a `Vec` of `enum_dispatch` objects is actually a //! `Vec` of enums rather than addresses, accessing an element takes half the indirection of the //! other techniques. Add that to the lack of vtable accesses, and we have a result that is 10 //! times faster than the closest alternative, and almost 12 times faster than the best technique //! from the standard library. #![doc( html_logo_url = "https://gitlab.com/antonok/enum_dispatch/raw/master/enum_dispatch.svg", html_favicon_url = "https://gitlab.com/antonok/enum_dispatch/raw/master/enum_dispatch.svg" )] extern crate proc_macro; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; /// Used for converting a macro input into an ItemTrait or an EnumDispatchItem. mod attributed_parser; /// Provides local storage for enum and trait definitions so that they can be accessed later. mod cache; /// Provides a custom syntax specification for the arguments to an `#[enum_dispatch(...)]` attribute. mod enum_dispatch_arg_list; /// Provides a custom syntax specification for enum dispatch syntax blocks. mod enum_dispatch_item; /// Provides a custom syntax specification for the variants of enum dispatch syntax blocks. mod enum_dispatch_variant; /// Provides utilities for building enum dispatch implementations. mod expansion; /// Convenience trait for token parsing. mod filter_attrs; /// Codifies the kinds of generic arguments supported in an `#[enum_dispatch(T<...>)]` attribute. mod supported_generics; /// Convenience methods for constructing `syn` types. mod syn_utils; use crate::expansion::add_enum_impls; use crate::supported_generics::{convert_to_supported_generic, num_supported_generics}; /// Annotating a trait or enum definition with an `#[enum_dispatch]` attribute will register it /// with the enum_dispatch library, allowing it to be used to generate impl blocks elsewhere. /// /// Annotating a trait or enum definition with an `#[enum_dispatch(BlockName)]` attribute will /// generate an enum dispatch implementation for the specified trait/enum pair, as well as adding /// an impl of std::convert::From for each variant. When annotating a trait, BlockName should be the /// name of a registered enum. When annotating an enum, BlockName should be the name of a registered /// trait. /// /// An annotated enum should have variants that are simply the names of types imported to the /// current scope. To force individual variants to use a custom name when expanded, each variant /// can also take the form of a normal tuple-style enum variant with a single field. #[proc_macro_attribute] pub fn enum_dispatch(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { enum_dispatch2(attr.into(), item.into()).into() } /// `proc_macro2::TokenStream` compatible version of the `enum_dispatch` function. /// /// Using only `proc_macro2::TokenStream` inside the entire crate makes methods unit-testable and /// removes the need for conversions everywhere. fn enum_dispatch2(attr: TokenStream, item: TokenStream) -> TokenStream { let new_block = attributed_parser::parse_attributed(item.clone()).unwrap(); let mut expanded = match &new_block { attributed_parser::ParsedItem::Trait(traitdef) => { cache::cache_trait(traitdef.to_owned()); item } attributed_parser::ParsedItem::EnumDispatch(enumdef) => { cache::cache_enum_dispatch(enumdef.clone()); syn::ItemEnum::from(enumdef.to_owned()) .into_token_stream() } }; // If the attributes are non-empty, the new block should be "linked" to the listed definitions. // Those definitions may or may not have been cached yet. // If one is not cached yet, the link will be pushed into the cache, and impl generation will // be deferred until the missing definition is encountered. // For now, we assume it is already cached. if !attr.is_empty() { let attr_parse_result = syn::parse2::(attr) .expect("Could not parse arguments to `#[enum_dispatch(...)]`.") .arg_list .into_iter() .try_for_each(|p| { if p.leading_colon.is_some() || p.segments.len() != 1 { panic!("Paths in `#[enum_dispatch(...)]` are not supported."); } let syn::PathSegment { ident: attr_name, arguments: attr_generics } = p.segments.last().unwrap(); let attr_generics = match attr_generics.clone() { syn::PathArguments::None => vec![], syn::PathArguments::AngleBracketed(args) => { assert!(args.colon2_token.is_none()); match args.args.iter().map(convert_to_supported_generic).collect::, _>>() { Ok(v) => v, Err((unsupported, span)) => { let error_string = unsupported.to_string(); return Err(quote::quote_spanned! {span=> compile_error!(#error_string) }); } } } syn::PathArguments::Parenthesized(_) => panic!("Expected angle bracketed generic arguments, found parenthesized arguments"), }; match &new_block { attributed_parser::ParsedItem::Trait(traitdef) => { let supported_generics = num_supported_generics(&traitdef.generics); cache::defer_link((attr_name, attr_generics.len()), (&traitdef.ident, supported_generics)) } attributed_parser::ParsedItem::EnumDispatch(enumdef) => { let supported_generics = num_supported_generics(&enumdef.generics); cache::defer_link((attr_name, attr_generics.len()), (&enumdef.ident, supported_generics)) } } Ok(()) }); if let Err(e) = attr_parse_result { return e; } }; // It would be much simpler to just always retrieve all definitions from the cache. However, // span information is not stored in the cache. Saving the newly retrieved definition prevents // *all* of the span information from being lost. match new_block { attributed_parser::ParsedItem::Trait(traitdef) => { let supported_generics = num_supported_generics(&traitdef.generics); let additional_enums = cache::fulfilled_by_trait(&traitdef.ident, supported_generics); for enumdef in additional_enums { expanded.append_all(add_enum_impls(enumdef, traitdef.clone())); } } attributed_parser::ParsedItem::EnumDispatch(enumdef) => { let supported_generics = num_supported_generics(&enumdef.generics); let additional_traits = cache::fulfilled_by_enum(&enumdef.ident, supported_generics); for traitdef in additional_traits { expanded.append_all(add_enum_impls(enumdef.clone(), traitdef)); } } } expanded } enum_dispatch-0.3.13/src/supported_generics.rs000064400000000000000000000112171046102023000175760ustar 00000000000000//! Utilities for dealing with generic arguments and parameters. /// Represents any single generic argument from e.g. `#[enum_dispatch(Ty<...>)]` that can be /// supported by `enum_dispatch`. pub enum SupportedGenericArg { /// A `_` type. Inferred, /// A named generic argument, e.g. `T`. Identifier(proc_macro2::Ident), /// A const generic char, e.g. `'a'`. ConstChar(syn::LitChar), /// A const generic byte, e.g. `b'a'`. ConstByte(syn::LitByte), /// A const generic integer, e.g. `9`. ConstInt(syn::LitInt), /// A const generic integer, e.g. `true`. ConstBool(syn::LitBool), } /// Represents any single generic argument from `#[enum_dispatch(Ty<...>)]` that can _not_ be /// supported by `enum_dispatch`. pub enum UnsupportedGenericArg { NonIdentifierType, NonIntegralConstGenericType, Lifetime, Constraint, AssocType, AssocConst, Unknown, } impl std::fmt::Display for UnsupportedGenericArg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::NonIdentifierType => write!(f, "Generic types in #[enum_dispatch(...)] must be identifiers"), Self::NonIntegralConstGenericType => write!(f, "Non-integral const generic types in #[enum_dispatch(...)] are not supported"), Self::Lifetime => write!(f, "Lifetime generics in #[enum_dispatch(...)] are not supported"), Self::AssocType => write!(f, "Generic associated types in #[enum_dispatch(...)] are not supported"), Self::AssocConst => write!(f, "Generic associated constants in #[enum_dispatch(...)] are not supported"), Self::Constraint => write!(f, "Generic trait constraints in #[enum_dispatch(...)] are not supported"), Self::Unknown => write!(f, "Unsupported generic argument syntax in #[enum_dispatch(...)]"), } } } /// Strings corresponding to types that are supported as const generics. const SUPPORTED_CONST_GENERIC_TYPES: &[&str] = &[ "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "u128", "i128", "usize", "isize", "char", "bool", ]; /// Counts the number of supported generic parameters from an enum or trait definition. pub fn num_supported_generics(g: &syn::Generics) -> usize { let type_generics = g.type_params().count(); let const_generics = g.const_params().filter(|p| { if let syn::Type::Path(syn::TypePath { qself: None, path }) = &p.ty { for supported_type in SUPPORTED_CONST_GENERIC_TYPES { if path.is_ident(supported_type) { return true; } } } false }).count(); type_generics + const_generics } /// Converts a `syn::GenericArgument` to a `SupportedGenericArg`, or an `UnsupportedGenericArg` if /// it is not supported. pub fn convert_to_supported_generic(generic_arg: &syn::GenericArgument) -> Result { use syn::spanned::Spanned as _; let span = generic_arg.span(); match generic_arg { syn::GenericArgument::Type(syn::Type::Path(t)) if t.qself.is_none() => { if let Some(ident) = t.path.get_ident() { Ok(SupportedGenericArg::Identifier(ident.clone())) } else { Err((UnsupportedGenericArg::NonIdentifierType, span)) } } syn::GenericArgument::Type(syn::Type::Infer(_)) => Ok(SupportedGenericArg::Inferred), syn::GenericArgument::Type(_) => Err((UnsupportedGenericArg::NonIdentifierType, span)), syn::GenericArgument::Const(syn::Expr::Lit(syn::ExprLit { attrs: _, lit })) => { match lit { syn::Lit::Byte(b) => Ok(SupportedGenericArg::ConstByte(b.clone())), syn::Lit::Char(c) => Ok(SupportedGenericArg::ConstChar(c.clone())), syn::Lit::Int(i) => Ok(SupportedGenericArg::ConstInt(i.clone())), syn::Lit::Bool(b) => Ok(SupportedGenericArg::ConstBool(b.clone())), _ => Err((UnsupportedGenericArg::NonIntegralConstGenericType, span)), } } syn::GenericArgument::Const(_) => Err((UnsupportedGenericArg::NonIntegralConstGenericType, span)), syn::GenericArgument::Lifetime(_) => Err((UnsupportedGenericArg::Lifetime, span)), syn::GenericArgument::Constraint(_) => Err((UnsupportedGenericArg::Constraint, span)), syn::GenericArgument::AssocType(_) => Err((UnsupportedGenericArg::AssocType, span)), syn::GenericArgument::AssocConst(_) => Err((UnsupportedGenericArg::AssocConst, span)), _ => Err((UnsupportedGenericArg::Unknown, span)), } } enum_dispatch-0.3.13/src/syn_utils.rs000064400000000000000000000011631046102023000157220ustar 00000000000000//! Contains common convenience methods for building `syn` syntactical constructs. use syn::{punctuated::Punctuated, Expr, ExprPath, Ident, Path, PathArguments, PathSegment}; use std::iter::FromIterator; /// Builds an expression from an identifier, i.e. just a named variable pub fn plain_identifier_expr(ident: Ident) -> Expr { Expr::Path(ExprPath { attrs: vec![], qself: None, path: Path { leading_colon: None, segments: Punctuated::from_iter(vec![PathSegment { ident, arguments: PathArguments::None, }]), }, }) } enum_dispatch-0.3.13/tests/ambiguous_associated_items.rs000064400000000000000000000010031046102023000216300ustar 00000000000000use enum_dispatch::enum_dispatch; use std::convert::TryInto; #[enum_dispatch] trait TestTrait { fn foo(&self) -> u8; } /// The `TryInto::Error` type should not cause conflicts with the variant name. #[enum_dispatch(TestTrait)] enum TestEnum { A, Error, } struct A; impl TestTrait for A { fn foo(&self) -> u8 { 0 } } struct Error; impl TestTrait for Error { fn foo(&self) -> u8 { 1 } } #[test] fn main() { let te = TestEnum::from(Error); let _e: Error = te.try_into().unwrap(); } enum_dispatch-0.3.13/tests/arg_patterns.rs000064400000000000000000000033061046102023000167360ustar 00000000000000#![feature(box_patterns)] use enum_dispatch::enum_dispatch; struct AppState; struct TupleStruct(usize, usize); enum Format { _Raw(usize), _Pretty(usize), } enum Sentinel { _V, } #[enum_dispatch(AppActionEnum)] trait AppAction { fn apply(&mut self, _target: &mut AppState) -> Result<(), String>; fn undo(&mut self, _: &mut AppState) -> Result<(), String>; fn redo( &mut self, target: &mut AppState, TupleStruct(_a, _): TupleStruct, ) -> Result<(), String> { self.apply(target) } fn merge( &mut self, _: &mut AppActionEnum, _: u32, _: &str, (_index1, _index2): (usize, usize), ) -> bool { false } fn batch(&mut self, [_foo, _bar]: [u8; 2], [_baz, ref _qux @ .., _quux]: &[u16; 6]) -> bool { false } fn format( &self, (Format::_Raw(max_len) | Format::_Pretty(max_len)): Format, box _f: Box, 0..=255: u8, Sentinel::_V: Sentinel, ) -> Option<&str> { if max_len > 20 { Some("TODO") } else { None } } } struct Command; impl AppAction for Command { fn apply(&mut self, _target: &mut AppState) -> Result<(), String> { Ok(()) } fn undo(&mut self, _: &mut AppState) -> Result<(), String> { Ok(()) } fn redo( &mut self, target: &mut AppState, TupleStruct(_a, _): TupleStruct, ) -> Result<(), String> { self.apply(target) } } #[enum_dispatch] enum AppActionEnum { Command, } #[test] fn main() { let mut a: AppActionEnum = Command.into(); assert_eq!(false, a.batch([0, 1], &[2, 3, 4, 5, 6, 7])); } enum_dispatch-0.3.13/tests/async-trait.rs000064400000000000000000000012041046102023000164760ustar 00000000000000#![feature(async_fn_in_trait)] use enum_dispatch::enum_dispatch; struct A; struct B; impl XTrait for A { async fn run(&mut self) -> Result { Ok(10) } } impl XTrait for B { async fn run(&mut self) -> Result { Ok(20) } } #[enum_dispatch] enum X { A, B, } #[enum_dispatch(X)] trait XTrait { async fn run(&mut self) -> Result; } fn main() -> smol::io::Result<()> { let mut a: X = A.into(); let mut b: X = B.into(); smol::block_on(async { assert_eq!(10, a.run().await.unwrap()); assert_eq!(20, b.run().await.unwrap()); Ok(()) }) } enum_dispatch-0.3.13/tests/build-no_std.sh000075500000000000000000000001751046102023000166220ustar 00000000000000#!/bin/sh set -eux TARGET="thumbv7em-none-eabi" rustup target add $TARGET cargo build --verbose --target $TARGET --release enum_dispatch-0.3.13/tests/compiles.rs000064400000000000000000000103101046102023000160510ustar 00000000000000use core::convert::TryInto; use enum_dispatch::enum_dispatch; #[enum_dispatch(Traited)] trait TestTrait { fn describe(&self) -> char; fn default_impl(&self) -> Vec { vec![0, 1, 2, 3] } fn has_args(&self, arg1: usize, arg2: &str) -> String; fn mutable(&mut self, argument: f32); fn self_by_value(self, argument: f32) -> f32; } impl TestTrait for A { fn describe(&self) -> char { 'A' } fn has_args(&self, arg1: usize, arg2: &str) -> String { format!("A{},{}", arg1, arg2) } fn mutable(&mut self, _argument: f32) {} fn self_by_value(self, argument: f32) -> f32 { argument + 1. } } impl TestTrait for B { fn describe(&self) -> char { 'B' } fn default_impl(&self) -> Vec { vec![0, 1, 2, 3, 4] } fn has_args(&self, arg1: usize, arg2: &str) -> String { format!("B{},{}", arg1, arg2) } fn mutable(&mut self, _argument: f32) {} fn self_by_value(self, argument: f32) -> f32 { argument + 2. } } impl TestTrait for C { fn describe(&self) -> char { 'C' } fn default_impl(&self) -> Vec { vec![self.custom_number as i8; 10] } fn has_args(&self, arg1: usize, arg2: &str) -> String { format!( "C{},{}+{}", self.custom_number * arg1 as f64, arg2, self.custom_string ) } fn mutable(&mut self, argument: f32) { self.custom_number = argument.into(); } fn self_by_value(self, argument: f32) -> f32 { argument + self.custom_number as f32 } } pub struct A; pub struct B; pub struct C { custom_string: String, custom_number: f64, } pub struct D { a_string: String, } impl TestTrait for D { fn describe(&self) -> char { 'D' } fn default_impl(&self) -> Vec { vec![self.a_string.len() as i8; 15] } fn has_args(&self, arg1: usize, arg2: &str) -> String { format!("D{},{} ==> {}", arg1, arg2, self.a_string) } fn mutable(&mut self, argument: f32) { self.a_string = format!("updated as {}", argument); } fn self_by_value(self, argument: f32) -> f32 { argument * 9. + self.a_string.len() as f32 } } #[enum_dispatch] pub enum Traited { A, B, C, LetterD(D), } #[test] fn main() { let mut a = Traited::from(A); let mut b = Traited::from(B); let mut c = Traited::from(C { custom_string: "the letter C".to_string(), custom_number: 4.2, }); let mut d: Traited = D { a_string: "contained D".to_string(), } .into(); match d { Traited::A(_) => assert!(false), Traited::B(_) => assert!(false), Traited::C(_) => assert!(false), Traited::LetterD(_) => assert!(true), } assert_eq!(a.describe(), 'A'); assert_eq!(b.describe(), 'B'); assert_eq!(c.describe(), 'C'); assert_eq!(d.describe(), 'D'); assert_eq!(a.default_impl().len(), 4); assert_eq!(b.default_impl().len(), 5); assert_eq!(c.default_impl().len(), 10); assert_eq!(c.default_impl()[2], 4); assert_eq!(d.default_impl().len(), 15); assert_eq!(a.has_args(10, "Argument of A"), "A10,Argument of A"); assert_eq!(b.has_args(29, "B's argument"), "B29,B's argument"); assert_eq!( c.has_args(42, "a C parameter"), "C176.4,a C parameter+the letter C" ); assert_eq!( d.has_args(800, "provided to D"), "D800,provided to D ==> contained D" ); a.mutable(9.0); b.mutable(10.0); c.mutable(11.0); d.mutable(90.0); assert_eq!(a.self_by_value(8.2), 9.2); assert_eq!(b.self_by_value(123.45), 125.45); assert_eq!(c.self_by_value(2.4), 13.4); assert_eq!(d.self_by_value(3.), 40.); let d: Traited = D { a_string: "contained D".to_string(), } .into(); let d_from_d: Result = d.try_into(); assert!(d_from_d.is_ok()); let c = Traited::from(C { custom_string: "the letter C".to_string(), custom_number: 4.2, }); let d_from_c: Result = c.try_into(); assert!(d_from_c.is_err()); assert_eq!( d_from_c.err().unwrap(), "Tried to convert variant C to LetterD" ); } enum_dispatch-0.3.13/tests/complex_generics.rs000064400000000000000000000137451046102023000176030ustar 00000000000000use std::hash::Hash; use std::marker::PhantomData; use enum_dispatch::enum_dispatch; /// In this module, the attribute with the argument appears over the enum. mod attribute_argument_on_enum { use super::*; #[enum_dispatch] trait StoresKV { fn store(&mut self, key: K, value: V); fn get(&self, key: &K) -> Option<&V>; fn update_value V>(&mut self, key: &K, update_by: F); } #[enum_dispatch(StoresKV)] enum KVStore { FileStorage(FileStorage), RemoteStorage(RemoteStorage), DevNull, } impl StoresKV for FileStorage { #[allow(unused_variables)] fn store(&mut self, key: K, value: V) { unimplemented!(); } #[allow(unused_variables)] fn get(&self, key: &K) -> Option<&V> { unimplemented!(); } #[allow(unused_variables)] fn update_value V>(&mut self, key: &K, update_by: F) { unimplemented!(); } } impl StoresKV for RemoteStorage { #[allow(unused_variables)] fn store(&mut self, key: K, value: V) { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } #[allow(unused_variables)] fn get(&self, key: &K) -> Option<&V> { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } #[allow(unused_variables)] fn update_value V>(&mut self, key: &K, update_by: F) { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } } impl StoresKV for DevNull { fn store(&mut self, _key: K, _value: V) {} fn get(&self, _key: &K) -> Option<&V> { None } fn update_value V>(&mut self, _key: &K, _update_by: F) {} } #[test] fn attribute_argument_on_enum() { let mut storages: Vec> = vec![ DevNull.into(), FileStorage::default().into(), RemoteStorage::new(SimpleCreds).into(), ]; storages[0].store(String::from("key1"), String::from("value")); assert_eq!(storages[0].get(&String::from("key2")), None); storages[0].update_value(&String::from("key1"), |value| format!("{} + 1", value)); } } /// Same as above, but the attributes are reversed such that the one with an argument is on the /// trait rather than the enum. mod attribute_argument_on_trait { use super::*; #[enum_dispatch(KVStoreReversed)] trait StoresKVReversed { fn store(&mut self, key: K, value: V); fn get(&self, key: &K) -> Option<&V>; fn update_value V>(&mut self, key: &K, update_by: F); } #[enum_dispatch] enum KVStoreReversed { FileStorage(FileStorage), RemoteStorage(RemoteStorage), DevNull, } impl StoresKVReversed for FileStorage { #[allow(unused_variables)] fn store(&mut self, key: K, value: V) { unimplemented!(); } #[allow(unused_variables)] fn get(&self, key: &K) -> Option<&V> { unimplemented!(); } #[allow(unused_variables)] fn update_value V>(&mut self, key: &K, update_by: F) { unimplemented!(); } } impl StoresKVReversed for RemoteStorage { #[allow(unused_variables)] fn store(&mut self, key: K, value: V) { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } #[allow(unused_variables)] fn get(&self, key: &K) -> Option<&V> { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } #[allow(unused_variables)] fn update_value V>(&mut self, key: &K, update_by: F) { let identity = self.credentials.identity(); let api_key = self.credentials.api_key(); unimplemented!(); } } impl StoresKVReversed for DevNull { fn store(&mut self, _key: K, _value: V) {} fn get(&self, _key: &K) -> Option<&V> { None } fn update_value V>(&mut self, _key: &K, _update_by: F) {} } #[test] fn attribute_argument_on_enum() { let mut storages: Vec> = vec![ DevNull.into(), FileStorage::default().into(), RemoteStorage::new(SimpleCreds).into(), ]; storages[0].store(String::from("key1"), String::from("value")); assert_eq!(storages[0].get(&String::from("key2")), None); storages[0].update_value(&String::from("key1"), |value| format!("{} + 1", value)); } } #[derive(Default)] struct FileStorage { key_value_type: PhantomData<(K, V)>, } trait Credentials { fn identity(&self) -> String; fn api_key(&self) -> String; } struct RemoteStorage { key_value_type: PhantomData<(K, V)>, credentials: C, } impl RemoteStorage { fn new(credentials: C) -> Self { Self { key_value_type: Default::default(), credentials, } } } struct DevNull; struct SimpleCreds; impl Credentials for SimpleCreds { fn identity(&self) -> String { String::from("root") } fn api_key(&self) -> String { String::from("D0nTb3f0ol3D-Thi51Sn0tAR34al4P1kEY") } } enum_dispatch-0.3.13/tests/const_generics.rs000064400000000000000000000023041046102023000172470ustar 00000000000000use enum_dispatch::enum_dispatch; struct Nothing; struct One(u8); struct Array([u8; N]); impl Trait for Nothing { fn get_array(&self) -> [u8; N] { [0; N] } } impl Trait for One { fn get_array(&self) -> [u8; N] { [self.0; N] } } impl Trait for Array { fn get_array(&self) -> [u8; N] { self.0 } } impl Trait for Vec { fn get_array(&self) -> [u8; N] { [self.len() as u8; N] } } #[enum_dispatch(Enum)] trait Trait { fn get_array(&self) -> [u8; N]; } #[enum_dispatch] enum Enum { Nothing, One, Array(Array), L(Vec), } #[test] fn main() { let three: Enum<(), 3, b'0', true> = One(2).into(); let eight: Enum = One(3).into(); assert_eq!(three.get_array(), [2, 2, 2]); assert_eq!(eight.get_array(), [3, 3, 3, 3, 3, 3, 3, 3]); } enum_dispatch-0.3.13/tests/core_namespace.rs000064400000000000000000000011261046102023000172070ustar 00000000000000use ::core::convert::TryInto; use enum_dispatch::enum_dispatch; mod core {} struct MyImplementorA {} impl MyBehavior for MyImplementorA { fn my_trait_method(&self) {} } struct MyImplementorB {} impl MyBehavior for MyImplementorB { fn my_trait_method(&self) {} } #[enum_dispatch] enum MyBehaviorEnum { MyImplementorA, MyImplementorB, } #[enum_dispatch(MyBehaviorEnum)] trait MyBehavior { fn my_trait_method(&self); } #[test] fn main() { let a: MyBehaviorEnum = MyImplementorA {}.into(); a.my_trait_method(); let _a: MyImplementorA = a.try_into().unwrap(); } enum_dispatch-0.3.13/tests/field_attrs.rs000064400000000000000000000017051046102023000165460ustar 00000000000000use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; #[enum_dispatch] trait SomeTrait { fn some_method(&self); } #[enum_dispatch(SomeTrait)] #[derive(Debug, PartialEq, Serialize, Deserialize)] enum MyEnum { Foo, Bar(#[serde(skip)] Bar), } #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Foo { test: u32, } impl SomeTrait for Foo { fn some_method(&self) {} } #[derive(Debug, Default, PartialEq, Serialize, Deserialize)] struct Bar; impl SomeTrait for Bar { fn some_method(&self) {} } #[derive(Debug, PartialEq, Serialize, Deserialize)] struct MyContainer { list: Vec, } #[test] fn main() { let data = MyContainer { list: vec![Foo { test: 12 }.into(), Bar.into()], }; let json = serde_json::to_string(&data).unwrap(); assert_eq!(json, "{\"list\":[{\"Foo\":{\"test\":12}},\"Bar\"]}"); let data2 = serde_json::from_str(&json).unwrap(); assert_eq!(data, data2); } enum_dispatch-0.3.13/tests/foreign_types.rs000064400000000000000000000020071046102023000171170ustar 00000000000000use core::convert::TryInto; use enum_dispatch::enum_dispatch; #[enum_dispatch] trait IsAttribute { fn size(&self) -> usize; } impl IsAttribute for Vec { fn size(&self) -> usize { self.len() * std::mem::size_of::() } } impl IsAttribute for Vec { fn size(&self) -> usize { self.len() * std::mem::size_of::() } } #[enum_dispatch(IsAttribute)] enum AnyAttribute { VariantVector2Forf32(Vec), VariantVector3Forf32(Vec), } #[test] fn main() { let floats: Vec = vec![0., 1., 2., 3.]; let doubles: Vec = vec![0., 1., 2., 3., 4., 5., 6., 7.]; let mut all: Vec = vec![floats.into(), doubles.into()]; assert_eq!(all[0].size(), 16); assert_eq!(all[1].size(), 64); let doubles: Vec = all.pop().unwrap().try_into().unwrap(); let floats: Vec = all.pop().unwrap().try_into().unwrap(); assert_eq!(floats, vec![0.0f32, 1., 2., 3.]); assert_eq!(doubles, vec![0.0f64, 1., 2., 3., 4., 5., 6., 7.]); } enum_dispatch-0.3.13/tests/generics.rs000064400000000000000000000020751046102023000160460ustar 00000000000000use enum_dispatch::enum_dispatch; pub struct SuperFoo { _bar: T, } impl Foo for SuperFoo { fn do_something(&mut self, _val: T) { println!("SuperFoo"); } } pub struct UltraFoo { _bar: T, } impl Foo for UltraFoo { fn do_something(&mut self, _val: T) { println!("UltraFoo"); } } pub trait Bar {} #[enum_dispatch] pub trait Foo { fn do_something(&mut self, val: T); } #[enum_dispatch(Foo)] pub enum AnyFoo { SuperFoo(SuperFoo), UltraFoo(UltraFoo), } #[enum_dispatch] pub trait Faz {} use std::marker::PhantomData; pub struct SuperFaz<'a>(PhantomData<&'a u8>); pub struct UltraFaz(); impl<'a> Faz for SuperFaz<'a> {} impl Faz for UltraFaz {} #[enum_dispatch(Faz)] pub enum AnyFaz<'a> { SuperFaz(SuperFaz<'a>), UltraFaz(UltraFaz), } #[test] fn main() { use core::convert::TryInto; let anyfaz: AnyFaz = SuperFaz(PhantomData::<&u8>::default()).into(); let superfaz: Result = anyfaz.try_into(); assert!(superfaz.is_ok()); } enum_dispatch-0.3.13/tests/latebound.rs000064400000000000000000000053501046102023000162230ustar 00000000000000#![deny(late_bound_lifetime_arguments)] use enum_dispatch::enum_dispatch; pub struct S<'a, T>(std::marker::PhantomData<&'a T>); pub trait Trait<'a> {} #[enum_dispatch] pub trait FooAPI { fn foo_warn<'a>(&mut self) -> &'a u32 { unimplemented!() } fn foo_err<'a, 'b>(&'a self, x: &'b u32) -> &'b u32 { x } // have late bound parameters fn e1<'a>(&self) {} fn e2(&self, _e: &()) {} fn e3(&self, _e: &()) -> &() { unimplemented!() } fn e4<'a>(&self, _e: &'a ()) -> &'a () { unimplemented!() } fn e5(&self) {} fn e6(&mut self) {} fn e7<'a, 'b>(&self) {} fn e8<'a>(&self, _t: &'a ()) {} fn e9<'a>(&self, _t: &()) -> &'a () { unimplemented!() } fn e10<'a>(&self, _t: &mut ()) -> &'a () { unimplemented!() } fn e11<'a>(&self, _t: &mut ()) -> &'a mut () { unimplemented!() } fn e12<'a>(&self, _t: ()) -> &'static mut () { unimplemented!() } fn e13<'a>(&self, _t: &'a S<'static, ()>) -> &'static () { unimplemented!() } fn e14<'a>(&self, _t: &'static S<'a, ()>) -> &'static () { unimplemented!() } fn e15<'a>(&self, _t: &'a S<'static, ()>) -> &'a () { unimplemented!() } fn e16<'a>(&self, _t: &'a S<'a, ()>) -> &'static () { unimplemented!() } fn e17<'a, 'b, 'c, T: Trait<'b>>(&self) {} fn e18<'a, 'b, T: Trait<'b>>(&self) {} fn e19<'a, 'b, T: Trait<'b>, F>(&self, _e: F) where F: for<'d, 'c> FnOnce(), { } // have no late bound parameters fn e20(&self) {} fn e21<'a>(&self) -> &'a () { unimplemented!() } fn e22<'a>(&self, _t: ()) -> &'a () { unimplemented!() } fn e23<'a>(&self, _e: &'static ()) -> &'a () { unimplemented!() } fn e24(&self, _t: ()) -> &'static () { unimplemented!() } fn e25(&self, _t: &'static ()) -> &'static () { unimplemented!() } fn e26(&self, _t: &'static S<()>) -> &'static () { unimplemented!() } fn e27(&self, _t: &'static S<'static, ()>) -> &'static () { unimplemented!() } fn e28<'a>(&self, _t: &'static S<'static, ()>) -> &'a () { unimplemented!() } fn e29<'b, T: Trait<'b>>(&self) {} fn e30<'b, T: Trait<'b>, F>(&self, _e: F) where F: for<'d, 'c> FnOnce(), { } fn e31<'b, T: Trait<'b>, F>(&self, _e: F) where F: for<'d, 'c> FnOnce(&'b ()), { } fn e32<'b, F>(&self, _e: F) where F: for<'d, 'c> FnOnce(&'b ()), { } } #[enum_dispatch(FooAPI)] pub enum Foo { Something, } pub struct Something(u32); impl FooAPI for Something {} #[test] fn main() { assert_eq!(*Something(2).foo_err(&8), 8); } enum_dispatch-0.3.13/tests/method_generics.rs000064400000000000000000000024071046102023000174050ustar 00000000000000use enum_dispatch::enum_dispatch; use std::marker::PhantomData; trait Constants { const EXAMPLE: u32; } struct Zero; impl Constants for Zero { const EXAMPLE: u32 = 0; } struct Five; impl Constants for Five { const EXAMPLE: u32 = 5; } #[enum_dispatch(AnyMath)] trait Math { fn get_number(&self) -> u32 where C: Constants; } struct Adder(pub u32); impl Math for Adder { fn get_number(&self) -> u32 where C: Constants, { self.0 + C::EXAMPLE } } struct MultiplierWith { _phantom: PhantomData<*const C2>, } impl Math for MultiplierWith { fn get_number(&self) -> u32 where C: Constants, { C2::EXAMPLE * C::EXAMPLE } } #[enum_dispatch] enum AnyMath { Adder, FiveMultiplier(MultiplierWith), } #[test] fn main() { let two_adder: AnyMath = Adder(2).into(); let five_multiplier: AnyMath = MultiplierWith:: { _phantom: PhantomData, } .into(); assert_eq!(two_adder.get_number::(), 2); assert_eq!(two_adder.get_number::(), 7); assert_eq!(five_multiplier.get_number::(), 0); assert_eq!(five_multiplier.get_number::(), 25); } enum_dispatch-0.3.13/tests/multiple_enums.rs000064400000000000000000000042441046102023000173110ustar 00000000000000use core::convert::TryInto; use enum_dispatch::enum_dispatch; #[enum_dispatch(Traited1)] #[enum_dispatch(Traited2)] trait Trait { fn describe(&self) -> char; } impl Trait for A { fn describe(&self) -> char { 'A' } } impl Trait for B { fn describe(&self) -> char { 'B' } } impl Trait for C { fn describe(&self) -> char { 'C' } } #[enum_dispatch(Traited1, Traited2)] trait TraitSingleLine { fn as_string(&self) -> String; } impl TraitSingleLine for A { fn as_string(&self) -> String { "A".to_string() } } impl TraitSingleLine for B { fn as_string(&self) -> String { " B ".to_string() } } impl TraitSingleLine for C { fn as_string(&self) -> String { self.describe().to_string() } } pub struct A; pub struct B; pub struct C; #[enum_dispatch] pub enum Traited1 { A, B, C, } #[enum_dispatch] pub enum Traited2 { A, B, C, } #[test] fn main() { let a = Traited1::from(A); let b = Traited1::from(B); let c = Traited1::from(C); assert_eq!(a.describe(), 'A'); assert_eq!(b.describe(), 'B'); assert_eq!(c.describe(), 'C'); assert_eq!(a.as_string(), "A".to_string()); assert_eq!(b.as_string(), " B ".to_string()); assert_eq!(c.as_string(), "C".to_string()); let b_from_c: Result = c.try_into(); assert!(b_from_c.is_err()); assert_eq!(b_from_c.err().unwrap(), "Tried to convert variant C to B"); let a_from_a: Result = a.try_into(); assert!(a_from_a.is_ok()); let a_sl = Traited2::from(A); let b_sl = Traited2::from(B); let c_sl = Traited2::from(C); assert_eq!(a_sl.describe(), 'A'); assert_eq!(b_sl.describe(), 'B'); assert_eq!(c_sl.describe(), 'C'); assert_eq!(a_sl.as_string(), "A".to_string()); assert_eq!(b_sl.as_string(), " B ".to_string()); assert_eq!(c_sl.as_string(), "C".to_string()); let b_from_c_sl: Result = c_sl.try_into(); assert!(b_from_c_sl.is_err()); assert_eq!( b_from_c_sl.err().unwrap(), "Tried to convert variant C to B" ); let a_from_a_sl: Result = a_sl.try_into(); assert!(a_from_a_sl.is_ok()); } enum_dispatch-0.3.13/tests/multiple_traits.rs000064400000000000000000000042341046102023000174670ustar 00000000000000use core::convert::TryInto; use enum_dispatch::enum_dispatch; #[enum_dispatch] trait Trait1 { fn describe(&self) -> char; } impl Trait1 for A { fn describe(&self) -> char { 'A' } } impl Trait1 for B { fn describe(&self) -> char { 'B' } } impl Trait1 for C { fn describe(&self) -> char { 'C' } } #[enum_dispatch] trait Trait2 { fn as_string(&self) -> String; } impl Trait2 for A { fn as_string(&self) -> String { "A".to_string() } } impl Trait2 for B { fn as_string(&self) -> String { " B ".to_string() } } impl Trait2 for C { fn as_string(&self) -> String { self.describe().to_string() } } pub struct A; pub struct B; pub struct C; #[enum_dispatch(Trait1)] #[enum_dispatch(Trait2)] pub enum Traited { A, B, C, } #[enum_dispatch(Trait1, Trait2)] pub enum TraitedSingleLine { A, B, C, } #[test] fn main() { let a = Traited::from(A); let b = Traited::from(B); let c = Traited::from(C); assert_eq!(a.describe(), 'A'); assert_eq!(b.describe(), 'B'); assert_eq!(c.describe(), 'C'); assert_eq!(a.as_string(), "A".to_string()); assert_eq!(b.as_string(), " B ".to_string()); assert_eq!(c.as_string(), "C".to_string()); let b_from_c: Result = c.try_into(); assert!(b_from_c.is_err()); assert_eq!(b_from_c.err().unwrap(), "Tried to convert variant C to B"); let a_from_a: Result = a.try_into(); assert!(a_from_a.is_ok()); let a_sl = TraitedSingleLine::from(A); let b_sl = TraitedSingleLine::from(B); let c_sl = TraitedSingleLine::from(C); assert_eq!(a_sl.describe(), 'A'); assert_eq!(b_sl.describe(), 'B'); assert_eq!(c_sl.describe(), 'C'); assert_eq!(a_sl.as_string(), "A".to_string()); assert_eq!(b_sl.as_string(), " B ".to_string()); assert_eq!(c_sl.as_string(), "C".to_string()); let b_from_c_sl: Result = c_sl.try_into(); assert!(b_from_c_sl.is_err()); assert_eq!( b_from_c_sl.err().unwrap(), "Tried to convert variant C to B" ); let a_from_a_sl: Result = a_sl.try_into(); assert!(a_from_a_sl.is_ok()); } enum_dispatch-0.3.13/tests/no_std.rs000064400000000000000000000011231046102023000155260ustar 00000000000000#![no_std] use core::convert::TryInto; use enum_dispatch::enum_dispatch; struct MyImplementorA {} impl MyBehavior for MyImplementorA { fn my_trait_method(&self) {} } struct MyImplementorB {} impl MyBehavior for MyImplementorB { fn my_trait_method(&self) {} } #[enum_dispatch] enum MyBehaviorEnum { MyImplementorA, MyImplementorB, } #[enum_dispatch(MyBehaviorEnum)] trait MyBehavior { fn my_trait_method(&self); } #[test] fn main() { let a: MyBehaviorEnum = MyImplementorA {}.into(); a.my_trait_method(); let _a: MyImplementorA = a.try_into().unwrap(); } enum_dispatch-0.3.13/tests/orders.rs000064400000000000000000000020151046102023000155370ustar 00000000000000use enum_dispatch::enum_dispatch; struct Foo; struct Bar; impl TaggedTrait for Foo {} impl TaggedTrait for Bar {} #[enum_dispatch(TaggedTrait)] enum TaggedEnumBeforeTrait { Foo, Bar, } #[enum_dispatch] enum UntaggedEnumBeforeTrait { Foo, Bar, } // It's unnecessary to add an #[enum_dispatch] attribute here, since the trait will be registered by // the tagged versions. #[enum_dispatch(UntaggedEnumBeforeTrait)] #[enum_dispatch(UntaggedEnumAfterTrait)] trait TaggedTrait { fn baz(&self) -> u8 { 0 } } #[enum_dispatch(TaggedTrait)] enum TaggedEnumAfterTrait { Foo, Bar, } #[enum_dispatch] enum UntaggedEnumAfterTrait { Foo, Bar, } #[test] fn main() { let foo_a = UntaggedEnumAfterTrait::from(Foo); let bar_a = TaggedEnumAfterTrait::from(Bar); let foo_b = UntaggedEnumBeforeTrait::from(Foo); let bar_b = TaggedEnumBeforeTrait::from(Bar); assert_eq!(foo_a.baz(), 0); assert_eq!(bar_a.baz(), 0); assert_eq!(foo_b.baz(), 0); assert_eq!(bar_b.baz(), 0); } enum_dispatch-0.3.13/tests/result_alias.rs000064400000000000000000000010461046102023000167330ustar 00000000000000// A Result alias should not affect the enum_dispatch macro #[allow(dead_code)] #[allow(unused_imports)] use std::io::Result; use enum_dispatch::enum_dispatch; struct Foo; struct Bar; impl TaggedTrait for Foo {} impl TaggedTrait for Bar {} #[enum_dispatch(TaggedTrait)] enum TaggedEnum { Foo, Bar, } #[enum_dispatch] trait TaggedTrait { fn baz(&self) -> u8 { 0 } } #[test] fn main() { let foo = TaggedEnum::from(Foo); let bar = TaggedEnum::from(Bar); assert_eq!(foo.baz(), 0); assert_eq!(bar.baz(), 0); } enum_dispatch-0.3.13/tests/return_self.rs000064400000000000000000000006411046102023000165740ustar 00000000000000use enum_dispatch::enum_dispatch; #[enum_dispatch] trait TestTrait { fn foo(&self) -> Self; } #[enum_dispatch(TestTrait)] enum TestEnum { A, B, } struct A; impl TestTrait for A { fn foo(&self) -> A { A } } struct B; impl TestTrait for B { fn foo(&self) -> B { B } } #[test] fn main() { let a = A; let e: TestEnum = a.into(); let _: TestEnum = e.foo(); } enum_dispatch-0.3.13/tests/scopes.rs000064400000000000000000000014731046102023000155440ustar 00000000000000mod scope1 { use crate::scope2::{Itemized, Subitem2}; use enum_dispatch::enum_dispatch; pub struct Subitem1; impl Itemized for Subitem1 { fn do_it(&self) -> u8 { 1 } } #[enum_dispatch] pub enum Item { Subitem1, Subitem2, } } mod scope2 { use crate::scope1::{Item, Subitem1}; use enum_dispatch::enum_dispatch; pub struct Subitem2; impl Itemized for Subitem2 { fn do_it(&self) -> u8 { 2 } } #[enum_dispatch(Item)] pub trait Itemized { fn do_it(&self) -> u8; } } use crate::scope2::Itemized; #[test] fn main() { let s1: scope1::Item = scope1::Subitem1.into(); let s2 = scope1::Item::from(scope2::Subitem2); assert_eq!(s1.do_it(), 1); assert_eq!(s2.do_it(), 2); } enum_dispatch-0.3.13/tests/serde.rs000064400000000000000000000032051046102023000153450ustar 00000000000000use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; #[enum_dispatch] trait Shaped { fn area(&self) -> f32; } #[enum_dispatch(Shaped)] #[derive(Serialize, Deserialize, Debug, PartialEq)] enum Shape { Rectangle, Square, Circle, } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Rectangle { w: f32, h: f32, } impl Shaped for Rectangle { fn area(&self) -> f32 { self.w * self.h } } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Square { s: f32, } impl Shaped for Square { fn area(&self) -> f32 { self.s * self.s } } #[derive(Serialize, Deserialize, Debug, PartialEq)] struct Circle { r: f32, } impl Shaped for Circle { fn area(&self) -> f32 { self.r * self.r * std::f32::consts::PI } } #[test] fn through_serde() { let rectangle = Rectangle { w: 10., h: 20. }.into(); let square = Square { s: 15. }.into(); let circle = Circle { r: 10. }.into(); let shapes: Vec = vec![rectangle, square, circle]; let serialized_shapes: Vec = shapes .iter() .map(|s| serde_json::to_string(&s).unwrap()) .collect(); assert_eq!( serialized_shapes, [ "{\"Rectangle\":{\"w\":10.0,\"h\":20.0}}", "{\"Square\":{\"s\":15.0}}", "{\"Circle\":{\"r\":10.0}}" ] ); let deserialized_shapes: Vec = serialized_shapes .iter() .map(|s| serde_json::from_str(&s).unwrap()) .collect(); for (shape, new_shape) in shapes.iter().zip(deserialized_shapes.iter()) { assert_eq!(shape, new_shape); } } enum_dispatch-0.3.13/tests/trait_cfg.rs000064400000000000000000000016651046102023000162150ustar 00000000000000use enum_dispatch::enum_dispatch; #[enum_dispatch(Traited)] trait TestTrait { #[cfg(test)] fn test_method(&self) -> char; #[cfg(not(test))] fn release_method(&self) -> char; } impl TestTrait for A { #[cfg(test)] fn test_method(&self) -> char { 'a' } #[cfg(not(test))] fn release_method(&self) -> char { 'A' } } impl TestTrait for B { #[cfg(test)] fn test_method(&self) -> char { 'b' } #[cfg(not(test))] fn release_method(&self) -> char { 'B' } } pub struct A; pub struct B; #[enum_dispatch] pub enum Traited { A, B, } #[test] fn main() { let a = Traited::from(A); let b = Traited::from(B); #[cfg(test)] { assert_eq!(a.test_method(), 'a'); assert_eq!(b.test_method(), 'b'); } #[cfg(not(test))] { assert_eq!(a.release_method(), 'A'); assert_eq!(b.release_method(), 'B'); } } enum_dispatch-0.3.13/tests/type_annotations_needed.rs000064400000000000000000000031571046102023000211530ustar 00000000000000use enum_dispatch::enum_dispatch; struct HasMany(T, Q); struct HasSome(T); struct HasNothing; #[enum_dispatch] trait OptionMethods { fn to_std(self) -> Option; fn count(&self) -> u8; } impl OptionMethods for HasMany { fn to_std(self) -> Option { Some(self.0) } fn count(&self) -> u8 { 2 } } impl OptionMethods for HasSome { fn to_std(self) -> Option { Some(self.0) } fn count(&self) -> u8 { 1 } } impl OptionMethods for HasNothing { fn to_std(self) -> Option { None } fn count(&self) -> u8 { 0 } } // This tests that the dispatch implementation has type annotations // (i.e. `OptionMethods::::count(inner)`), otherwise the compiler cannot infer the types. // See issue #26 #[enum_dispatch(OptionMethods)] enum ManySomeOrNone { HasMany(HasMany), HasSome(HasSome), HasNothing, } #[test] fn test_works() { let has_many: ManySomeOrNone<&str, usize> = HasMany("abc", 19usize).into(); assert_eq!(has_many.count(), 2); assert_eq!(has_many.to_std(), Some("abc")); let has_some: ManySomeOrNone<&str, usize> = HasSome("abc").into(); assert_eq!(has_some.count(), 1); assert_eq!(has_some.to_std(), Some("abc")); let has_nothing: ManySomeOrNone<&str, usize> = HasNothing.into(); assert_eq!( as OptionMethods<&'static str>>::count(&has_nothing), 0 ); assert_eq!( as OptionMethods<&'static str>>::to_std(has_nothing), None ); } enum_dispatch-0.3.13/tests/ucfs.rs000064400000000000000000000023711046102023000152060ustar 00000000000000use core::convert::TryInto; use enum_dispatch::enum_dispatch; pub struct A; pub struct B; impl A { fn describe(&self) -> &str { "A - base impl" } } #[enum_dispatch] pub enum Traited { A, B, } #[enum_dispatch(Traited)] trait TestTrait { fn describe(&self) -> &str; } impl TestTrait for A { fn describe(&self) -> &str { "A - TestTrait" } } impl TestTrait for B { fn describe(&self) -> &str { "B - TestTrait" } } #[enum_dispatch(Traited)] trait Descriptive { fn describe(&self) -> &str; } impl Descriptive for A { fn describe(&self) -> &str { "A - Descriptive" } } impl Descriptive for B { fn describe(&self) -> &str { "B - Descriptive" } } #[test] fn main() { let a = Traited::from(A); let b = Traited::from(B); assert_eq!(TestTrait::describe(&a), "A - TestTrait"); assert_eq!(TestTrait::describe(&b), "B - TestTrait"); assert_eq!(Descriptive::describe(&a), "A - Descriptive"); assert_eq!(Descriptive::describe(&b), "B - Descriptive"); let a: A = a.try_into().unwrap(); assert_eq!(a.describe(), "A - base impl"); assert_eq!(TestTrait::describe(&a), "A - TestTrait"); assert_eq!(Descriptive::describe(&a), "A - Descriptive"); } enum_dispatch-0.3.13/tests/variant_cfg.rs000064400000000000000000000021461046102023000165310ustar 00000000000000use enum_dispatch::enum_dispatch; #[enum_dispatch(Application)] enum App { Menu, #[cfg(test)] DebugClock, #[cfg(not(test))] Clock, } #[enum_dispatch] trait Application { fn run(self) -> usize; } struct Menu; #[cfg(test)] struct DebugClock; #[cfg(not(test))] struct Clock; impl Application for Menu { fn run(self) -> usize { 0 } } #[cfg(test)] impl Application for DebugClock { fn run(self) -> usize { 1 } } #[cfg(not(test))] impl Application for Clock { fn run(self) -> usize { 2 } } #[test] fn main() { use std::convert::TryInto; let menu = Menu; let app: App = menu.into(); let menu: Menu = app.try_into().unwrap(); assert_eq!(menu.run(), 0); #[cfg(test)] { let clock = DebugClock; let app: App = clock.into(); let clock: DebugClock = app.try_into().unwrap(); assert_eq!(clock.run(), 1); } #[cfg(not(test))] { let clock = Clock; let app: App = clock.into(); let clock: Clock = app.try_into().unwrap(); assert_eq!(clock.run(), 2); } }