是否有可能在Rust中运行时确定堆栈分配的数组大小?

时间:2015-01-09 11:43:39

标签: rust

在Rust中是否有等效的alloca来创建可变长度数组?

我正在寻找以下C99代码的等价物:

void go(int n) {
    int array[n];
    // ...
}

2 个答案:

答案 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个元素时使用堆栈
  • 移动到堆,否则,以避免炸毁堆栈

当然,它也总是为堆栈中的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并不能真正支持这一点。我已经相信您可以这样做,但显着会干扰优化。因此,我不知道有任何近期计划允许这样做。