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);
}