macro_rules! wrapper {
($($tt:tt)*) => { ... };
}Expand description
Helper macro for integrating with the new-type idiom in Rust.
§Basic usage
Just write the wrapper type definition using Rust’s struct syntax, and then
add #[wrapper(...)] attributes to generate implementations of traits for
the wrapper type automatically.
wrapper_lite::wrapper!(
#[wrapper(AsRef)]
#[derive(Debug, Clone, Copy)]
pub struct Foobar([u8; 128]);
);
wrapper_lite::wrapper!(
#[wrapper(AsRef)]
#[derive(Debug, Clone, Copy)]
pub struct Barfoo {
inner: [u8; 128],
}
);This macro supports both tuple struct syntax and braced struct syntax. For braced struct syntax, the first field will be treated as the “inner” field.
§Associated constructor method: from_inner
This macro will generate a const associated constructor method from_inner
when applicable, which takes an instance of the inner type and returns an
instance of the wrapper struct.
mod inner {
wrapper_lite::wrapper!(
pub(crate) struct Foobar<'a>(&'a str);
);
}
const _: () = {
let _ = inner::FooBar::from_inner("Hello");
};mod inner {
wrapper_lite::wrapper!(
pub(crate) struct Foobar<'a>(pub(crate) &'a str);
);
}
const _: () = {
let _ = inner::Foobar::from_inner("Hello");
};§The #[default(...)] attribute
When there are other fields in the wrapper struct, it is not possible to construct an instance of the wrapper struct from the inner value alone.
wrapper_lite::wrapper!(
struct Foobar {
inner_field: &'static str,
other_field1: u8,
other_field2: u8,
}
);
const _: () = {
// ❌ Oh, we don't know what value to use for `other_field`!
let _ = Foobar::from_inner("Hello");
};The #[default(...)] attribute allows specifying default values for those
other fields to work around this issue.
wrapper_lite::wrapper!(
struct Foobar {
inner_field: &'static str,
#[default(6)]
other_field1: u8,
#[default(8)]
other_field2: u8,
}
);
const _: () = {
let foobar = Foobar::from_inner("Hello");
assert!(foobar.other_field1 == 6);
assert!(foobar.other_field2 == 8);
};Both the method from_inner and the From trait implementation
benefit from this.
§The #[wrapper(...)] attribute
Instructs the macro to generate implementations of some commonly used traits for the wrapper struct.
§#[wrapper(Debug)]
Implements trait Debug for the wrapper struct if the inner type
implements it.
wrapper_lite::wrapper!(
#[wrapper(Debug)]
struct Wrapper<'a>(&'a str);
);
assert_eq!(format!("{:?}", Wrapper { inner: "168" }), "\"168\"");§#[wrapper(DebugName)]
Implements trait Debug for the wrapper struct, but only prints the name
of the wrapper struct.
wrapper_lite::wrapper!(
#[wrapper(DebugName)]
struct Wrapper<'a>(&'a str);
);
assert_eq!(format!("{:?}", Wrapper { inner: "168" }), "Wrapper");§#[wrapper(Display)]
Implements trait Display for the wrapper struct if the inner type
implements it.
wrapper_lite::wrapper!(
#[wrapper(Display)]
struct Wrapper<'a>(&'a str);
);
assert_eq!(format!("{}", Wrapper { inner: "168" }), "168");§#[wrapper(DisplayName)]
Implements trait Display for the wrapper struct, but only prints the
name of the wrapper struct.
wrapper_lite::wrapper!(
#[wrapper(DisplayName)]
struct Wrapper<'a>(&'a str);
);
assert_eq!(format!("{}", Wrapper { inner: "168" }), "Wrapper");§#[wrapper(AsRef)] / #[wrapper(AsMut)] / #[wrapper(Borrow)] / #[wrapper(BorrowMut)] / #[wrapper(Deref)] / #[wrapper(DerefMut)]
Implements trait AsRef / AsMut / Borrow / BorrowMut /
Deref / DerefMut for the wrapper struct.
The target type can be omitted (defaults to the type of the inner field,
denoted as U), or it can be explicitly specified (denoted as T),
provided that &U can be automatically coerced to &T via deref coercions.
wrapper_lite::wrapper!(
#[wrapper(AsRef)]
#[wrapper(AsRef<[u8]>)]
struct Wrapper([u8; 42]);
);
const fn assert_as_ref<T, U>()
where
T: AsRef<U>,
U: ?Sized,
{
}
assert_as_ref::<Wrapper, [u8; 42]>();
assert_as_ref::<Wrapper, [u8]>();wrapper_lite::wrapper!(
#[wrapper(AsMut)]
#[wrapper(AsMut<[u8]>)]
struct Wrapper([u8; 42]);
);
const fn assert_as_mut<T, U>()
where
T: AsMut<U>,
U: ?Sized,
{
}
assert_as_mut::<Wrapper, [u8; 42]>();
assert_as_mut::<Wrapper, [u8]>();use core::borrow::Borrow;
wrapper_lite::wrapper!(
#[wrapper(Borrow)]
#[wrapper(Borrow<[u8]>)]
struct Wrapper([u8; 42]);
);
const fn assert_borrow<T, U>()
where
T: Borrow<U>,
U: ?Sized,
{
}
assert_borrow::<Wrapper, [u8; 42]>();
assert_borrow::<Wrapper, [u8]>();use core::borrow::{Borrow, BorrowMut};
wrapper_lite::wrapper!(
#[wrapper(BorrowMut)]
#[wrapper(BorrowMut<[u8]>)]
struct Wrapper([u8; 42]);
);
const fn assert_borrow<T, U>()
where
T: Borrow<U>,
U: ?Sized,
{
}
const fn assert_borrow_mut<T, U>()
where
T: BorrowMut<U>,
U: ?Sized,
{
}
assert_borrow::<Wrapper, [u8; 42]>();
assert_borrow_mut::<Wrapper, [u8; 42]>();
assert_borrow::<Wrapper, [u8]>();
assert_borrow_mut::<Wrapper, [u8]>();use core::ops::Deref;
wrapper_lite::wrapper!(
#[wrapper(Deref)]
struct WrapperA([u8; 42]);
);
wrapper_lite::wrapper!(
#[wrapper(Deref<[u8]>)]
struct WrapperB([u8; 42]);
);
const fn assert_deref<T, U>()
where
T: Deref<Target = U>,
U: ?Sized,
{
}
assert_deref::<WrapperA, [u8; 42]>();
assert_deref::<WrapperB, [u8]>();use core::ops::{Deref, DerefMut};
wrapper_lite::wrapper!(
#[wrapper(DerefMut)]
struct WrapperA([u8; 42]);
);
wrapper_lite::wrapper!(
#[wrapper(DerefMut<[u8]>)]
struct WrapperB([u8; 42]);
);
const fn assert_deref<T, U>()
where
T: Deref<Target = U>,
U: ?Sized,
{
}
const fn assert_deref_mut<T, U>()
where
T: DerefMut<Target = U>,
U: ?Sized,
{
}
assert_deref::<WrapperA, [u8; 42]>();
assert_deref_mut::<WrapperA, [u8; 42]>();
assert_deref::<WrapperB, [u8]>();
assert_deref_mut::<WrapperB, [u8]>();For #[wrapper(AsRef)] or #[wrapper(AsMut)], we will also generate an
associated method as_inner or as_inner_mut with the same visibility as
the wrapper struct. Since &mut in const was stabilized only as of Rust
1.83, and this crate’s MSRV is 1.56, the as_inner_mut method is not const
by default.
We add limited support for syntax #[wrapper([const] Trait)] as a temporary
workaround, and currently the only supported trait is AsMut. Once const
traits implementations are stabilized, this syntax may be further expanded
in its usage, but that seems a long way off.
wrapper_lite::wrapper!(
#[wrapper([const] AsMut)]
pub struct Wrapper<P>(pub(crate) P);
);
const fn test_const_as_inner_mut<P>(wrapper: &mut Wrapper<P>) {
let _ = wrapper.as_inner_mut();
}When #[wrapper(BorrowMut)] or #[wrapper(DerefMut)] is specified, the
corresponding Borrow or Deref implementation is generated
automatically. Combining them with an explicit #[wrapper(Borrow)] or
#[wrapper(Deref)] therefore results in a duplicate implementation error:
wrapper_lite::wrapper!(
#[wrapper(Borrow)]
#[wrapper(BorrowMut)]
pub struct Wrapper<P>(pub(crate) P);
);wrapper_lite::wrapper!(
#[wrapper(Deref)]
#[wrapper(DerefMut)]
pub struct Wrapper<P>(pub(crate) P);
);§#[wrapper(From)]
Implements trait From for the wrapper struct, allowing it to be
constructed from the inner type.
wrapper_lite::wrapper!(
#[wrapper(From)]
pub struct Wrapper<P>(pub(crate) P);
);
const fn assert_from<T, U>()
where
T: From<U>,
{
}
const _: () = {
assert_from::<Wrapper<()>, ()>();
};§Advanced usage
§#[repr(align(cache))]
You can use #[repr(align(cache))] to pad and align the wrapper type to the
length of a cache line. This is useful for performance optimization in
certain scenarios.
use core::mem::align_of;
wrapper_lite::wrapper!(
#[repr(align(cache))]
pub struct Foobar(u8);
);
#[cfg(target_arch = "x86_64")]
const _: () = {
assert!(align_of::<Foobar>() == 128);
};§Notes
§wrapper! { ... } vs. wrapper!( ... );
The parenthesized form is preferred, as it’s better supported by rustfmt.
§Limitation on specifying multiple traits in a single #[wrapper(...)]
Like derive attribute, only tokens that $($trait:ident),+ can represent
can be recognized as a list of traits to be implemented:
wrapper_lite::wrapper!(
// 👇 Oh, `AsRef<[T]>` is not an ident
#[wrapper(Debug, From, AsRef, AsRef<[T]>)]
pub struct Foobar<T>(pub [T; 16]);
);Instead:
wrapper_lite::wrapper!(
#[wrapper(Debug, From, AsRef)]
// 👇 Move to a dedicated `wrapper` attr.
#[wrapper(AsRef<[T]>)]
pub struct Foobar<T>(pub [T; 16]);
);§Limitation on generic parameters
Due to the inherent limitations of declarative macros, some complex syntax related to generic parameters is not supported.
-
A trait bound that lexically contains multiple tokens, like
?Sized, is not supported:ⓘwrapper_lite::wrapper!( // 👇 '?' + 'Sized', 2 tokens pub struct HolyMolyCow<'a, P: ?Sized>(&'a P); ); -
A trailing comma after the last generic parameter is not supported:
ⓘwrapper_lite::wrapper!( // 👇 pub struct HolyMolyCow<'a, P: Default,>(&'a P); ); -
whereclauses are not supported:ⓘwrapper_lite::wrapper!( pub struct HolyMolyCow<'a, P>(&'a P) where P: ?Sized + Clone; );
The #[bound(...)] attribute serves as a partial workaround for the
limitations above, at the cost of lacking rustfmt support for the content
specified in #[bound(...)].
wrapper_lite::wrapper!(
#[bound(P: ?Sized + Clone)]
pub struct HolyMolyCow<'a, P = ::core::marker::PhantomData<()>>(&'a P);
);§Limitation on conditional compilation attribute #[cfg(...)]
This macro can correctly handle conditional compilation attribute
#[cfg(...)] at the struct level. However, due to the inherent complexity
limitations of declarative macros, #[cfg(...)] on struct fields are not
supported now. This will affect scenarios involving the construction of
wrapper types, such as implementing the From trait, etc.