Hello Rust community,
I am rustifying a bit of my code base and I am trying to integrate a rust module into a larger C code base.
The C code base has a lot of dynamic lists, implemented as a struct with a length and a buffer:
#[repr(C)]
struct CSeq<T> {
len: usize,
buffer: *const T
}
My aim is to keep the Rust code base clean, so I do not want C types leaking into the Rust code. Therefore, I thought it would be a good approach to have thin wrappers which convert the C types into idiomatic Rust types and call functions with a Rust signature. I want to avoid copies and new allocations in these wrappers, but if I have to make them, so be it.
Since in my use case, the data is passed read-only to Rust, I thought the best implementation would be a slice, so I implemented Deref.
impl<T> std::ops::Deref for CSeq<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { std::slice::from_raw_parts(self.buffer, self.len) }
}
}
This feels very Rusty, as the wrappers are indeed thin and I can directly call the actual Rust functions which accept slices as input.
Now however, I have the situation that CSeq
can be nested, such as CSeq<CSeq<CSeq<u32>>>
. Let's for now look at this specific case.
First, I am wondering what the most idiomatic call signature from the Rust side would look like. I assume &[&[&[u32]]]
is ugly but most flexible (even though using that if you have a Vec<Vec<Vec<u32>>>
seems like a lot of pain as well).
Second, how do I ideally convert the nested CSeq into that format? Is there some magic one can use here, or is repeatedly creating Vecs of slices and then slices from these Vecs the way to go? In the end, I also want to avoid writing a lot of boiler plate code by hand for different nesting levels; is there some clever way to leverage the compiler here (like with Generics and Deref in the unnested case?) or is there at least a solution which I could readily put into a macro?
I would be very grateful for pointers into the right direction.