Skip to main content

wrapper

Macro wrapper 

Source
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);
    );
  • where clauses 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.