Hi! The title is a bit gnarly, so I'll make things simpler here.

I have a struct `Jones([num_complex::Complex64; 4])`

and a function that returns `Vec<Jones>`

. I'd like to pass this to a Python caller via pyo3 as a `numpy::PyArray2<numpy::c64>`

, where the second dimension is always length 4 (in other words, each element of the `[Complex64; 4]`

becomes a column in a 2D array). `numpy::c64`

can be treated as `num_complex::Complex64`

(if they're already not the same), which in memory is essentially just two `f64`

s next to each other.

To my knowledge, the easiest and cheapest way to get there safely is via `ndarray`

with the following:

```
// `jones` has type `Vec<Jones>`
let jones: Array2<numpy::c64> = ndarray::Array1::from_iter(jones.into_iter().flat_map(|j| {
[
numpy::c64::from(j[0]),
numpy::c64::from(j[1]),
numpy::c64::from(j[2]),
numpy::c64::from(j[3]),
]
}))
.into_shape((az_rad.len(), 4))
.unwrap();
jones.into_pyarray(py) // `py` is one of the function args and isn't important.
```

Now, I'd like to check that this is a zero-cost abstraction. I've tried using `cargo asm`

, but... I can't read the assembly. It is likely, though, that some panic code is emitted for the `unwrap`

, and I would be OK with that if the rest of the code doesn't copy.

An unsafe way to do the same thing is:

```
let jones: Array2<numpy::c64> = unsafe {
let ptr = jones.as_mut_ptr() as *mut numpy::c64;
let len = jones.len();
let cap = jones.capacity();
std::mem::forget(jones);
let v = Vec::from_raw_parts(ptr, len * 4, cap * 4);
ndarray::Array2::from_shape_vec((v.len() / 4, 4), v).unwrap_unchecked()
};
Ok(jones.into_pyarray(py))
```

This is essentially what'd I'd do in C (if I was writing in C), but I'm not actually sure how sound this is. Can I assume that `v`

has a length and capacity 4x the original `jones`

vector? And, does this code avoid a useless copy that the above safe Rust code might be doing?

Thanks in advance.