rust - una - que significa en matlab
¿matrices alloca/de longitud variable en Rust? (2)
¿Es posible tener matrices asignadas a la pila con el tamaño determinado en tiempo de ejecución en Rust?
Estoy buscando el equivalente del siguiente código C99:
void go(int n) {
int array[n];
// ...
}
No.
Hacer eso en Rust implicaría la capacidad de almacenar DST (tipos de tamaño dinámico) como [i32]
en la pila, que el idioma no es compatible.
Una razón más profunda es que LLVM, que yo sepa, en realidad no es compatible con esto. Me han dicho que puedes hacerlo, pero interfiere significativamente con las optimizaciones. Como tal, no estoy al tanto de ningún plan a corto plazo para permitir esto.
No es posible directamente , ya que no hay sintaxis directa en el idioma que lo soporta.
Dicho esto, esta característica particular de C99 es discutible, tiene ciertas ventajas (ubicación de caché y eludir malloc
) pero también tiene desventajas (es fácil hacer estallar la pila, limita una serie de optimizaciones, puede convertir las compensaciones estáticas en compensaciones dinámicas) , ...).
Por ahora, te aconsejaría que uses Vec
lugar. Si tiene problemas de rendimiento, entonces puede examinar la llamada "Optimización de Vector Pequeño". He visto regularmente en el código donde se requiere rendimiento el siguiente patrón (en 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); }
Ahora, esto es algo que Rust admite fácilmente (y mejor, en cierto modo):
enum InlineVector<T> {
Inline(usize, [T; 64]),
Dynamic(Vec<T>),
}
Puede ver un ejemplo de implementación simplista a continuación.
Lo que importa, sin embargo, es que ahora tienes un tipo que:
- usa la pila cuando se requieren menos de 64 elementos
- se mueve al montón de otra manera, para evitar explotar la pila
Por supuesto, también siempre reserva suficiente espacio para 64 elementos en la pila, incluso si solo usa 2; sin embargo, a cambio, no hay una llamada a alloca
para evitar el problema de tener compensaciones dinámicas para sus variantes.
Y, a diferencia de C, aún se beneficia del seguimiento de por vida, por lo que no puede devolver accidentalmente una referencia a la matriz asignada por la pila fuera de la función.
Nota: una implementación completa requeriría parámetros sin tipo para que pueda personalizar el 64
... pero Rust aún no está allí.
Mostraré los métodos más "obvios":
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(
FromIterator::from_iter(std::iter::repeat(v).take(n))
)
}
}
fn len(&self) -> usize {
match self {
&InlineVector::Inline(n, _) => n,
&InlineVector::Dynamic(ref vec) => vec.len(),
}
}
fn as_slice(&self) -> &[T] {
match self {
&InlineVector::Inline(n, ref array) => array.as_slice().slice_to(n),
&InlineVector::Dynamic(ref vec) => vec.as_slice(),
}
}
fn as_mut_slice(&mut self) -> &mut [T] {
match self {
&mut InlineVector::Inline(n, ref mut array) =>
array.as_mut_slice().slice_to_mut(n),
&mut InlineVector::Dynamic(ref mut vec) =>
vec.as_mut_slice(),
}
}
}
Uso:
fn main() {
let mut v = InlineVector::new(1u32, 4);
v.as_mut_slice()[2] = 3;
println!("{}: {}", v.len(), v.as_slice()[2])
}
Que imprime 4: 3
como se esperaba.
Como referencia, las importaciones / modificaciones requeridas:
#![allow(unstable)]
use std::iter::FromIterator;
use std::vec::Vec;