Crate ambassador

source ·
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 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 V = V;
}

impl<K, V> Map for BTreeMap<K, V>  {
    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<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")]
#[delegate(Get<X>, target_ref = "deref", generics = "X", where = "X: ?Sized")]
impl<M: ?Sized> Box<M> {
    fn deref(&self) -> &M;
}

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

§Cross module uses

Modules using delegateable traits should add use <MODULE PATH>::ambassador_impl_<TRAIT NAME>; where <MODULE PATH> is the path to the module which used either delegatable_trait or delegatable_trait_remote

§Example

mod m{
    pub mod m1 {
        use ambassador::delegatable_trait;
        #[delegatable_trait]
        pub trait Shout {
            fn shout(&self);
        }
    }

    mod m2 {
        use ambassador::Delegate;
        use super::m1::{Shout, ambassador_impl_Shout};

        #[derive(Delegate)]
        #[delegate(Shout)]
        struct Wrap<X>(X);
    }
}

§Backwards Compatibility

§0.3.x -> 0.4.x

§Creating delegateable traits

Delagatable trait macros ambassador_impl_Trait are no longer exported at the crate root, and are instead exported in the module where delegatable_trait or delegatable_trait_remote are used. If these traits are public then upgrading is also a breaking change for users of your library. The “backward_compatible” is also removed.

§Using delegateable traits

Switching versions does not affect usages of delegateable traits

§0.2.x -> 0.3.x

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: