在Rust中是否有类似alloca
的等效函数可以创建可变长度数组?
我正在寻找以下C99代码的等效函数:
void go(int n) {
int array[n];
// ...
}
在Rust中是否有类似alloca
的等效函数可以创建可变长度数组?
我正在寻找以下C99代码的等效函数:
void go(int n) {
int array[n];
// ...
}
这是不可能的直接实现的,因为语言中没有直接支持它的语法。
话虽如此,C99的这个特性是有争议的,它有一定的优点(缓存局部性和绕过malloc
),但也有缺点(容易引起栈溢出,阻碍一些优化,可能将静态偏移转换为动态偏移等)。
目前,我建议你使用Vec
。如果你有性能问题,那么可以考虑所谓的“小向量优化”。在需要性能的C代码中,我经常看到以下模式:
SomeType array[64] = {};
SomeType* pointer, *dynamic_pointer;
if (n <= 64) {
pointer = array;
} else {
pointer = dynamic_pointer = malloc(sizeof(SomeType) * n);
}
// ...
if (dynamic_pointer) { free(dynamic_pointer); }
enum InlineVector<T, const N: usize> {
Inline(usize, [T; N]),
Dynamic(Vec<T>),
}
enum SmallVector<T, const N: usize> {
Inline(usize, [T; N]),
Dynamic(Vec<T>),
}
impl<T: Copy + Clone, const N: usize> SmallVector<T, N> {
fn new(v: T, n: usize) -> Self {
if n <= N {
Self::Inline(n, [v; N])
} else {
Self::Dynamic(vec![v; n])
}
}
}
impl<T, const N: usize> SmallVector<T, N> {
fn as_slice(&self) -> &[T] {
match self {
Self::Inline(n, array) => &array[0..*n],
Self::Dynamic(vec) => vec,
}
}
fn as_mut_slice(&mut self) -> &mut [T] {
match self {
Self::Inline(n, array) => &mut array[0..*n],
Self::Dynamic(vec) => vec,
}
}
}
use std::ops::{Deref, DerefMut};
impl<T, const N: usize> Deref for SmallVector<T, N> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T, const N: usize> DerefMut for SmallVector<T, N> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
使用方法:
fn main() {
let mut v = SmallVector::new(1u32, 4);
v[2] = 3;
println!("{}: {}", v.len(), v[2])
}
这将按预期输出4:3
。
Array
特性仅实现了部分大小的子集,并且使用起来有些繁琐。虽然不是最好的选择,但显然是缺少常量泛型的权宜之计。 - Matthieu M.Inline
变体的切片实现不应该使用该变体的大小作为边界吗?这里的示例代码创建了一个整个长度为 64 的数组的切片。因此,该切片将具有错误的长度。 - dubiousjim[MaybeUninit<T>; 64]
,但现在我会把它留出来。最后,目前正在进行工作以便能够将内联存储传递给 rustc 的 std 结构,但那还只是原型阶段。 - Matthieu M.不行。
在 Rust 中这样做需要能够将类似 [i32]
的动态尺寸类型 (DSTs) 存储在堆栈上,而该语言不支持这种能力。
更深层次的原因是 LLVM,在我看来,它并没有真正支持此功能。我认为你可以这样做,但它会显著干扰优化。因此,我不知道有任何近期允许此操作的计划。
%vla = alloca <type>, <ty> <NumElements>
而不是 %sla = alloca [<NumElements> x <type>]
(以及等效的编程 AllocaInst
调用)。 - JAB
alloca
不是真正的函数,它更像是一个编译器内置函数。至少在LLVM中,它是一条实际的IR指令,所以没有直接的“导入”方式。你需要能够编写内联IR,但目前你还不能这样做。 - DK.alloca
不可能是一个常规函数。函数只能更改自己的堆栈帧,不能更改其父堆栈帧。手册页面中的注释部分指出,“函数”取决于编译器和机器。这就是为什么。 - Tyler