Derive Macro forest_filecoin::shim::fvm_latest::kernel::prelude::Delegate
source · #[derive(Delegate)]
{
// Attributes available to this derive:
#[delegate]
}
Expand description
Delegate the implementation of a trait to a struct field/enum variants by adding #[derive(Delegate)]
and its associated attribute #[delegate(Trait)]
to it:
use ambassador::{Delegate, 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)
}
}
#[derive(Delegate)] // <-------
#[delegate(Shout)] // <-------- Delegate implementation of Shout to struct field
pub struct WrappedCat(Cat);
§#[delegate(..., target = "foo")]
- target
key
For structs with multiple fields, the field that should act as delegation target can be specified via the target
key:
#[derive(Delegate)]
#[delegate(Shout, target = "foo")] // <-------- Delegate implementation of Shout to struct field .foo
pub struct WrappedCats {
foo: Cat,
bar: Cat,
}
This also works for tuple structs with multiple fields, by using their index as a target key:
#[derive(Delegate)]
#[delegate(Shout, target = "1")] // <-------- Delegate implementation of Shout to second field
pub struct WrappedCats(Cat, Cat);
§#[delegate(..., target = "self")]
- target="self"
Types that implement all the methods of a trait without implementing the trait itself,
can be made to implement that trait by setting target="self"
.
This doesn’t work for traits with associated types and constants, and requires the where clause to be added explicitly (see where
key).
If the type doesn’t actually implement the methods (possibly due to an incomplete where
clause) this can cause a [unconditional_recursion]
error.
A possible use case of this is when refactoring some methods of a public type into a trait, the type still needs to implement the methods outside the trait for semver reasons, and using this feature reduces the boilderplate of implementing the trait with the same methods.
#[derive(Delegate)]
#[delegate(Shout, target="self")]
pub struct Cat;
impl Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
§#[delegate(..., where = "A: Debug")]
- where
key
To make a delegation apply only for certain generic bounds, similar to a native where clause, you can specify a where
attribute:
A where clause is automatically applied that makes sure the target field implements the trait being delegated
use std::fmt::Debug;
#[derive(Delegate)]
#[delegate(Shout, where = "A: Debug")] // <---- Delegate implementation of Shout to .foo field if foo field implements Debug
// "A: Shout" is automatically added
pub struct WrappedFoo<A> {
foo: A,
}
§#[delegate(Shout<X>, generics = "X")]
- trait generics
We can also delegate traits with generics.
The type parameters listed in the generics
key are treated as fully generic.
The automatically added where clause ensures they are valid for the inner type being delegated to.
Explict where clauses to further refine these types can be added as normal.
use ambassador::{delegatable_trait, Delegate};
use std::fmt::Display;
#[delegatable_trait]
pub trait Shout<T> {
fn shout(&self, input: T) -> String;
}
pub struct Cat;
impl<T: Display> Shout<T> for Cat {
fn shout(&self, input: T) -> String {
format!("{} - meow!", input)
}
}
#[derive(Delegate)]
#[delegate(Shout<X>, generics = "X")] // <-------- `X` is fully generic
// The automatic where clause ensures X: Display
// We could also use #[delegate(Shout<& 'a str>, generics = "'a")] to only delegate for &str
pub struct WrappedCat(Cat);
§#[delegate(Shout, automatic_where_clause = "false")]
- inhibit automatic generation of where
clause.
Normally #[derive(Delegate)]
generates code to ensure that chosen field
indeed implements the trait you want to implement for the container struct.
For example, the #[derive(Delegate)]
+ #[delegate(Shout<X>, generics = "X")]
in the example above will emit code that requires Cat
(the type we’re
delegating to) to implement Shout<X>
: Cat: Shout<X>
.
However, you may want to delegate implementation to a type that does not in fact fully implement the trait in question but instead has compatible methods that can be called as if the trait were in fact implemented.
One notable examples of this is delegating a trait implementation to
container types holding a trait object; i.e. MyTrait
for
Box<dyn MyTrait>
. In this example, Box<dyn MyTrait
Deref
s into dyn MyTrait
which provides MyTrait
s
methods; this allows us effectively just call MyTrait
s methods directly
on Box<dyn MyTrait>
even though Box<dyn MyTrait>
does not actually
implement MyTrait
.
automatic_where_clause = "false"
lets us create a delegated impl of
MyTrait
that takes advantage of this.
use ambassador::{delegatable_trait, Delegate};
use std::fmt::Display;
#[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)
}
}
#[derive(Delegate)]
#[delegate(Shout, automatic_where_clause = "false")]
pub struct BoxedAnimal(pub Box<dyn Shout + Send + Sync>);
// Can accept both `Cat` and `BoxedAnimal`.
fn recording_studio<S: Shout>(voice_actor: S){}
Note that it is also possible to create such a delegated impl by making use
of delegate_to_remote_methods
with Deref::deref
and
DerefMut::deref_mut
as the target methods. The docs on
delegate_to_remote_methods
contain an example of this.