2015-05-24 3 views
6

Я знаю, что Руст не поддерживает массивы переменной длины, но это оставляет мне интересно, что заменить их, при условии, что:Как конвертировать C массив переменной длины в Rust?

  • Я не хочу выделять и освобождать крошечные VEC в цикле
  • Заимствования проверка не позволяет мне двигаться код вне цикла
  • Есть много ограничений на массивах фиксированного размера, так что я не могу понять, как использовать их

кода с I Преобразование процессов в изображение путем вызова обратного вызова по каждой строке, pa ssing небольшого массива указателей:

float *tmp[img->channels]; // Small, up to 4 elements 
for(int y = 0; y < height; y++) { 
    for(int ch = 0; ch < img->channels; ch++) { 
     tmp[ch] = &img->channel[ch]->pixels[width * y]; 
    } 
    callback(tmp, img->channels); 
} 

Моей Rust попытки (example in playpen):

for y in 0..height { 
    let tmp = &img.channel.iter().map(|channel| { 
     &mut channel.pixels.as_ref().unwrap()[width * y .. width * (y+1)] 
    }).collect(); 
    callback(tmp); 
} 

Но это отвергнута:

a collection of type [&mut [f32]] cannot be built from an iterator over elements of type &mut [f32]

К сожалению, это звучит так же, как и то, что я пытался делать!

Я пробовал использовать массив фиксированного размера, но Rust не поддерживает дженерики на них, поэтому я не могу заполнить его из итератора, и я не могу заполнить их в C-подобном цикле, потому что ссылки, сделанные в цикле, не ожидают его.

the trait core::iter::FromIterator<&mut [f32]> is not implemented for the type [&mut [f32]; 4]


Другой подход с приемом кусочком памяти из массива фиксированного размера, также не:

let mut row_tmp: [&mut [f32]; 4] = unsafe{mem::zeroed()}; 
for y in 0..height { 
    row_tmp[0..channels].iter_mut().zip(img.chan.iter_mut()).map(|(t, chan)| { 
     *t = &mut chan.img.as_ref().unwrap()[(width * y) as usize .. (width * (y+1)) as usize] 
    }); 
    cb(&row_tmp[0..channels], y, width, image_data); 
} 

error: cannot borrow img.chan as mutable more than once at a time

ответ

4

arrayvec это библиотека, которая делает то, что вы ищете. (Кроме того, вы, вероятно, хотите iter_mut и as_mut вместо iter и as_ref.)

for y in 0..height { 
    let tmp: ArrayVec<[_; 4]> = img.channel.iter_mut().map(|channel| { 
     &mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)] 
    }).collect(); 
    callback(&tmp); 
} 

Он выделяет фиксированный объем памяти (здесь 4 элементов) в стеке, и ведет себя как Vec, размер которого ограничен (до к емкости, указанной во время компиляции), но переменной.

Большая часть сложности в arrayvec предназначена для работы с деструкторами, выполняющими различные количества элементов. Но так как &mut _ не имеет деструктора, вы также можете получить доступ к массиву фиксированного размера. Но вы должны использовать код unsafe и будьте осторожны, чтобы не читать неинициализированные элементы. (Массивы фиксированного размера не реализует FromIterator, что и Iterator::collect использует.)

(Playpen)

let n_channels = img.channel.len(); 
for y in 0..height { 
    let tmp: [_; 4] = unsafe { mem::uninitialized() } 
    for (i, channel) in img.channel.iter_mut().enumerate() { 
     tmp[i] = &mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)]; 
    } 
    // Careful to only touch initialized items... 
    callback(&tmp[..n_channels]); 
} 

Edit: В связи с небезопасными коды могут быть заменены:

let mut tmp: [&mut [_]; 4] = [&mut [], &mut [], &mut [], &mut []]; 

Более короткий синтаксис инициализатора [&mut []; 4] здесь не применяется, поскольку &mut [_] неявно копируется.Аннотации типа необходимы, чтобы вы не получили [&mut [_; 0]; 4].

+1

Это потрясающий Саймон, реквизит для создания его! –

Смежные вопросы