10 unstable releases (3 breaking)

0.4.3 Apr 12, 2024
0.4.2 Dec 11, 2023
0.3.2 Dec 9, 2023
0.2.3 Oct 31, 2023
0.0.2 Oct 28, 2023

#390 in Rust patterns

50 downloads per month

MIT license

29KB
599 lines

Functor Derive

githubcrates-iodocs-rs

This crate can generate a functor for generic structs and enums.

A functor is a trait that contains an fmap function that maps a generic parameter. This enables you to transform the contents of any type without altering its shape.

The following example demonstrates how to derive a functor, providing you with an fmap method. For more intricate examples, refer to the tests directory in the project repository.

use functor_derive::Functor;

#[derive(Functor)]
struct MyType<T> {
    value: T,
    list: Vec<T>,
    unaffected: bool,
}

fn main() {
    let original = MyType { value: 42, list: vec![1, 3], unaffected: false };
    let transformed = original.fmap(|x| (x, x * 2));

    assert_eq!(transformed.value, (42, 84));
    assert_eq!(transformed.list, vec![(1, 2), (3, 6)]);
}

Additionally, a try_fmap function is generated. This can be useful for fallible transformations.

let original = MyType { value: "42", list: vec!["1", "3"], unaffected: false };
let transformed = original.try_fmap(|x| x.parse::<u64>())?;

Attribute

You can invoke the derive macro in multiple ways. Omitting the attribute defaults to deriving the Functor trait for the first generic type parameter, as illustrated in the first example above.

Alternatively, you can specify a default type to override the derive macro, which will prevent the derive macro choosing the first generic type parameter. This is done as follows:

#[derive(Functor)]
#[functor(T2)]
struct MyType<T1, T2> {
    field_1: T1,
    field_2: T2,
}

Sometimes, you might want to rename your fmap function using the as keyword. The following example generates the method fmap_keys.

#[derive(Functor)]
#[functor(K as keys)]
struct MyType<K> {
    keys: Vec<K>
}

The above options can be combined to generate multiple implementations, by separating the options with commas. The code below generates 3 methods: fmap, fmap_keys and fmap_values.

use std::collections::HashMap;
use std::hash::Hash;

#[functor(V, K as keys, V as values)]
struct MyHashMap<K: Hash + Eq, V> {
    v: HashMap<K, V>
}

Supported features

This crate can handle the following perfectly:

  • Structs - except for unit structs, which cannot be generic
  • Enums
  • Arrays
  • Tuples
  • std::collections: Vec, VecDeque, LinkedList, HashSet, HashMap, BTreeMap, Result, Option, PhantomData
  • Nested types, like Option<Box<T>>
  • (Mutually) recursive types
  • Bounded parameters, like T: Display

If you find a case where the derive macro fails, feel free to open an issue here

Dependencies

~0.7–1.2MB
~25K SLoC