Attribute Macro ambassador::delegate_to_remote_methods

source ·
#[delegate_to_remote_methods]
Expand description

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

This macro is identical to delegate_to_methods except that it does not actually produce an impl block on the type for the target methods; instead it assumes that the methods are implemented elsewhere.

use ambassador::{delegate_to_remote_methods, delegatable_trait};

#[delegatable_trait]
pub trait Shout {
    fn shout(&self, input: &str) -> String;
}

pub struct Cat;

impl Shout for Cat {
    fn shout(&self, input: &str) -> String {
        format!("{} - meow!", input)
    }
}

pub struct BoxedCat(Box<Cat>);

impl BoxedCat {
   fn inner(&self) -> &Cat { &self.0 }
}

#[delegate_to_remote_methods]
#[delegate(Shout, target_ref = "inner")]
impl BoxedCat {
    // `inner` can be defined anywhere: trait method or inherent method, in
    // this crate or elsewhere
    fn inner(&self) -> &Cat;
}

As such, this macro will actually error if you provide it with methods with blocks, method signatures that aren’t used by a delegate attribute on the impl, or other impl items:


#[delegate_to_remote_methods]
#[delegate(Shout, target_ref = "inner")]
impl BoxedCat {
    fn inner(&self) -> &Cat { &self.0 } // This is an error, method bodies
                                        // aren't accepted

    fn extra(&self);                    // Extra methods are also error since no
                                        // `impl BoxedCat { ... }` is actually
                                        // emitted

    const CONST: () = ();               // This is also an error.
}

Target methods can come from a trait or be inherent methods whose actual implementation lives elsewhere. You can mix and match:

use std::ops::{Deref, DerefMut};

impl Deref for BoxedCat {
    type Target = Cat;

    fn deref(&self) -> &Self::Target { &self.0 }
}

impl DerefMut for BoxedCat {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

#[delegate_to_remote_methods]
#[delegate(Shout, target_ref = "inner", target_mut = "deref_mut")]
impl BoxedCat {
    fn inner(&self) -> &Cat;
    fn deref_mut(&mut self) -> &mut Cat;
}

Note that if you do use target methods from a trait, the trait must be in scope.

Because this macro does not implement any inherent methods on the type being delegated to, the type can be remote (like with delegate_remote):

use std::ops::{Deref, DerefMut};

// Note that this impl says `Deref for Box<Cat>`.
//
// The trait in this impl is ignored and only serves as documentation here.
#[delegate_to_remote_methods]
#[delegate(Shout, target_ref = "deref")]
impl Deref for Box<Cat> {
    fn deref(&self) -> &Cat;
}

fn shout(_: &impl Shout) { }

fn main() {
    let c = Cat;
    shout(&c);

    let boxed: Box<Cat> = Box::new(c);
    shout(&boxed);
}

This can be used in conjunction with generics to provide blanket delegated implementations of a local trait without needing to create intermediary local traits that provide target methods to use:

use std::{sync::Arc, rc::Rc};

pub struct Dog;
impl Shout for Dog {
    fn shout(&self, input: &str) -> String { format!("{} - wuff!", input) }
}

#[delegate_to_remote_methods]
#[delegate(Shout, target_ref = "deref")]
impl<S: ?Sized + Shout, T: Deref<Target = S>> T {
    fn deref(&self) -> &S;
}

pub fn shout(pet: &impl Shout) { println!("{}", pet.shout("hi")); }

pub fn main() {
    shout(&Cat);
    shout(&Dog);

    let a: Box<dyn Shout> = Box::new(Cat);
    let b: Arc<dyn Shout + Send> = Arc::new(Dog);
    let c: Rc<dyn Shout + Send + Sync + 'static> = Rc::new(Cat);
    shout(&a);
    shout(&b);
    shout(&c);

    let d: Box<Cat> = Box::new(Cat);
    shout(&d);
}