1

C file:

typedef struct point {
    int x;
    int y;
} point;

typedef struct points {
    int count;
    point *array_of_points;
} points;

Rust file:

#[derive(Debug)]
#[repr(C)]
pub struct point {
    x: c_int,
    y: c_int,
}

#[derive(Debug)]
#[repr(C)]
pub struct points {
    count: c_int,
    array_of_points: [point],
}

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

In my C file, I allocate a lot of struct point and add them to array_of_points, then I call the do_something function.

How do I get each single point in array_of_points in Rust?

Is way I defined the array_of_points array in Rust correct?

When I run it, this strange outcome appears:

point { x: 0, y: -952095696 }   
point { x: 32674, y: 101 }   

and so on.

3
  • I believe your question is answered by the answers of Pass a C array to a Rust function; How can I index C arrays in Rust?; How to represent a pointer to an array in Rust for C. If you disagree, please edit your question to explain the differences. Otherwise, we can mark this question as already answered. Commented Oct 29, 2018 at 17:03
  • @Shepmaster That target is too specific to passing an array to a Rust function, whereas I'd say that the main issue here is in the C binding type definition. Commented Oct 29, 2018 at 17:04
  • Note: don't confuse an array and a pointer in C. Commented Oct 30, 2018 at 7:12

2 Answers 2

4

That is undefined behaviour. In the Rust version of that type, the member array_of_points, of type point*, was translated to a Rust unsized slice [point], which is not equivalent nor compatible. By adding a member of type [point], you are suggesting that point has a variable number of trailing point objects directly after its first member count. This also makes points an unsized type (or dynamically sized type).

The memory layout of points in C should be the following:

[ int, point* ]
           |
            -->[ point, point, ...] // dynamically allocated

But that Rust definition was making this:

[ int, point, point, ... ]          // unknown compile time size

The member in points needs to be defined with a raw pointer:

#[derive(Debug)]
#[repr(C)]
pub struct points {
    count: c_int,
    array_of_points: *mut point,
}

Then do_something should either dereference the pointer by an offset to retrieve each point:

#[no_mangle]
pub fn do_something(all_points: &points) {
    for i in 0..all_points.count {
        unsafe {
            let crr_point = &*all_points.array_of_points.offset(i as isize);
            println!("{:?}", crr_point);
        }
    }
}

Or construct a proper Rust slice out of the given parts in points:

#[no_mangle]
pub fn do_something(all_points: &points) {
    let point_array = unsafe {
        std::slice::from_raw_parts(all_points.array_of_points, all_points.count as usize)
    };
    for crr_point in point_array {
        println!("{:?}", crr_point);
    }
}

Note how you need unsafe code in any of these cases.

See also:

Sign up to request clarification or add additional context in comments.

Comments

0

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.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.