是否有相当于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个元素保留足够的空间,即使你只使用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按预期.