hobofan / ambassador

Delegation of trait implementations via procedural macros
Apache License 2.0
257 stars 13 forks source link

Delegate directly from `Box<T>`, `Arc<T>`, etc. #68

Open musjj opened 4 weeks ago

musjj commented 4 weeks ago

Is there a way to delegate traits directly from Box<T>, Arc<T>, etc. without a wrapper? For example, something like the following:

#[delegatable_trait(for = "Box<T>")]
trait Foo {
    fn hello(&self) -> String;
}

Will generate something like this:

trait Foo {
    fn hello(&self) -> String;
}

impl<T: Foo> Foo for Box<T> {
    fn hello(&self) -> String {
        (**self).hello()
    }
}
dewert99 commented 3 weeks ago

Yes it is possible by using delegate_to_remote_methods which allows foreign types to delegate to arbitrary methods. For example, in your case you could use:

use ambassador::{delegatable_trait, delegate_to_remote_methods};
use std::ops::Deref;

#[delegatable_trait]
trait Foo {
    fn hello(&self) -> String;
}

#[delegate_to_remote_methods]
#[delegate(Foo, target_ref = "deref")]
impl<T> Box<T> {
    fn deref(&self) -> &T;
}

If your trait includes methods with owned self or mutable reference &mut self receivers you would also need to specify target_mut and target_owned. For example:

use ambassador::{delegatable_trait, delegate_to_remote_methods};
use std::ops::{Deref, DerefMut};

#[delegatable_trait]
trait Foo {
    fn hello(&self) -> String;

    fn consume(self);

    fn mutate(&mut self);
}

trait IntoInner: Deref {
    fn into_inner(self) -> Self::Target;
}

// Unfortunately Box doesn't already have a method for this so we need to add one
impl<T> IntoInner for Box<T> {
    fn into_inner(self) -> Self::Target {
        *self
    }
}

#[delegate_to_remote_methods]
#[delegate(Foo, target_ref = "deref", target_mut = "deref_mut", target_owned = "into_inner")]
impl<T> Box<T> {
    fn deref(&self) -> &T;
    fn deref_mut(&mut self) -> &mut T;
    fn into_inner(self) -> T;
}

If you have multiple traits you want to delegate, you can use the delegate attribute multiple times under the same delegate_to_remote_methods to reuse the impl block.