3个版本
0.0.3 | 2023年8月9日 |
---|---|
0.0.2 | 2023年7月25日 |
0.0.1 | 2023年7月24日 |
#1866 in 数据结构
在 liter 中使用
34KB
477 行
construe
编译时增长数组:Vec
& String
for const
!
Construe
& StrConstrue
允许你编写 const
函数,这些函数的行为就像它们可以将项目收集到一个 Vec
或将 &str
放入一个 String
中,返回一个数组或 &str
,其长度在函数调用之前不需要知道。
唯一的缺点是,这仅适用于确定性函数,这些函数给定相同的输入,将始终返回相同数量的项目。这是因为它们将被调用两次:一次是为了确定所需的缓冲区大小,一次是为了收集其内容。
(目前)还有一些限制适用
- 不能覆盖先前的项目或直接赋值给索引
- 只能将项目添加到末尾(只有
.push()
) - 不能从缓冲区中删除项目(例如,没有
.pop()
) - 在构建期间无法检查缓冲区
- 没有用于
const
上下文之外的回退模式
示例
使用 StrConstrue
的简单编译时 &str
连接
use construe::{StrConstrue, construe};
/// Concatenate all `&str`s into a single `&str`
const fn concat<const N: usize>(mut slice: &[&str]) -> StrConstrue<N> {
let mut strc = StrConstrue::new();
// no `for` 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", "!"]));
assert_eq!(HELLO_WORLD, "Hello World!");
以下是一个稍微复杂一点的示例,使用Construe
use construe::{Construe, construe};
/// Multiply each item in `slice` `x` times
const fn multiply<const N: usize, T: Copy>(mut slice: &[T], x: usize)
-> Construe<T, N>
{
let mut c = Construe::new();
while let [item, rest @ ..] = slice {
slice = rest;
let mut i = 0;
while i < x {
// see Construe::push docs on why we need `.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 NUMBERS_ARRAY: [u8; _] = multiply(&[1, 2, 3], 2));
assert_eq!(NUMBERS, &NUMBERS_ARRAY);
// or with `&str`s:
construe!(const WORDS: &[&str] = multiply(&["hey", "you"], 2));
assert_eq!(WORDS, &["hey", "hey", "you", "you"]);
可能的改进
上述提到的某些限制可能在未来的版本中得到修复(大致按照给出的顺序)。
然而,如果需要这些类型的专用版本,最好的方法可能是自己编写。例如,如果您的函数在构造期间需要检查缓冲区的最后4个项目,那么编写一个在首次运行时也包含大小为4的缓冲区的Construe
就相当简单。
在不需要结果的情况下实现pop()
将是微不足道的,如果需要,它将与上一个案例类似,只要您可以保证大小为N
的缓冲区可以跟上(例如,“pop()
不会在两次push()
之间调用”)。如果您无法做出这个保证,仍然可能实现:您可以将您的函数运行三次,一次用于确定回溯缓冲区的大小,然后像正常的Construe
一样运行两次。
通常,我认为这个crate使用的方法可以扩展到实现任何计算,前提是您可以实现一个分配器:如果空间不足,它将提前终止计算并请求两倍的空间。然后您最多需要调用计算log2(可用内存)次。
不过,可能还是等到const
支持更好再说。