#macro-derive #map #derive #data #macro #no-std

macro no-std funcmap_derive

Derivable functorial mappings for Rust

6 releases

new 0.1.5 Nov 5, 2024
0.1.4 Jun 25, 2023
0.1.3 Mar 21, 2023
0.1.2 Aug 13, 2022
0.1.1 Apr 24, 2022

#484 in #map

Download history 12/week @ 2024-07-21 21/week @ 2024-07-28 25/week @ 2024-09-22 1/week @ 2024-09-29 1/week @ 2024-10-06 125/week @ 2024-11-03

125 downloads per month
Used in funcmap

MIT/Apache

100KB
2K SLoC

funcmap - Derivable functorial mappings for Rust

GitHub crates.io docs.rs license rustc 1.65+

This Rust crate provides the FuncMap trait (and its fallible version TryFuncMap) that can be automatically derived for a type that is generic over a type parameter. It provides a method that applies a given closure to all (potentially nested) occurrences of the type parameter within the type, removing the need to write verbose mapping code.

Concretely, given a generic type Foo<T> and an FnMut(A) -> B closure, it can turn any value of type Foo<A> into a value of type Foo<B>. This is called a functorial mapping following the functor design pattern of functional programming.

Installation

This crate is available on crates.io. In order to use it, add this to the dependencies table of your Cargo.toml:

[dependencies]
funcmap = "0.1.5"

Usage

Suppose you have a type that is generic over some type parameter T and contains a T in various places:

struct Foo<T> {
    value: T,
    more_values: Vec<T>,
}

Now suppose you want to turn a Foo<i32> into a Foo<String> by converting each i32 contained in the type into a String by applying to_string. You can do this by deriving the FuncMap trait provided by this crate and then invoking its func_map method like this:

#[derive(FuncMap)]
struct Foo<T> {
    value: T,
    more_values: Vec<T>,
}

let foo = Foo {
    value: 1,
    more_values: vec![2, 3, 4],
};

let bar = foo.func_map(|v| v.to_string());

assert_eq!(bar.value, "1");
assert_eq!(bar.more_values, vec!["2", "3", "4"]);

The expression foo.func_map(|v| v.to_string()) is equivalent to this:

Foo {
    value: foo.value.to_string(),
    more_values: foo.more_values.into_iter().map(|v| v.to_string()).collect()
}

This way, you avoid writing boilerplate mapping code, especially in cases where your type contains many and/or deeply nested occurrences of T. This works for both structs and enums and many ways of nesting T within your type such as arrays, tuples and many types from the standard library as well as your own types as long as they implement FuncMap themselves. Note that the purpose of the funcmap crate is just to provide utility functionality, so

  • you shouldn't depend on any of the items it exports in your public API,
  • it shouldn't be necessary to use bounds on the traits it exports anywhere except in generic implementations of those same traits.

For a more detailed explanation and more features, see the crate documentation.

For larger examples, see the examples folder.

Minimum Supported Rust Version (MSRV) Policy

The current MSRV of this crate is 1.65.

Increasing the MSRV of this crate is not considered a breaking change. However, in such cases there will be at least a minor version bump. Each version of this crate will support at least the four latest stable Rust versions at the time it is published.

Changelog

See CHANGELOG.md

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~1–1.6MB
~32K SLoC