GitHub license GitHub Workflow Status GitHub Workflow Status Crates.io

degeneric-macros

Quick Start

toml degeneric-macros = "0.2.0"

```rust use degeneric_macros::{Degeneric}; use std::marker::PhantomData;

use traitset::traitset; use typed_builder::TypedBuilder;

trait_set!(trait FactoryFn = 'static + Send + Sync + Fn() -> T);

[derive(Degeneric, TypedBuilder)]

struct Container, B> { a: A, b: B, c: u32, #[builder(default)] _t: PhantomData, }

fn my_fact() -> String { format!("hello world!") }

let c = Container::builder().a(myfact).b(true).c(20).build(); dosomething(&c); accessinnertypes(&c);

fn dosomething(c: &impl ContainerTrait) {} fn accessinnertypes(c: &C) { let sameas_a: C::A; } ```

Elevator pitch

The problem

Degeneric is a utility library that solves the common problem of having too many generics. Let's say we want to construct a dependency container like this: ```rust struct Container { logger: Logger, client: HttpClient, // ...and so on... }

let container = Container { logger: String::from("logger"), client: String::from("http"), };

acceptscontainer(container); // now to consume such a container, one needs to write the function like this: fn acceptscontainer(c: Container) {} ```

This creates a problem of ever growing list of generics in all functions that touch the container and pollutes APIs with unnecessary generics.

Degeneric solution

Degeneric proposes solution to this problem by creating a trait and stuffing all of the generic types into the trait as associated types. Instead of the pattern above, you'll end up with this: ```rust use degeneric_macros::Degeneric;

[derive(Degeneric)]

struct Container { logger: Logger, client: HttpClient, }

let c = Container { logger: String::from("logger"), client: String::from("http"), };

acceptscontainer(c); fn acceptscontainer(c: impl ContainerTrait) {} ```

How is this different, you ask? Instead of accepting a whole lot of generic arguments, I can now write the function without even using angular brackets and I think that's beautiful. What is even more beautiful is that you can add more generics without having to modify the signature of accepts_container.

Degeneric understands lifetimes

```rust use std::borrow::Cow; use std::fmt::Debug;

use degenericmacros::{Degeneric}; use typedbuilder::TypedBuilder;

[derive(Degeneric, TypedBuilder)]

struct Container<'a, T: 'a + PartialEq + Debug> { cow: &'a Cow<'a, str>, reference: &'a T, }

let cow = Cow::Owned(String::from("hello lifetimes")); { let reference = 42; let c = Container::builder().cow(&cow).reference(&reference).build();

fn accept_container<'a>(cont: &impl ContainerTrait<'a>) {
    assert_eq!(cont.cow().as_ref(), "hello lifetimes");
    assert_eq!(*cont.reference(), &42_i32);
}

accept_container(&c);

} ```

Crates degeneric plays nice with

License: MIT