macro_rules! define_index_type {
    (
        $(#[$attrs:meta])*
        $v:vis struct $type:ident = $raw:ident;
        $($CONFIG_NAME:ident = $value:expr;)* $(;)?
    ) => { ... };
}
Expand description

Generate the boilerplate for a newtyped index struct, for use with IndexVec.

In the future, if the compile-time overhead of doing so is reduced, this may be replaced with a proc macro.

Usage

Standard

The rough usage pattern of this macro is:

index_vec::define_index_type! {
    // Note that isn't actually a type alias, `MyIndex` is
    // actually defined as a struct. XXX is this too confusing?
    pub struct MyIndex = u32;
    // optional extra configuration here of the form:
    // `OPTION_NAME = stuff;`
    // See below for details.
}

Note that you can use other index types than u32, and you can set it to be MyIndex(pub u32) as well. Currently, the wrapped item be a tuple struct, however (patches welcome).

Customization

After the struct declaration, there are a number of configuration options the macro uses to customize how the type it generates behaves. For example:

index_vec::define_index_type! {
    pub struct Span = u32;

    // Don't allow any spans with values higher this.
    MAX_INDEX = 0x7fff_ff00;

    // But I also am not too worried about it, so only
    // perform the asserts in debug builds.
    DISABLE_MAX_INDEX_CHECK = cfg!(not(debug_assertions));
}

Configuration options

This macro has a few ways you can customize it’s output behavior. There’s not really any great syntax I can think of for them, but, well.

MAX_INDEX = <expr producing usize>

Assert if anything tries to construct an index above that value.

By default, this is $raw_type::max_value() as usize, e.g. we check that our cast from usize to our wrapper is lossless, but we assume any all instance of $raw_type is valid in this index domain.

Note that these tests can be disabled entirely, or conditionally, with DISABLE_MAX_INDEX_CHECK. Additionally, the generated type has from_usize_unchecked and from_raw_unchecked functions which can be used to ignore these checks.

DISABLE_MAX_INDEX_CHECK = <expr>;

Set to true to disable the assertions mentioned above. False by default.

To be clear, if this is set to false, we blindly assume all casts between usize and $raw_type succeed.

A common use is setting DISABLE_MAX_INDEX_CHECK = !cfg!(debug_assertions) to avoid the tests at compile time

For the sake of clarity, disabling this cannot lead to memory unsafety – we still go through bounds checks when accessing slices, and no unsafe code should rely on on these checks (unless you write some, and don’t! only use this for correctness!).

DEFAULT = <expr>;

If provided, we’ll implement Default for the index type using this expresson.

Example:

index_vec::define_index_type! {
    pub struct MyIdx = u16;
    MAX_INDEX = (u16::max_value() - 1) as usize;
    // Set the default index to be an invalid index, as
    // a hacky way of having this type behave somewhat
    // like it were an Option<MyIdx> without consuming
    // extra space.
    DEFAULT = (MyIdx::from_raw_unchecked(u16::max_value()));
}
DEBUG_FORMAT = <expr>;

By default we write the underlying integer out in a Debug implementation with {:?}. Sometimes you’d like more info though. For example, the type of the index. This can be done via DEBUG_FORMAT:

index_vec::define_index_type! {
    struct FooIdx = usize;
    DEBUG_FORMAT = "Foo({})";
}
// Then ...
let v = FooIdx::new(10);
assert_eq!("Foo(10)", format!("{:?}", v));
DISPLAY_FORMAT = <expr>;

Similarly to DEBUG_FORMAT, we can implement Display for you. Unlike DEBUG_FORMAT, if you do not set this, we will not implement Display for the index type.

index_vec::define_index_type! {
    struct FooIdx = usize;
    DISPLAY_FORMAT = "{}";
    // Note that you can use both DEBUG_FORMAT and DISPLAY_FORMAT.
    DEBUG_FORMAT = "#<foo {}>";
}
// Then ...
let v = FooIdx::new(10);
assert_eq!("10", format!("{}", v));
assert_eq!("#<foo 10>", format!("{:?}", v));
IMPL_RAW_CONVERSIONS = true;

We always automatically implement From<usize> for YourIndex and From<YourIndex> for usize. We don’t do this for the “raw” type (e.g. u32 if your type is declared as struct FooIdx = u32;), unless you request it via this option. It’s an error to use this if your raw type is usize.

index_vec::define_index_type! {
    struct FooIdx = u32;
    IMPL_RAW_CONVERSIONS = true;
}

let as_index = FooIdx::from(5u32);
let as_u32 = u32::from(as_index);
assert_eq!(as_u32, 5);