How to convert a Vec of Point2f into a mutable slice of f32?

Maybe this is trivial, but right now I would not know how to do this:

#[derive(Debug)]                                                                                        
struct Point2f {                                                                                        
    x: f32,                                                                                             
    y: f32,                                                                                             
}                                                                                                       
                                                                                                        
fn some_fn(to_swap: &mut [f32]) {                                                                       
    to_swap.swap(0_usize, 3_usize);                                                                     
}                                                                                                       
                                                                                                        
fn main() {                                                                                             
    let mut swap_me: Vec<Point2f> = Vec::new();                                                         
    swap_me.push(Point2f { x: 0.0, y: 1.0 });                                                           
    swap_me.push(Point2f { x: 2.0, y: 3.0 });                                                           
    println!("replace");                                                                                
    println!("{:?}", swap_me[0]);                                                                       
    println!("{:?}", swap_me[1]);                                                                       
    // some_fn(&mut swap_me[..]);                                                                       
    let tmp = swap_me[0].x;                                                                             
    swap_me[0].x = swap_me[1].y;                                                                        
    swap_me[1].y = tmp;                                                                                 
    // replace three lines above through function call                                                  
    assert!(swap_me[0].x == 3.0);                                                                       
    assert!(swap_me[1].y == 0.0);                                                                       
    println!("by");                                                                                     
    println!("{:?}", swap_me[0]);                                                                       
    println!("{:?}", swap_me[1]);                                                                       
}                                                                                                       

Can this be done via From and Into?

An inefficient way could be to iterate over the Vec and collect the into a flat Vec<f32> which you can then slice. (Experienced FMs may provide more efficient alternatives.)

let flat_arr: Vec<f32> = swap_me.iter().map(|p| vec![p.x, p.y]).flatten().collect();
// You can use &flat_arr or slice it as needed

However, if you wish to limit to just swapping the Point2f types then you could operate on &mut [Point2f] slice directly:

#[derive(Debug)]                                                                                        
struct Point2f {                                                                                        
    x: f32,                                                                                             
    y: f32,                                                                                             
}

trait SwapXY {
    fn swap_xy(&mut self, x_idx: usize, y_idx: usize);
}

impl SwapXY for &mut [Point2f] {
    fn swap_xy(&mut self, x_idx: usize, y_idx: usize) {
        let tmp = self[x_idx].x;
        self[x_idx].x = self[y_idx].y;
        self[y_idx].y = tmp;
    }
}



fn main() {                                                                                             
    let mut swap_me: Vec<Point2f> = Vec::new();                                                         
    swap_me.push(Point2f { x: 0.0, y: 1.0 });                                                           
    swap_me.push(Point2f { x: 2.0, y: 3.0 });
    
    println!("Before: {:?}", swap_me);
    
    let mut swap_me_slice = &mut swap_me[..];
    swap_me_slice.swap_xy(0, 1);
    
    println!("After: {:?}", swap_me);
    
}
1 Like

Hi @osrust, thanks for your answer. I solved my particular problem in a similar way (operating on &mut [Point2f] directly):

https://www.janwalter.org/doc/rust/src/pbrt/core/sampling.rs.html#275-308

But this works here only because the C++ counterpart was actually using that function only once:

> rg -tcpp LatinHypercube
samplers/stratified.cpp
68:            LatinHypercube(&sampleArray2D[i][j * count].x, count, 2, rng);

core/sampling.h
54:void LatinHypercube(Float *samples, int nSamples, int nDim, RNG &rng);

core/sampling.cpp
62:void LatinHypercube(Float *samples, int nSamples, int nDim, RNG &rng) {

I was just wondering if there is another way, because from what I have read so far the memory layout of a vector would be an owning pointer, followed by two usize values for the capacity and length of the Vec (on the stack), whereas the reference to the (mutable) slice should be a pointer (on the stack) into the same memory block (on the heap) with an additional size value (on the stack). The Point2f values would be (on the heap) just twice as many f32 values. Swapping any of the valid f32 values should be easy (and efficient), as long as you know how to index into them (and what that index really means, e.g. x- or y-coordinate). Anyway, problem solved, but maybe someone has another idea how to deal with such a situation...

I think this will work:

slice::from_raw_parts_mut(
    vec.as_mut_ptr().cast::<f32>(),
    vec.len() * 2,
)

But you should also tag your struct #[repr(C)], or else the layout (including field order) is not guaranteed to be predictable.

5 Likes

Yup, that's the right solution. I'd recommend moving that code to a function that takes &mut Vec and returns the slice, so that the lifetime of the Vec is still bound to lifetime of the returned slice. If that code is pasted somewhere in the middle of a larger function, the borrow checker won't know the Vec and the slice are related, and could allow incorrect use of the Vec or slice.

5 Likes