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

Dog*_*ert 23 rust

是否有相当于alloca在Rust中创建可变长度数组?

我正在寻找相当于以下C99代码:

void go(int n) {
    int array[n];
    // ...
}
Run Code Online (Sandbox Code Playgroud)

Mat*_* M. 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); }
Run Code Online (Sandbox Code Playgroud)

现在,这是Rust容易支持的东西(在某种程度上更好):

enum InlineVector<T> {
    Inline(usize, [T; 64]),
    Dynamic(Vec<T>),
}
Run Code Online (Sandbox Code Playgroud)

您可以在下面看到示例简化实现.

然而,重要的是你现在有一种类型:

  • 当需要少于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()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

fn main() {
    let mut v = InlineVector::new(1u32, 4);
    v[2] = 3;
    println!("{}: {}", v.len(), v[2])
}
Run Code Online (Sandbox Code Playgroud)

哪个打印4: 3按预期.

  • [smallvec](https://crates.io/crates/smallvec)箱子实现了这一点. (3认同)
  • [`smallvec`](https://docs.rs/smallvec/1.4.0/smallvec/) 板条箱提供了这一点。 (2认同)