Skip to main content

wrapper

Macro wrapper 

Source
macro_rules! wrapper {
    ($($tt:tt)*) => { ... };
}
Expand description

Helper macro for building a wrapper type and implementing common traits for it.

§Basic usage

Write the wrapper type definition using standard struct syntax, then add #[wrapper(...)] attributes to automatically generate trait implementations for interoperability with the inner type:

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. When using the 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. The method will have the same visibility as the inner field.

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 type alone. The #[default(...)] attribute allows specifying default values for those other fields to work around this limitation.

wrapper_lite::wrapper!(
    struct Foobar {
        inner_field: &'static str,
        other_field: u8,
    }
);

const _: () = {
    let _ = Foobar::from_inner("Hello");
};
wrapper_lite::wrapper!(
    struct Foobar {
        inner_field: &'static str,
        #[default(0)]
        other_field: u8,
    }
);

const _: () = {
    let foobar = Foobar::from_inner("Hello");
    assert!(foobar.other_field == 0);
};

Both the from_inner method and the From trait implementation benefit from this.

§The #[wrapper(...)] attribute

Instructs the macro to automatically generate implementations of 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(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 either the inner type or a custom type, provided that the inner type coerces to the target via deref coercion.

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]>();

When #[wrapper(AsRef)] or #[wrapper(AsMut)] is specified, we will also generate associated method as_inner or as_inner_mut that returns a (mutable) reference to the inner value. These methods will have the same visibility as the wrapper struct. Additionally, since mutable references are not allowed in constant functions before Rust 1.83, and this library’s MSRV is 1.56, by default the associated method as_inner_mut we generate is not a const method, but we support the #[wrapper([const] AsMut)] syntax to make as_inner_mut a const method like as_inner as well.

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. A const associated constructor method with the same name from will also be generated.

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<()>, ()>();

    // This actually tests the generated `from` method.
    let _ = Wrapper::from("Hello");
};

§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 syntax wrapper! { ... } is equivalent to wrapper!( ... );. The parenthesized form is preferred, as it is better supported by rustfmt.

§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 with more than one token like ?Sized is not supported:

    wrapper_lite::wrapper!(
        //                           👇
        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. There is currently no workaround:

    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:

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 currently. 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.