Compile-Time Growable Array: Vec
& String
for const
!
[Construe
] & [StrConstrue
] allow you to write const
functions that behave as though though they can collect items into a Vec
or &str
s into a String
, returning an array or &str
, whose length does not need to be known before the function is called.
The one caveat is that this is only possible for deterministic functions which, given the same inputs, will always return the same amount of items. This is because they will be invoked twice: once to determine the size of the buffer needed and once to collect its contents.
Some other restrictions (currently) apply:
- can't overwrite previous items or assign directly to an index
- items can only be added to the end (only .push()
)
- can't remove items from the buffer (e.g. no .pop()
)
- it's not possible to inspect the buffer during construction
- no fallback mode for use outside of const
contexts
Simple compile-time [&str
] concatenation using [StrConstrue
]:
```rust
use construe::{StrConstrue, construe};
/// Concatenate all &str
s into a single &str
const fn concatfor
in const
while let [s, rest @ ..] = slice {
slice = rest;
// by-value since there's no &mut
in const
strc = strc.push_str(s);
}
strc
}
construe!(const HELLO_WORLD: &str = concat(&["Hello", " ", "World", "!"]));
asserteq!(HELLOWORLD, "Hello World!"); ```
And a slightly more complicated example, using [Construe
]:
```rust
use construe::{Construe, construe};
/// Multiply each item in slice
x
times
const fn multiply.0
at the end
c = c.push(*item).0;
i += 1;
}
}
c
}
// as slice: construe!(const NUMBERS: &[u8] = multiply(&[1, 2, 3], 2)); assert_eq!(NUMBERS, [1, 1, 2, 2, 3, 3]);
// as array: construe!(const NUMBERSARRAY: [u8; _] = multiply(&[1, 2, 3], 2)); asserteq!(NUMBERS, &NUMBERS_ARRAY);
// or with &str
s:
construe!(const WORDS: &[&str] = multiply(&["hey", "you"], 2));
assert_eq!(WORDS, &["hey", "hey", "you", "you"]);
```
Some of the restrictions mentioned above may or may not be remedied in future versions (roughly in the order given).
However, if a specialized version of these types is required, it would be easiest for you to just write it yourself.
For instance, if your function will need to inspect, say, the last 4 items of the buffer during construction, it would be fairly easy to write a [Construe
] that holds a buffer of size 4 for the first run too.
Implementing pop()
where you don't need the result would be trivial, and if you do, it would be analogous to the previous case, as long as you can guarantee that a buffer of size N
can keep up (e.g. "pop()
won't be called more than once between push()
es").
If you can't make this guarantee it might still be possible to implement: you could run your function thrice, once to determine the size of the look-back buffer, then twice like a normal [Construe
].
Generally, I think the method used by this crate can be extended to make any computation work, given that you could implement an allocator: it would abort the computation prematurely if it runs out of space and ask for twice as much.
Then you need to invoke the computation at most log2(available_memory) times.
Probably better to just wait until const
support is better though.