#traits #forwarding #newtype #delegation #forward

macro forward-traits

A general trait-forwarding crate

8 stable releases

new 3.1.0 Apr 21, 2024
3.0.1 Apr 5, 2024
3.0.0 Mar 26, 2024
2.0.3 Mar 10, 2024
1.0.0 Feb 26, 2024

#1035 in Rust patterns

Download history 140/week @ 2024-02-25 375/week @ 2024-03-03 220/week @ 2024-03-10 7/week @ 2024-03-17 69/week @ 2024-03-24 75/week @ 2024-03-31 15/week @ 2024-04-07

176 downloads per month

MPL-2.0 license

95KB
3K SLoC

This crate provides general mechanisms for implementing traits on types by forwarding an implementation provided by another type.

Two different forwarding methods are provided: Forwarding traits implemented by members, and forwarding traits implemented by types that the receiver type can convert to. These methods may be used in combination on the same receiver type. This crate fully supports generic traits and struct types.

See crate documentation for more details.


lib.rs:

This crate provides general mechanisms for implementing traits on types by forwarding an implementation provided by another type.

Two different forwarding methods are provided: Forwarding traits implemented by members, and forwarding traits implemented by types that the receiver type can convert to. These methods may be used in combination on the same receiver type. This crate fully supports generic traits and struct types.

For more details about capabilities and limitations, see the documentation pages for the individual macros.

Basics

In order to forward a trait, some basic things are needed.

use forward_traits::{forwardable, forward_receiver, forward_traits};

We need a trait definition which is annotated with information which is used to generate forwarded implementations. This is done by applying the #[forwardable] attribute to the definition.

#[forwardable]
trait FooTrait
{
type Bar;

fn foo (&self) -> &Self::Bar;

const BAZ: u32;
}

Then we need a type which initially implements this trait.

struct A {}

impl FooTrait for A
{
type Bar = Self;

fn foo (&self) -> &Self::Bar { self }

const BAZ: u32 = 42;
}

Next, we need a type for which we want to implement this trait by forwarding the implementation found on the initially implementing type. There are a few different ways to define such a type, but here we will demonstrate the newtype idiom. This type needs to be annotated with the #[forward_receiver] attribute.

#[forward_receiver]
struct B (A);

Lastly, we need to specify that we want to forward a trait. In this case, we want to forward a trait implemented by a member, so we write:

forward_traits! (for B . 0 impl FooTrait);

And now we can see that the trait is properly forwarded.

assert_eq! (<B as FooTrait>::BAZ, 42);

Re-Exporting Forwardable Traits

When re-exporting forwardable traits, the #[forwardable] attribute should be applied to the use statement as well. Note that the attribute will interpret every item in the use tree as a trait that should be forwardable. If you want to re-export items that aren't forwardable traits from the same module(s), you'll need to separate those re-exports out into another use statement;

use forward_traits::forwardable;

mod inner
{
use forward_traits::forwardable;

#[forwardable]
pub trait Foo {}

pub struct Bar {}
}

#[forwardable]
pub use inner::Foo;

pub use inner::Bar;

Traits in Other Crates

Forwarding traits works with traits in other crates, so long as those trait definitions are annotated with #[forwardable].

If not, then annotations must be supplied separately. When supplying annotations in this way, the trait is imported (or re-exported if a visibility modifier is supplied) at the location of the annotation macro. When forwarding this trait, you must refer to this import/re-export (or a re-export thereof).

use forward_traits
::{
supply_forwarding_info_for_trait,
forward_receiver,
forward_traits
};

// This has the side-effect of importing IntoIterator into the current scope.
supply_forwarding_info_for_trait!
(
std::iter::IntoIterator,
trait
{
type Item;
type IntoIter;
fn into_iter (self) -> Self::IntoIter;
}
);

#[forward_receiver]
struct VecWrapper <T> (Vec <T>);

// Note that we are referring to the IntoIterator in the current scope.
forward_traits! (for VecWrapper . 0 impl IntoIterator);

// Now we can call the trait method on the wrapper type.
VecWrapper (vec! (1, 2, 3)) . into_iter ();

Dependencies

~0.3–0.8MB
~19K SLoC