# Dynamize

In order to turn a trait into a [trait object] the trait must be [object-safe]
and the values of all [associated types] must be specified.  Sometimes you
however want a trait object to be able to encompass trait implementations with
different associated type values. This crate provides a procedural macro to
achieve that.

[trait object]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html
[object-safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
[associated types]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

The following code illustrates a scenario where dynamize can help you:

```rust ignore
trait Client {
    type Error;

    fn get(&self, url: String) -> Result<Vec<u8>, Self::Error>;
}

impl Client for HttpClient { type Error = HttpError; ...}
impl Client for FtpClient { type Error = FtpError; ...}

let client: HttpClient = ...;
let object = &client as &dyn Client;
```

The last line of the above code fails to compile with:

> error\[E0191\]: the value of the associated type `Error` (from trait `Client`)
> must be specified

To use dynamize you only have to make some small changes:

```rust ignore
#[dynamize::dynamize]
trait Client {
    type Error: Into<SuperError>;

    fn get(&self, url: String) -> Result<Vec<u8>, Self::Error>;
}
```

1. You add the `#[dynamize::dynamize]` attribute to your trait.
2. You specify a trait bound for each associated type.

Dynamize defines a new trait for you, named after your trait but
with the `Dyn` prefix, so e.g. `Client` becomes `DynClient`:

```rust ignore
let client: HttpClient = ...;
let object = &client as &dyn DynClient;
```

The new "dynamized" trait can then be used without having to specify the
associated type value.

## How does this work?

For the above example dynamize generates the following code:

```rust ignore
trait DynClient {
    fn get(&self, url: String) -> Result<Vec<u8>, SuperError>;
}

impl<__to_be_dynamized: Client> DynClient for __to_be_dynamized {
    fn get(&self, url: String) -> Result<Vec<u8>, SuperError> {
        Client::get(self, url).map_err(|x| x.into())
    }
}
```

As you can see in the dynamized trait the associated type was replaced with the
destination type of the `Into` bound. The magic however happens afterwards:
dynamize generates a blanket implementation: each type implementing `Client`
automatically also implements `DynClient`!

## How does this actually work?

The destination type of an associated type is
determined by looking at its trait bounds:

* if the first trait bound is `Into<T>` the destination type is `T`

* otherwise the destination type is the boxed trait object of all trait bounds  
  e.g. `Error + Send` becomes `Box<dyn Error + Send>`  
  (for this the first trait bound needs to be object-safe)

Dynamize can convert associated types in:

* return types, e.g. `fn example(&self) -> Self::A`
* callback parameters, e.g. `fn example<F: Fn(Self::A)>(&self, f: F)`

Dynamize also understands if you wrap associated types in the following types:

* tuples
* `Option<_>`
* `Result<_, _>`
* `some::module::Result<_>` (type alias with fixed error type)
* `&mut dyn Iterator<Item = _>`
* `Vec<_>`, `VecDeque<_>`, `LinkedList<_>`, `HashSet<K>`, `BinaryHeap<K>`,
  `BTreeSet<K>`, `HashMap<K, _>`, `BTreeMap<K, _>`  
  (for `K` only `Into`-bounded associated types work because they require `Eq`)

Note that since these are resolved recursively you can actually nest these
arbitrarily so e.g. the following also just works:

```rust ignore
fn example(&self) -> Result<Vec<Self::Item>, Self::Error>;
```

## How does dynamize deal with method generics?

In order to be object-safe methods must not have generics, so dynamize simply
moves them to the trait definition:

```rust
trait Gen {
    fn foobar<A>(&self, a: A) -> A;
}
```

becomes

```rust
trait DynGen<A> {
    fn foobar(&self, a: A) -> A;
}
```

If two method type parameters have the same name, dynamize enforces that they
also have the same bounds and only adds the parameter once to the trait.

## Dynamize supports async

Dynamize supports async out of the box. Since Rust however does not yet support
async functions in traits, you'll have to additionally use another library like
[async-trait](https://crates.io/crates/async-trait), for example:

```rust ignore
#[dynamize::dynamize]
#[dyn_trait_attr(async_trait)]
#[blanket_impl_attr(async_trait)]
#[async_trait]
trait Client: Sync {
    type Error: std::error::Error + Send;

    async fn get(&self, url: String) -> Result<Vec<u8>, Self::Error>;
}
```

* `#[dyn_trait_attr(foo)]` attaches `#[foo]` to the dynamized trait
* `#[blanket_impl_attr(foo)]` attaches `#[foo]` to the blanket implementation

Note that it is important that the `#[dynamize]` attribute comes before the
`#[async_trait]` attribute, since dynamize must run before async_trait.

## Using dynamize with other collections

Dynamize automatically recognizes collections from the standard library like
`Vec<_>` and `HashMap<_, _>`. Dynamize can also work with other collection
types as long as they implement `IntoIterator` and `FromIterator`, for example
dynamize can be used with [indexmap](https://crates.io/crates/indexmap) as
follows:

```rust ignore
#[dynamize::dynamize]
#[collection(IndexMap, 2)]
trait Trait {
    type A: Into<String>;
    type B: Into<i32>;

    fn example(&self) -> IndexMap<Self::A, Self::B>;
}
```

The passed number tells dynamize how many generic type parameters to expect.

* for 1 dynamize expects: `Type<A>: IntoIterator<Item=A> + FromIterator<A>`
* for 2 dynamize expects: `Type<A,B>: IntoIterator<Item=(A,B)> + FromIterator<(A,B)>`
* for 3 dynamize expects: `Type<A,B,C>: IntoIterator<Item=(A,B,C)> + FromIterator<(A,B,C)>`
* etc ...