2 unstable releases
0.2.0 | May 9, 2020 |
---|---|
0.1.0 | May 8, 2020 |
#1984 in Rust patterns
270 downloads per month
16KB
153 lines
array-lit
Documentation · Crates.io · Lib.rs
Rust macros for array and Vec
literals with superpowers.
The macro for arrays is called arr!
; the macro for Vec
is called vec!
and shadows the macro from the standard library.
They allow creating an array or Vec
where only some elements are
specified, and the rest is set to a default value.
The following macro specifies three consecutive values at index 0:
let a = arr![0; 8; { [0]: [1, 2, 3] }];
assert_eq!(a, [1, 2, 3, 0, 0, 0, 0, 0]);
The square brackets are only needed when specifying multiple consecutive elements:
let a = arr![1; 8; { 6: 0 }];
assert_eq!(a, [1, 1, 1, 1, 1, 1, 0, 1]);
You can specify as many single and consecutive values as you like:
let a = arr![0; 8; {
6: 1, // 1 at index 6
[2]: [3, 4], // 3 and 4 starting from index 2
7: 5, // 5 at index 7
}];
assert_eq!(a, [0, 0, 3, 4, 0, 0, 1, 5]);
The familiar array syntax (arr![a; N]
and arr![a, b, c]
) is also
supported, so the vec!
macro from this crate is a drop-in replacement for
std::vec!
.
How does it work?
The macros generate a block that first creates a array or Vec
, and then
inserts the specified values:
arr![4; 10; { 0: 0, 1: 1 }]
// is expanded to
{
let mut arr = [4; 10]; // in the vec! macro, std::vec! is used
arr[0] = 0;
arr[1] = 1;
arr
}
If an array is inserted that is not of the form [a, b, c, ..]
, a loop is
used:
arr![4; 10; { [1]: [2; 4] }]
// is expanded to
{
let mut arr = [4; 10];
let mut i = 1;
let end = i + 4;
while i < end {
arr[i] = 2;
i += 1;
}
arr
}
This even works for slices, arrays and Vec
s created at runtime:
let my_slice = &[1, 2, 3, 4];
arr![4; 10; { [1]: my_slice }];
What about array lifetimes?
In trivial cases such as arr![3; 5]
, the 'static
lifetime is inferred
for array literals. This means that they have the exact same behavior as
normal array literals.
In the other cases, the 'static
lifetime is not inferred. This means that
the array literal is computed at runtime and doesn't have a fixed memory
location. It also means that the following
// does NOT compile!
fn return_temporary() -> &'static [i32; 4] {
&arr![0; 4; { 0: 1 }]
}
produces error[E0515]: cannot return reference to temporary value
. This
can be solved by assigning the literal to a const
or static
variable
first:
fn return_temporary() -> &'static [i32; 4] {
static ARR: &[i32; 4] = &arr![0; 4; { 0: 1 }];
ARR
}
Values assigned to a static
or const
variable must be constant. Due to
limitations in the compiler, macros that expand to loops aren't allowed
there:
// does NOT compile!
const ARR: [i32; 4] = arr![0; 16; { [0]: [1; 8] }];
// this is expanded to a loop ~~~~~~~~~~~^^^^^^
Note that const
enforces const evaluation, which means that the whole
array is included in the application binary. This might not be desirable if
the array is large.
Usage
Import the macros with
use array_lit::{arr, vec};
If you don't want to shadow std::vec!
, you can rename the macro:
use array_lit::{arr, vec as vector};
Importing the macros globally is also supported, although not recommended:
#[macro_use]
extern crate array_lit;
Custom indices
If you want to use your own Index
/IndexMut
implementation in these
macros, you probably need an extra pair of parentheses:
#[derive(Copy, Clone)]
struct S(bool);
/// Your custom index
struct Idx(usize);
impl std::ops::Index<Idx> for Vec<S> {
type Output = bool;
// etc.
}
impl std::ops::IndexMut<Idx> for Vec<S> {
// etc.
}
vec![S(true); 1000; { (Idx(16)): false }];
// parens needed ~~~~~^~~~~~~~^
no_std
support
This library supports no_std
, if default features are disabled.
This makes the vec!
macro unavailable.
Minimum required Rust version
Requires Rust 1.33.
License
Licensed under MIT or Apache 2.0 at your choice.