How to convert a variable length C array to Rust?

I know that Rust does not support variable length arrays, but this leaves me wondering what to replace them, given that:

  • I don't want to allocate and free tiny Vec in a loop
  • The borrow controller does not allow me to move the code outside the loop
  • There are many limitations to fixed-size arrays, so I cannot figure out how to use them.

The C code that I convert processes the image, calling a callback on each line, passing a small array of pointers:

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); } 

My rust attempt ( 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); } 

But he rejected:

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

Unfortunately, this sounds exactly like I tried!

I tried using a fixed-size array, but Rust does not support generics on them, so I cannot populate it from an iterator, and I cannot populate them in a C-like loop, because the links made in the loop don't expect it.

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


Another approach using a memory fragment from a fixed-size array also fails:

 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 take img.chan as mutable more than once at a time

+6
source share
1 answer

arrayvec is a library that does what you are looking for. (Also, you probably want iter_mut and as_mut instead of iter and 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); } 

It allocates a fixed amount of memory (there are 4 elements) on the stack and behaves like a Vec , the size of which is limited (to the capacity indicated at compile time), but variable.

Most of the complexity in arrayvec is how to handle the launch of destructors for a variable number of elements. But since &mut _ does not have a destructor, you can also access a fixed-size array. But you must use unsafe code and be careful not to read uninitialized elements. (Fixed-size arrays do not implement FromIterator , which is used by Iterator::collect .)

( Mannequin )

 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]); } 

Change Insecure code can be replaced by:

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

Shorter initializer syntax [&mut []; 4] [&mut []; 4] is not applicable here because &mut [_] implicitly copied. Type annotations are necessary, so you do not get [&mut [_; 0]; 4] [&mut [_; 0]; 4] [&mut [_; 0]; 4] .

+4
source

Source: https://habr.com/ru/post/987819/


All Articles