Design improvements proposals
Hi! Our team created macro with similar functionality but it hasn't been published yet. To avoid duplication we consider it will be better to offer you several options for improving the design of the macro.
First of all. Here's it's documentation and test cases of our variant:
Macro documentation
Derives trait on a new-type struct or enum, invoking it on its inner type.
Examples
use common::delegate;
#[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
}
}
fn main() {
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");
}
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.
- Implementation for external (remote) types or traits is not supported yet.
- 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.
-
#[delegate(for(Enum<Generics>))]
supports only concrete generic types.
Tests
use common::delegate;
mod derives_trait {
use super::*;
#[delegate]
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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
#[delegate(derive(AsString))]
enum Name {
First(FirstName),
Last { name: LastName },
}
#[delegate(derive(AsString))]
struct FirstName(String);
#[delegate(derive(AsString))]
struct LastName {
name: String,
}
#[test]
fn newtype_derives_trait() {
let mut first_name = FirstName("John".into());
*first_name.as_mut_str() = "Jane".into();
assert_eq!(first_name.as_str(), "Jane");
assert_eq!(first_name.into_string(), "Jane");
let mut last_name = LastName { name: "Doe".into() };
*last_name.as_mut_str() = "Smith".into();
assert_eq!(last_name.as_str(), "Smith");
assert_eq!(last_name.into_string(), "Smith");
}
#[test]
fn enum_derives_trait() {
let mut first_name = Name::First(FirstName("John".into()));
*first_name.as_mut_str() = "Jane".into();
assert_eq!(first_name.as_str(), "Jane");
assert_eq!(first_name.into_string(), "Jane");
let mut last_name = Name::Last {
name: LastName { name: "Doe".into() },
};
*last_name.as_mut_str() = "Smith".into();
assert_eq!(last_name.as_str(), "Smith");
assert_eq!(last_name.into_string(), "Smith");
}
}
mod impls_for {
use super::*;
#[delegate(for(Name, FirstName, 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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
#[delegate]
enum Name {
First(FirstName),
Last { name: LastName },
}
#[delegate]
struct FirstName(String);
#[delegate]
struct LastName {
name: String,
}
#[test]
fn impls_trait_for_newtype() {
let mut first_name = FirstName("John".into());
*first_name.as_mut_str() = "Jane".into();
assert_eq!(first_name.as_str(), "Jane");
assert_eq!(first_name.into_string(), "Jane");
let mut last_name = LastName { name: "Doe".into() };
*last_name.as_mut_str() = "Smith".into();
assert_eq!(last_name.as_str(), "Smith");
assert_eq!(last_name.into_string(), "Smith");
}
#[test]
fn impls_trait_for_enum() {
let mut first_name = Name::First(FirstName("John".to_string()));
*first_name.as_mut_str() = "Jane".to_string();
assert_eq!(first_name.as_str(), "Jane");
assert_eq!(first_name.into_string(), "Jane");
let mut last_name = Name::Last {
name: LastName { name: "Doe".into() },
};
*last_name.as_mut_str() = "Smith".into();
assert_eq!(last_name.as_str(), "Smith");
assert_eq!(last_name.into_string(), "Smith");
}
}
mod trait_default_method_impls {
use super::*;
#[delegate]
trait AsString: Sized {
fn into_string(self) -> String {
"default impl".into()
}
fn as_str(&self) -> &str {
"default impl"
}
fn as_mut_str(&mut self) -> String {
"default impl".into()
}
}
impl AsString for String {
fn into_string(self) -> String {
self
}
fn as_str(&self) -> &str {
self.as_str()
}
fn as_mut_str(&mut self) -> String {
self.clone()
}
}
impl AsString for () {}
#[delegate(derive(AsString))]
enum Name {
First(FirstName),
Last { name: LastName },
}
#[delegate(derive(AsString))]
struct FirstName(String);
#[delegate(derive(AsString))]
struct LastName {
name: (),
}
#[test]
fn newtype_derives_trait() {
let mut first_name = FirstName("John".to_string());
assert_eq!(first_name.as_str(), "John");
assert_eq!(first_name.as_mut_str(), "John");
assert_eq!(first_name.into_string(), "John");
let mut last_name = LastName { name: () };
assert_eq!(last_name.as_str(), "default impl");
assert_eq!(last_name.as_mut_str(), "default impl");
assert_eq!(last_name.into_string(), "default impl");
}
#[test]
fn enum_derives_trait() {
let mut first_name = Name::First(FirstName("John".to_string()));
assert_eq!(first_name.as_str(), "John");
assert_eq!(first_name.as_mut_str(), "John");
assert_eq!(first_name.into_string(), "John");
let mut last_name = Name::Last {
name: LastName { name: () },
};
assert_eq!(last_name.as_str(), "default impl");
assert_eq!(last_name.as_mut_str(), "default impl");
assert_eq!(last_name.into_string(), "default impl");
}
}
mod trait_generic_param {
use super::*;
trait Name {}
impl Name for String {}
#[delegate]
trait Named<N>
where
N: Name,
{
fn name(&self) -> N;
}
#[delegate]
trait Versioned<const V: u8> {
fn version(&self) -> String;
}
#[delegate(derive(Named, Versioned))]
enum Users {
Oleg(UserOleg),
Boris { user: UserBoris },
}
struct UserOleg(String);
impl Named<String> for UserOleg {
fn name(&self) -> String {
self.0.clone()
}
}
impl Versioned<2> for UserOleg {
fn version(&self) -> String {
format!("UserOleg v2")
}
}
struct UserBoris {
name: String,
}
impl Named<String> for UserBoris {
fn name(&self) -> String {
self.name.clone()
}
}
impl Versioned<2> for UserBoris {
fn version(&self) -> String {
format!("UserBoris v2")
}
}
#[test]
fn derives_with_generics() {
let oleg = Users::Oleg(UserOleg("Oleg".to_string()));
assert_eq!(oleg.name(), "Oleg");
let boris = Users::Boris {
user: UserBoris {
name: "Boris".to_string(),
},
};
assert_eq!(boris.name(), "Boris");
}
#[test]
fn derives_with_const_generics() {
let oleg = Users::Oleg(UserOleg("Oleg".to_string()));
assert_eq!(oleg.version(), "UserOleg v2");
let boris = Users::Boris {
user: UserBoris {
name: "Boris".to_string(),
},
};
assert_eq!(boris.version(), "UserBoris v2");
}
}
mod enum_generic_param {
use super::*;
struct UserTypes;
const USER_OLEG: u8 = 1;
const USER_BORIS: u8 = 2;
trait UserType<const V: u8> {
type User: User;
}
type TypedUser<const V: u8> = <UserTypes as UserType<V>>::User;
impl UserType<{ USER_OLEG }> for UserTypes {
type User = UserOleg;
}
impl UserType<{ USER_BORIS }> for UserTypes {
type User = UserBoris;
}
#[delegate(for(
EitherUser<UserOleg, UserBoris>,
GenericUser<{ USER_OLEG }, { USER_BORIS }>
))]
trait User {
fn name(&self) -> &str;
}
#[delegate]
enum EitherUser<L, R> {
Left(L),
Right { user: R },
}
#[delegate]
enum GenericUser<const U1: u8, const U2: u8>
where
UserTypes: UserType<{ U1 }> + UserType<{ U2 }>,
{
Left(TypedUser<{ U1 }>),
Right { user: TypedUser<{ U2 }> },
}
struct UserOleg;
impl User for UserOleg {
fn name(&self) -> &str {
"Oleg"
}
}
struct UserBoris;
impl User for UserBoris {
fn name(&self) -> &str {
"Boris"
}
}
#[test]
fn derives_with_generics() {
let oleg = EitherUser::<UserOleg, UserBoris>::Left(UserOleg);
assert_eq!(oleg.name(), "Oleg");
let boris =
EitherUser::<UserOleg, UserBoris>::Right { user: UserBoris };
assert_eq!(boris.name(), "Boris");
}
#[test]
fn derives_with_const_generics() {
let oleg = GenericUser::<{ USER_OLEG }, { USER_BORIS }>::Left(UserOleg);
assert_eq!(oleg.name(), "Oleg");
let boris = GenericUser::<{ USER_OLEG }, { USER_BORIS }>::Right {
user: UserBoris,
};
assert_eq!(boris.name(), "Boris");
}
}
mod generics_in_methods {
use super::*;
#[delegate]
trait PrependString {
fn prepend_string<S: Into<String>>(&self, s: S) -> String;
}
struct FirstName;
impl PrependString for FirstName {
fn prepend_string<S: Into<String>>(&self, s: S) -> String {
format!("Bob's {}", s.into())
}
}
struct LastName;
impl PrependString for LastName {
fn prepend_string<S: Into<String>>(&self, s: S) -> String {
format!("Smith's {}", s.into())
}
}
#[delegate(derive(PrependString))]
enum Name {
First(FirstName),
Last(LastName),
}
#[test]
fn derives_with_generics() {
let first = Name::First(FirstName);
assert_eq!(first.prepend_string("apple"), "Bob's apple");
let last = Name::Last(LastName);
assert_eq!(last.prepend_string("apple"), "Smith's apple");
}
}
mod trait_lifetimes {
use std::fmt::Display;
use super::*;
#[delegate]
trait User<'s> {
fn name(&'s self) -> &'s str;
fn into_prepended_with_name<S: Display + ?Sized>(
self,
s: &'s S,
) -> String;
}
struct UserOleg;
impl<'s> User<'s> for UserOleg {
fn name(&'s self) -> &'s str {
"Oleg"
}
fn into_prepended_with_name<S: Display + ?Sized>(
self,
s: &'s S,
) -> String {
format!("{}'s {s}", self.name())
}
}
struct UserBoris;
impl<'s> User<'s> for UserBoris {
fn name(&'s self) -> &'s str {
"Boris"
}
fn into_prepended_with_name<S: Display + ?Sized>(
self,
s: &'s S,
) -> String {
format!("{}'s {s}", self.name())
}
}
#[delegate(derive(User))]
enum Users {
Oleg(UserOleg),
Boris(UserBoris),
}
#[test]
fn derives_with_lifetimes() {
let oleg = Users::Oleg(UserOleg);
assert_eq!(oleg.name(), "Oleg");
assert_eq!(oleg.into_prepended_with_name("apple"), "Oleg's apple");
let boris = Users::Boris(UserBoris);
assert_eq!(boris.name(), "Boris");
assert_eq!(boris.into_prepended_with_name("apple"), "Boris's apple");
}
}
mod methods_lifetimes {
use super::*;
#[delegate]
trait User {
fn name(&'_ self) -> &'_ str;
fn prepended_with_name(&'_ self, s: &'_ str) -> String;
fn prepend_with_name<'s>(&'s self, s: &'s mut String)
-> &'s mut String;
fn prepend_name_to<'s>(&self, s: &'s mut String);
fn into_prepended_with_name<'a>(self, s: &'a str) -> String;
}
struct UserOleg;
impl User for UserOleg {
fn name(&'_ self) -> &'_ str {
"Oleg"
}
fn prepended_with_name(&'_ self, s: &'_ str) -> String {
format!("{}'s {s}", self.name())
}
fn prepend_with_name<'s>(&self, s: &'s mut String) -> &'s mut String {
s.insert_str(0, &format!("{}'s ", self.name()));
s
}
fn prepend_name_to(&self, s: &mut String) {
s.insert_str(0, &format!("{}'s ", self.name()));
}
fn into_prepended_with_name<'a>(self, s: &'a str) -> String {
format!("{}'s {s}", self.name())
}
}
struct UserBoris;
impl User for UserBoris {
fn name(&'_ self) -> &'_ str {
"Boris"
}
fn prepended_with_name(&'_ self, s: &'_ str) -> String {
format!("{}'s {s}", self.name())
}
fn prepend_with_name<'s>(&self, s: &'s mut String) -> &'s mut String {
s.insert_str(0, &format!("{}'s ", self.name()));
s
}
fn prepend_name_to(&self, s: &mut String) {
s.insert_str(0, &format!("{}'s ", self.name()));
}
fn into_prepended_with_name<'a>(self, s: &'a str) -> String {
format!("{}'s {s}", self.name())
}
}
#[delegate(derive(User))]
enum Users {
Oleg(UserOleg),
Boris(UserBoris),
}
#[test]
fn derives_with_lifetimes() {
let oleg = Users::Oleg(UserOleg);
assert_eq!(oleg.name(), "Oleg");
assert_eq!(oleg.prepended_with_name("apple"), "Oleg's apple");
assert_eq!(
oleg.prepend_with_name(&mut String::from("orange")),
"Oleg's orange",
);
let mut orange = String::from("orange");
oleg.prepend_name_to(&mut orange);
assert_eq!(orange, "Oleg's orange");
assert_eq!(oleg.into_prepended_with_name("apple"), "Oleg's apple");
let boris = Users::Boris(UserBoris);
assert_eq!(boris.name(), "Boris");
assert_eq!(boris.prepended_with_name("apple"), "Boris's apple");
assert_eq!(
boris.prepend_with_name(&mut String::from("orange")),
"Boris's orange",
);
let mut orange = String::from("orange");
boris.prepend_name_to(&mut orange);
assert_eq!(orange, "Boris's orange");
assert_eq!(boris.into_prepended_with_name("apple"), "Boris's apple");
}
}
enum_delegate
and delegate
macro
The main differences between
delegate
macro not using macro expansion to pass enum declaration to actual macro.
1. Using macro_rules!
causes some potential problems for users:
- slowdown compilation speed;
- leaking private macros outside of the crate.
Source code
#[enum_delegate::implement(b::AsString)]
enum Name {
First(String),
Last(String),
}
mod b {
#[enum_delegate::register]
pub(super) 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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
}
Expanded `enum_delegate` macro on enum
enum Name {
First(String),
Last(String),
}
b::AsString!(
b::AsString,
Name,
enum Name {
First(String),
Last(String),
}
);
Expanded `enum_delegate` macro on trait
#[doc(hidden)]
#[macro_export] // <--- leaked from current crate.
macro_rules! enum_delegate_AsString_AQAB1t6kB7Ro6giYcTro4lRYzbVN2IKV6B4aUAYAKM {
(
$trait_path:path,
$enum_path:path,
$enum_declaration:item
) => {
enum_delegate::implement_trait_for_enum! { // <--- will be error in cases when `enum_delegate` is already defined in scope/`enum_delegate` crate is renamed in `Cargo.toml`.
$trait_path,
pub(super) trait AsString {
fn into_string(self) -> String;
fn as_str(&self) -> &str;
fn as_mut_str(&mut self) -> &mut String;
},
$enum_path,
$enum_declaration
}
};
}
#[doc(hidden)]
pub use enum_delegate_AsString_AQAB1t6kB7Ro6giYcTro4lRYzbVN2IKV6B4aUAYAKM as AsString; // <--- public leak. `pub` instead of `pub(super)`.
pub(super) trait AsString {
fn into_string(self) -> String;
fn as_str(&self) -> &str;
fn as_mut_str(&mut self) -> &mut String;
}
The way delegate
macro avoid this problems is using Rust's type system instead of passing info between macros on different items:
Macro expanded on enum implements trait for converting enum
into type representing "any of the enum
's variant". Another part of the macro expanded on trait uses generic implementation for any type can be converted into its variants.
Common definitions
/// Enum for holding either `L` or `R` type.
#[derive(Clone, Copy, Debug)]
pub enum Either<L, R> {
/// Left type.
Left(L),
/// Right type.
Right(R),
}
/// Type of unreachable [`Either`] variant.
#[derive(Clone, Copy, Debug)]
pub enum Void {}
/// Trait for restricting delegation of a trait to the specified types only.
pub trait Bound<
const PATH_HASH: u128,
const TRAIT_NAME_HASH: u128,
const LINE: u32,
const COLUMN: u32,
>
{
}
/// Trait for converting a type into its delegate.
pub trait Convert {
/// Type of an owned any enum variant.
type Owned;
/// Type of a referenced any enum variant.
type Ref<'a>
where
Self: 'a;
/// Type of a mutable referenced any enum variant.
type RefMut<'a>
where
Self: 'a;
/// Converts this enum into an owned variant.
fn convert_owned(self) -> Self::Owned;
/// Converts reference to this enum into a variant reference.
fn convert_ref(&self) -> Self::Ref<'_>;
/// Converts mutable reference to this enum into a mutable variant
/// reference.
fn convert_ref_mut(&mut self) -> Self::RefMut<'_>;
}
Source code
#[delegate]
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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
#[delegate(derive(AsString))]
enum Name {
First(String),
Last { name: String },
}
Expanded `delegate` macro on enum
enum Name {
First(String),
Last { name: String },
}
// Interface to access enum's variants.
#[automatically_derived]
impl ::common::delegate::Convert for Name {
type Owned = ::common::delegate::Either<
String,
::common::delegate::Either<String, ::common::delegate::Void>,
>;
type Ref<'__delegate> = ::common::delegate::Either<&'__delegate String, ::common::delegate::Either<&'__delegate String, ::common::delegate::Void>>
where
String: '__delegate,
String: '__delegate;
type RefMut<'__delegate> = ::common::delegate::Either<&'__delegate mut String, ::common::delegate::Either<&'__delegate mut String, ::common::delegate::Void>>
where
String: '__delegate,
String: '__delegate;
fn convert_owned(self) -> <Self as ::common::delegate::Convert>::Owned {
match self {
Self::First(v) => ::common::delegate::Either::Left(v),
Self::Last { name } => ::common::delegate::Either::Right(
::common::delegate::Either::Left(name),
),
}
}
fn convert_ref(&self) -> <Self as ::common::delegate::Convert>::Ref<'_> {
match self {
Self::First(v) => ::common::delegate::Either::Left(v),
Self::Last { name } => ::common::delegate::Either::Right(
::common::delegate::Either::Left(name),
),
}
}
fn convert_ref_mut(
&mut self,
) -> <Self as ::common::delegate::Convert>::RefMut<'_> {
match self {
Self::First(v) => ::common::delegate::Either::Left(v),
Self::Last { name } => ::common::delegate::Either::Right(
::common::delegate::Either::Left(name),
),
}
}
}
// Allow `AsString` generic implementation to implement delegation for `Name`.
#[automatically_derived]
impl
::common::delegate::Bound<
{ AsString.0 },
{ AsString.1 },
{ AsString.2 },
{ AsString.3 },
> for Name
{
}
Expanded `delegate` macro on trait
// Trait itself.
trait AsString {
fn into_string(self) -> String;
fn as_str(&self) -> &str;
fn as_mut_str(&mut self) -> &mut String;
}
// Marker to use in `Bound` implementation.
#[allow(non_upper_case_globals)]
#[automatically_derived]
#[doc(hidden)]
const AsString: (u128, u128, u32, u32) = (
::common::delegate::fnv1a128(::core::file!()),
::common::delegate::fnv1a128("AsString"),
::core::line!(),
::core::column!(),
);
#[allow(non_snake_case)]
#[automatically_derived]
#[doc(hidden)]
mod __delegate_AsString {
use super::*;
// Owned part of `AsString` trait.
#[allow(non_camel_case_types)]
#[automatically_derived]
trait AsString__DelegateOwned {
fn into_string(self) -> String;
}
// Implementation of owned part for any enum's variant.
#[automatically_derived]
impl<__Left, __Right> AsString__DelegateOwned
for ::common::delegate::Either<__Left, __Right>
where
__Left: AsString,
__Right: AsString__DelegateOwned,
{
fn into_string(self) -> String {
match self {
Self::Left(__delegate) => {
<__Left as AsString>::into_string(__delegate)
}
Self::Right(__delegate) => {
<__Right as AsString__DelegateOwned>::into_string(
__delegate,
)
}
}
}
}
// Implementation of owned part for end marker of "any enum's variant" type.
#[automatically_derived]
impl AsString__DelegateOwned for ::common::delegate::Void {
fn into_string(self) -> String {
match self {}
}
}
// Referenced part of `AsString` trait.
#[allow(non_camel_case_types)]
#[automatically_derived]
trait AsString__DelegateRef<'__delegate>
where
Self: Sized + '__delegate,
{
fn as_str(self) -> &'__delegate str;
}
// Implementation of referenced part for any enum's variant.
#[automatically_derived]
impl<'__delegate, __Left, __Right> AsString__DelegateRef<'__delegate>
for ::common::delegate::Either<&'__delegate __Left, __Right>
where
Self: Sized + '__delegate,
__Left: AsString,
__Right: AsString__DelegateRef<'__delegate>,
{
fn as_str(self) -> &'__delegate str {
match self {
Self::Left(__delegate) => {
<__Left as AsString>::as_str(__delegate)
}
Self::Right(__delegate) => {
<__Right as AsString__DelegateRef<'__delegate>>::as_str(
__delegate,
)
}
}
}
}
// Implementation of referenced part for end marker of "any enum's variant" type.
#[automatically_derived]
impl<'__delegate> AsString__DelegateRef<'__delegate>
for ::common::delegate::Void
where
Self: Sized + '__delegate,
{
fn as_str(self) -> &'__delegate str {
match self {}
}
}
// Mutable referenced part of `AsString` trait.
#[allow(non_camel_case_types)]
#[automatically_derived]
trait AsString__DelegateRefMut<'__delegate>
where
Self: Sized + '__delegate,
{
fn as_mut_str(self) -> &'__delegate mut String;
}
// Implementation of mutable referenced part for any enum's variant.
#[automatically_derived]
impl<'__delegate, __Left, __Right> AsString__DelegateRefMut<'__delegate>
for ::common::delegate::Either<&'__delegate mut __Left, __Right>
where
Self: Sized + '__delegate,
__Left: AsString,
__Right: AsString__DelegateRefMut<'__delegate>,
{
fn as_mut_str(self) -> &'__delegate mut String {
match self {
Self::Left(__delegate) => { <__Left as AsString>::as_mut_str(__delegate ) }
Self::Right(__delegate) => { <__Right as AsString__DelegateRefMut<'__delegate>>::as_mut_str(__delegate ) }
}
}
}
// Implementation of mutable referenced part for end marker of "any enum's variant" type.
#[automatically_derived]
impl<'__delegate> AsString__DelegateRefMut<'__delegate>
for ::common::delegate::Void
where
Self: Sized + '__delegate,
{
fn as_mut_str(self) -> &'__delegate mut String {
match self {}
}
}
// Generic implementation of `AsString` trait for any type implementing `Convert` trait.
#[automatically_derived]
impl<__Delegate> AsString for __Delegate
where
__Delegate: ::common::delegate::Convert
+ ::common::delegate::Bound< // <--- bound implementation to avoid unexpected implementations.
{ AsString.0 },
{ AsString.1 },
{ AsString.2 },
{ AsString.3 },
>,
<__Delegate as ::common::delegate::Convert>::Owned:
AsString__DelegateOwned, // <--- owned variants of the enum must implement owned part of the trait.
for<'__delegate> <__Delegate as ::common::delegate::Convert>::Ref<'__delegate>:
AsString__DelegateRef<'__delegate>,
for<'__delegate> <__Delegate as ::common::delegate::Convert>::RefMut<'__delegate>:
AsString__DelegateRefMut<'__delegate>,
{
fn into_string(self) -> String {
<<__Delegate as ::common::delegate::Convert>::Owned as AsString__DelegateOwned>::into_string(
<Self as ::common::delegate::Convert>::convert_owned(self)
)
}
fn as_str(&self) -> &str {
<<__Delegate as ::common::delegate::Convert>::Ref<'_> as AsString__DelegateRef<'_>>::as_str(
<Self as ::common::delegate::Convert>::convert_ref(self)
)
}
fn as_mut_str(&mut self) -> &mut String {
<<__Delegate as ::common::delegate::Convert>::RefMut<'_> as AsString__DelegateRefMut<'_>>::as_mut_str(
<Self as ::common::delegate::Convert>::convert_ref_mut(self)
)
}
}
// Constantly check predicates of `AsString` doesn't contains `Self: NotMarkerTrait`.
#[automatically_derived]
const _: fn() = || {
struct OnlyMarkerSelfBoundsSupportedForNow;
fn assert_impl_all<T: Sized>() {}
assert_impl_all::<OnlyMarkerSelfBoundsSupportedForNow>();
};
}
Full expanded code can be found here: playground
delegate
macro supports delegation in both directions.
2. In some cases trait (or enum) can be a private item in some module. In such situations it will be great to pass arguments through attribute on private item to avoid leaking it.
Delegating for types
#[delegate]
pub enum Name {
First(String),
Last(String),
}
mod private_magic_of_the_crate {
#[delegate(for(super::Name))]
trait MagicTrait {
fn do_something(self) -> bool;
}
// Usages of `MagicTrait`.
}
Deriving delegation
#[delegate]
pub trait AsString {
fn as_str(&self) -> &str;
}
impl AsString for String { ... }
mod private_magic_of_the_crate {
#[delegate(derive(super::AsString))]
enum Name {
First(String),
Last(String),
}
// Usages of `Name::as_str`.
}
delegate
macro supports newtype structs.
3. Delegation can also be useful in cases with nested types.
Example
#[delegate]
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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
#[delegate(derive(AsString))]
struct FirstName(String);
delegate
macro supports one-fielded enums/structs.
4. For better ergonomics sometimes library users my prefer a named field over an unnamed one.
Example
#[delegate]
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) -> String {
self
}
fn as_str(&self) -> &str {
self
}
fn as_mut_str(&mut self) -> &mut String {
self
}
}
#[delegate(derive(AsString))]
struct LastName {
name: String, // <--- can be used it type methods as `self.name` without additional annotations.
}
enum_delegate
macro
5. Conflict conversion functionality in enum_delegate
on enums expands with additional conversion impls that will conflict with manual/another macro implementations.
Source code
#[enum_delegate::implement(b::AsString)]
#[derive(derive_more::From)] // conflict.
enum Name {
First(FirstName),
Last(LastName),
}
struct FirstName(String);
struct LastName(String);
Expanded `enum_delegate` macro
enum Name {
First(FirstName),
Last(LastName),
}
#[allow(non_snake_case)]
mod enum_delegate_helpers_AsString_for_Name {
#[doc = r" Indicates that a pair of types (A, B) are the same"]
#[doc = r""]
#[doc = r" Used in generated where-clauses as a workaround for https://github.com/rust-lang/rust/issues/20041"]
pub trait EqualTypes: private::Sealed {}
impl<T> EqualTypes for (T, T) {}
mod private {
pub trait Sealed {}
impl<T> Sealed for (T, T) {}
}
}
impl From<FirstName> for Name { fn from(inner: FirstName) -> Self { Name::First(inner) } }
impl TryInto<FirstName> for Name {
type Error = ();
fn try_into(self) -> Result<FirstName, Self::Error> {
match self {
Name::First(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl<'a> TryInto<&'a FirstName> for &'a Name {
type Error = ();
fn try_into(self) -> Result<&'a FirstName, Self::Error> {
match self {
Name::First(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl<'a> TryInto<&'a mut FirstName> for &'a mut Name {
type Error = ();
fn try_into(self) -> Result<&'a mut FirstName, Self::Error> {
match self {
Name::First(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl From<LastName> for Name { fn from(inner: LastName) -> Self { Name::Last(inner) } }
impl TryInto<LastName> for Name {
type Error = ();
fn try_into(self) -> Result<LastName, Self::Error> {
match self {
Name::Last(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl<'a> TryInto<&'a LastName> for &'a Name {
type Error = ();
fn try_into(self) -> Result<&'a LastName, Self::Error> {
match self {
Name::Last(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl<'a> TryInto<&'a mut LastName> for &'a mut Name {
type Error = ();
fn try_into(self) -> Result<&'a mut LastName, Self::Error> {
match self {
Name::Last(inner) => Ok(inner),
_ => Err(()),
}
}
}
impl b::AsString for Name where {
fn into_string(self) -> String where Self: Test + 'static {
match self {
Name::First(target) => { b::AsString::into_string(target ).into() }
Name::Last(target) => { b::AsString::into_string(target ).into() }
}
}
fn as_str(&self) -> &str {
match self {
Name::First(target) => { b::AsString::as_str(target ).into() }
Name::Last(target) => { b::AsString::as_str(target ).into() }
}
}
fn as_mut_str(&mut self) -> &mut String {
match self {
Name::First(target) => { b::AsString::as_mut_str(target ).into() }
Name::Last(target) => { b::AsString::as_mut_str(target ).into() }
}
}
}
Non-design issues
enum_delegate
macro
1. Wrong relative path expansions in Source code
mod b {
pub(super) trait Test {}
#[enum_delegate::register]
pub(super) trait AsString {
fn into_string(self) -> String
where
Self: Test + 'static;
fn as_str(&self) -> &str;
fn as_mut_str(&mut self) -> &mut String;
}
}
#[enum_delegate::implement(b::AsString)]
enum Name {
First(FirstName),
Last(LastName),
}
Compile error
|
4 | #[enum_delegate::implement(b::AsString)]
| ---------------------------------------- in this procedural macro expansion
...
49 | fn into_string(self) -> String where Self: Test + 'static;
| ^^^^ not found in this scope
Conclusion
If you interested in adding something from described above in your project - let me know and we discuss the way I can start integrating it.
Roadmap
-
Core design integrations. (!1 (merged)) -
Api changes. (!2 (merged)) -
External types/traits support. (!3 (merged)) -
Advanced generics support ( #[enum_delegate::register(for<T> MyType<T>)]
/#[enum_delegate::implement(for<T> MyTrait<T>)
). (!3 (merged)) -
Basic associated types support (usage in methods). (???) -
Advanced associated types support (associated type bounds). (???) -
Support methods without receivers (static methods) for single-variant delegates. (???) -
Support Self
-bounds and supertraits in trait delegate. (???)