Macro index_vec::define_index_type
source · 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);