delegate

Attribute Macro delegate 

Source
#[delegate]
Expand description

Derives trait on a new-type struct or enum, invoking it on its inner type.

§Example

#[delegate(derive(AsString))]
enum Name {
    First(FirstName),
    Last(LastName),
}

#[delegate(derive(AsString))]
struct FirstName(String);

#[delegate]
struct LastName(String);

#[delegate(for(LastName))]
trait AsString {
    fn into_string(self) -> String;
    fn as_str(&self) -> &str;
    fn as_mut_str(&mut self) -> &mut String;
}

impl AsString for String {
    fn into_string(self) -> Self {
        self
    }
    fn as_str(&self) -> &str {
        self.as_str()
    }
    fn as_mut_str(&mut self) -> &mut Self {
        self
    }
}

let mut name = Name::First(FirstName("John".into()));
assert_eq!(name.as_str(), "John");

name.as_mut_str().push_str("ny");
assert_eq!(name.as_str(), "Johnny");
assert_eq!(name.into_string(), "Johnny");

§Generics

In some cases, a trait or a type requires additional generic parameters to implement delegation. For this case, macro provides for<..> and where syntax for #[delegate(derive(..))]/#[delegate(for(..))] attribute arguments. Specified generics will be merged with the existing ones, provided by the trait/type definition.

#[delegate(for(
    for<U> Case2<U>
    where
        U: Named<N> + 'static,
))]
trait Named<N> {
    fn name(&self) -> N;
}

struct User(String);
impl Named<String> for User {
    fn name(&self) -> String {
        self.0.clone()
    }
}

#[delegate(derive(
    for<N> Named<N>
    where
        U: Named<N> + 'static,
))]
enum Case1<U> {
    User(U),
}

#[delegate]
struct Case2<U>(U);

#[delegate(derive(
   Named<String>
   where
       U: Named<String> + 'static,
))]
enum Case3<U> {
    Case1(Case1<U>),
    Case2(Case2<U>),
}

let user1 = Case1::User(User("Alice".to_string()));
assert_eq!(user1.name(), "Alice");

let user2 = Case2(User("Bob".to_string()));
assert_eq!(user2.name(), "Bob");

let user3 = Case3::Case1(Case1::User(User("Charlie".to_string())));
assert_eq!(user3.name(), "Charlie");

§External types

Because the both sides of the delegation should be marked with the #[delegate] attribute, it’s impossible to make external type delegatable. To handle this, the macro provides the #[delegate(as = my::Def)] attribute argument for struct fields and enum variants. It uses the provided type as known declaration of some external type. Provided type should be crate-local, and marked with the #[delegate] macro, and to provide an infallible conversion from external type (including reference-to-reference one).

#[delegate]
trait AsStr {
    fn as_str(&self) -> &str;
}

impl AsStr for String {
    fn as_str(&self) -> &str {
        self
    }
}

#[delegate(derive(AsStr))]
enum EitherDef {
    Left(String),
    Right(String),
}

impl<'a> From<&'a mut Either<String, String>> for &'a mut EitherDef {
    fn from(t: &'a mut Either<String, String>) -> Self {
        #[expect(unsafe_code, reason = "macro expansion")]
        unsafe {
            &mut *(t as *mut Either<String, String> as *mut EitherDef)
        }
    }
}

impl<'a> From<&'a Either<String, String>> for &'a EitherDef {
    fn from(t: &'a Either<String, String>) -> Self {
        #[expect(unsafe_code, reason = "macro expansion")]
        unsafe {
            &*(t as *const Either<String, String> as *const EitherDef)
        }
    }
}

impl From<Either<String, String>> for EitherDef {
    fn from(t: Either<String, String>) -> Self {
        match t {
            Either::Left(t) => EitherDef::Left(t),
            Either::Right(t) => EitherDef::Right(t),
        }
    }
}

#[delegate(derive(AsStr))]
struct EitherString(#[delegate(as = EitherDef)] Either<String, String>);

let left = EitherString(Either::Left("left".to_string()));
let right = EitherString(Either::Right("right".to_string()));
assert_eq!(left.as_str(), "left");
assert_eq!(right.as_str(), "right");

§External traits

Because the both sides of the delegation should be marked with the #[delegate] attribute, it’s impossible to make an external trait delegatable. To handle this, the macro provides the #[delegate(as = my::Def)] attribute argument for traits. It uses the provided trait as known declaration of some external trait. With this argument, the macro will generate a wrapper type implementing the external trait on it, with the name of the expanded “declaration” trait. By using this wrapper type in #[delegate(derive(ext::Trait as my::TraitDef))] argument, you can delegate external trait to your type.

#[delegate(as = AsRef)]
trait AsRefDef<T: ?Sized> {
    fn as_ref(&self) -> &T;
}

#[delegate]
trait AsStr {
    fn as_str(&self) -> &str;
}

impl AsStr for String {
    fn as_str(&self) -> &str {
        self
    }
}

#[delegate(as = AsStr)]
trait AsStrDef {
    fn as_str(&self) -> &str;
}

#[delegate(derive(
    AsRef<str> as AsRefDef,
    AsStr as AsStrDef,
))]
enum Name {
    First(String),
}

let name = Name::First("John".to_string());
assert_eq!(name.as_ref(), "John");
assert_eq!(name.as_str(), "John");

§Limitations

  • Both struct/enum and trait should be marked with #[delegate] macro attribute.
  • Struct or enum variant should contain only single field.
  • Trait methods must have an untyped receiver.
  • Supertraits or Self trait/method bounds except marker traits like Sized, Send or Sync are not supported yet.
  • Associated types/constants are not supported yet.
  • Lifetimes in methods are limited to be early-bounded in some cases (see rust-lang/rust#87803).
  • Self type is limited to be used in methods return types.