Note: This answer is a little off, it propose you to use an another data layout for your C code.
You could change your C structure to something like this:
typedef struct point {
int x;
int y;
} point;
typedef struct points {
size_t len;
point points[];
} points;
This is called a flexible array member, a very nice and unknown C feature, that allows you to only make one allocation. The typical use-case matches your case.
Also, even in C int is not a suitable type to represent a size, you should use size_t.
You should also use bindgen to handle FAM, it's provide useful function like as_slice().
Given the following C code:
typedef struct point {
int x;
int y;
} point;
typedef struct points {
size_t len;
point points[];
} points;
struct points *new_points(size_t len) {
struct points *points = malloc(sizeof *points + sizeof *points->points * len);
if (points) {
points->len = len;
}
return points;
}
It currently generate:
#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>);
impl<T> __IncompleteArrayField<T> {
#[inline]
pub fn new() -> Self {
__IncompleteArrayField(::std::marker::PhantomData)
}
#[inline]
pub unsafe fn as_ptr(&self) -> *const T {
::std::mem::transmute(self)
}
#[inline]
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
::std::mem::transmute(self)
}
#[inline]
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
::std::slice::from_raw_parts(self.as_ptr(), len)
}
#[inline]
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
}
}
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_str("__IncompleteArrayField")
}
}
impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
#[inline]
fn clone(&self) -> Self {
Self::new()
}
}
impl<T> ::std::marker::Copy for __IncompleteArrayField<T> {}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct point {
pub x: ::std::os::raw::c_int,
pub y: ::std::os::raw::c_int,
}
#[repr(C)]
#[derive(Debug)]
pub struct points {
pub len: usize,
pub points: __IncompleteArrayField<point>,
}
extern "C" {
pub fn new_points(len: usize) -> *mut points;
}
Some lines omitted
With this binding you can do in Rust side:
#[no_mangle]
pub fn print_points(points: &points) {
for point in unsafe { points.points.as_slice(points.len) } {
println!("{:?}", point);
}
}
as_ptr() allow to avoid the overhead of creating a temporary slice, so do as you like.
And in the C side:
#include <stdlib.h>
typedef struct point {
int x;
int y;
} point;
typedef struct points {
size_t len;
point points[];
} points;
struct points *new_points(size_t len);
void print_points(struct points *points);
int main(void) {
struct points *points = new_points(42);
int x = 0;
for (size_t i = 0; i < points->len; i++, x++) {
points->points[i] = (struct point){ .x = x, .y = -x};
}
print_points(points);
}
However remember that nothing of this is guarantee, you could run into a complete undefined behavior, be careful.
#[derive(Debug)]
#[repr(C)]
pub struct points {
count: c_int,
array_of_points: [point],
}
You tell to the compiler that array_of_points is a valid slice, but it's not so your code:
#[no_mangle]
pub fn do_something(all_points: &points) {
for i in 0..all_points.count {
let crr_point = &all_points.array_of_points[i as usize];
println!("{:?}", crr_point);
}
}
is completely undefined behavior. I don't think there is a way to create such thing in C side, I didn't find one.