Expand description

Ambassador - Delegate trait implementations via procedural macros

Crates.io version docs.rs docs

Delegating the implementation of traits to enum variants or fields of a struct normally requires a lot of boilerplate code. Ambassador is an attempt to eliminate that boilerplate by deriving the delegating trait implementation via procedural macros.

The minimum supported Rust version is 1.53.0.

See individual macro documentation for detailed instructions.

This is one example combining a large number of features:

extern crate ambassador;

use std::collections::{HashMap, BTreeMap};
use std::borrow::Borrow;
use std::cmp::{Eq, Ord};
use std::hash::{Hash, BuildHasher};
use std::ops::Deref;
use ambassador::{delegatable_trait, delegate_remote, delegate_to_remote_methods, Delegate};

#[delegatable_trait]
pub trait Map {
    type K;
    type V;
}

#[delegatable_trait]
pub trait Get<Q: ?Sized>: Map {
    fn get(&self, k: &Q) -> Option<&Self::V>;
}

impl<K, V, S> Map for HashMap<K, V, S>  {
    type K = K;
    type V = V;
}

impl<K, V> Map for BTreeMap<K, V>  {
    type K = K;
    type V = V;
}


// No automatic where clause provided for target = "self"
#[delegate_remote]
#[delegate(Get<X>, target = "self", generics = "X", where = "K: Hash + Eq + Borrow<X>, S: BuildHasher, X: Hash + Eq + ?Sized")]
struct HashMap<K, V, S>();

#[delegate_remote]
#[delegate(Get<X>, target = "self", generics = "X", where = "K: Ord + Borrow<X>, X: Ord + ?Sized")]
struct BTreeMap<K, V>();

#[derive(Delegate)]
#[delegate(Map)]
#[delegate(Get<X>, generics = "X", where = "X: ?Sized, B: Map<K=A::K, V=A::V>")]  //auto where clause misses required on super trait
pub enum Either<A, B> {
    Left(A),
    Right(B),
}

#[delegate_to_remote_methods]
#[delegate(Map, target_ref = "deref")]
impl<M: ?Sized + Map> Map for Box<M> {
    fn deref(&self) -> &M;
}

fn takes_map(_m: &impl Map<K = &'static str, V = u32>) { }

pub fn main() {
    let my_map: Either<HashMap<&'static str, u32>, BTreeMap<&'static str, u32>> = Either::Left([("a", 1)].into());
    assert_eq!(my_map.get("a"), Some(&1));

    let boxed: Box<dyn Map<K = &'static str, V = u32>> = Box::new(my_map);
    takes_map(&boxed);
}

Backwards Compatibility

Since delegateable traits from one crate can be used in anther crate backwards compatibility of switching to 0.3.x depends on the use case

Self Contained Crate

Switching to 0.3.x should just work, in this case it safe to disable the “backward_compatible” feature

Library with public delegatable traits

Make sure use the “backward_compatible” feature (enabled by default), this makes sure users of your library using an older version of ambassador aren’t affected by the upgrade

Users of a library with public delegatable traits

Try to use the same version of ambassador as the library you’re using

Attribute Macros

Make a trait available for delegation

Make an existing trait that lives outside you crate available for delegation.

Make an existing type that lives outside you crate delegate traits to it’s members

Delegate the implementation of a trait to a type’s methods by adding #[delegate_to_methods] and its associated attribute #[delegate(Trait)] to the relevant impl block

Delegate the implementation of a trait to methods on a type that are defined elsewhere.

Derive Macros

Delegate the implementation of a trait to a struct field/enum variants by adding #[derive(Delegate)] and its associated attribute #[delegate(Trait)] to it: