macro_rules! define_varlen_newtype {
    (
        #[repr(transparent)]
        $(
            #[macro_derive($( $derive:ident ),*)]
        )?
        $(#[$attrs:meta])*
        $tyvis:vis struct $outer:ident $(< $( ( $($generics:tt)* ) ),* >)? ($fieldvis:vis $inner:ty);

        $(
            with signature: impl< $( ( $($generic_params:tt)* ) ),* > $ignored:ident < $( ($($generics_apply:tt)*) ),* > { _ }
        )?

        with init:
            $(#[$initattrs:meta])*
            $initvis:vis struct $init:ident < _ > ($initfieldvis:vis _);
        with inner_ref:
            $(#[$refattrs:meta])*
            $refvis:vis fn $ref:ident(&self) -> &_;
        with inner_mut:
            $(#[$mutattrs:meta])*
            $mutvis:vis fn $mut:ident(self: _) -> _;
    ) => { ... };
}
Expand description

Defines a newtype wrapper around a varlen type.

Examples

As an example, we’ll create a wrapper around Str that also tracks the number of UTF8 code points in the string. To ensure these stay in sync, we allow the users of our library to access the codepoint count in immutable form (read access) but not mutable form (write access). The newtype wrapper helps us ensure this visibility as desired:

use varlen::prelude::*;

define_varlen_newtype! {
    #[repr(transparent)]
    /// A string which counts UTF8 code points.
    pub struct CountedStr(
        Tup2<
            // Count of UTF8 code points in the str
            FixedLen<usize>,
            // Paylod
            Str
        >);

    with init: struct CountedStrInitImpl<_>(_);
    with inner_ref: fn inner(&self) -> &_;
    with inner_mut: fn inner_mut(self: _) -> _;
}

impl CountedStr {
    pub fn from_str<'a>(src: &'a str) -> impl 'a + Initializer<Self> {
        let count = src.chars().count();
        CountedStrInitImpl(
            tup2::Init(
                FixedLen(count),
                Str::copy(src),
            )
        )
    }

    pub fn char_count(&self) -> usize {
        self.inner().refs().0.0
    }

    pub fn str(&self) -> &str {
        &self.inner().refs().1[..]
    }
}

let s = VBox::new(CountedStr::from_str("hellö wörld"));
assert_eq!(11, s.char_count());
assert_eq!(13, s.str().len());

Generated items

The macro generates the following items:

  • The struct, exactly as you specified. This is CountedStr in the example above.
  • An initializer struct, with signature as specified. The type argument is expected to be an initializer for the inner type.
  • Two member functions, with signatures as above, for accessing the inner type immutably (inner_ref) and mutably (inner_mut).
  • Implementations of traits crate::VarLen and crate::Initializer for your newtype.

For all of these except the trait implementations, you must provide a signature in the macro invocation site. The signature can specify the name, visibility, and documentation for this item.

Using generics

Your type may use generics. However, the syntax diverges a little from standard Rust syntax, because of limitations of Rust’s macro_rules. The requirements are:

  • Every generic must be surrounded by parentheses in the type’s definition.

  • You must provide an explicit with signature clause to define the impl’s signatures.

The following example shows this in action:

use varlen::prelude::*;

define_varlen_newtype! {
    #[repr(transparent)]
    /// A string which counts UTF8 code points.
    pub struct TwoArrays<(T: Copy), (U: Clone = u16)>(
        pub Tup2<
            Array<T>,
            Array<U>,
        >);

    with signature: impl<(T: Copy), (U: Clone)> TwoArrays <(T), (U)> { _ }
    with init: pub struct TwoArraysInit<_>(_);
    with inner_ref: pub fn inner(&self) -> &_;
    with inner_mut: pub fn inner_mut(self: _) -> _;
}

let t: VBox<TwoArrays<u16, u8>> = VBox::new(TwoArraysInit(
    tup2::Init(
        Array::copy(&[1, 2, 3]),
        Array::copy(&[4, 5, 6, 7]),
    )
));
assert_eq!(&t.inner().refs().0[..], &[1, 2, 3]);
assert_eq!(&t.inner().refs().1[..], &[4, 5, 6, 7]);