在Rust中是否有等效的alloca
来创建可变长度数组?
我正在寻找以下C99代码的等价物:
void go(int n) {
int array[n];
// ...
}
答案 0 :(得分:19)
直接是不可能的,因为在支持它的语言中没有直接的语法。
话虽如此,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); }
现在,这是Rust很容易支持的东西(在某种程度上更好):
enum InlineVector<T> {
Inline(usize, [T; 64]),
Dynamic(Vec<T>),
}
您可以在下面看到一个简单的示例实现。
然而,重要的是你现在有一个类型:
当然,它也总是为堆栈中的64个元素保留足够的空间,即使你只使用2个;但是作为交换,没有电话alloca
,所以你可以避免对你的变种进行动态抵消的问题。
与C相反,您仍然可以从终身跟踪中受益,这样您就不会在函数外部意外地返回对堆栈分配数组的引用。
注意:完整的实现需要非类型参数,以便您可以自定义64
...但Rust还没有。
我将展示最“明显”的方法:
enum InlineVector<T> {
Inline(usize, [T; 64]),
Dynamic(Vec<T>),
}
impl<T: Copy + Clone> InlineVector<T> {
fn new(v: T, n: usize) -> InlineVector<T> {
if n <= 64 {
InlineVector::Inline(n, [v; 64])
} else {
InlineVector::Dynamic(vec![v; n])
}
}
}
impl<T> InlineVector<T> {
fn len(&self) -> usize {
match self {
InlineVector::Inline(n, _) => *n,
InlineVector::Dynamic(vec) => vec.len(),
}
}
fn as_slice(&self) -> &[T] {
match self {
InlineVector::Inline(_, array) => array,
InlineVector::Dynamic(vec) => vec,
}
}
fn as_mut_slice(&mut self) -> &mut [T] {
match self {
InlineVector::Inline(_, array) => array,
InlineVector::Dynamic(vec) => vec,
}
}
}
use std::ops::{Deref, DerefMut};
impl<T> Deref for InlineVector<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T> DerefMut for InlineVector<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
用法:
fn main() {
let mut v = InlineVector::new(1u32, 4);
v[2] = 3;
println!("{}: {}", v.len(), v[2])
}
按预期打印4: 3
。
答案 1 :(得分:3)
没有
在Rust中执行此操作需要能够在堆栈中存储动态大小的类型(DST),例如[i32]
,语言不支持。
更深层次的原因是,据我所知,LLVM并不能真正支持这一点。我已经相信您可以这样做,但显着会干扰优化。因此,我不知道有任何近期计划允许这样做。