Reputation: 753
I'm writing a rust library, and I have a struct (only my culprit field shown here). In many cases, I have actual serializable structs or other data being assigned to my_field
, but occasionally I will have no data, and therefore want to assign null
or no item.
I attempted to do this by returning like this (in functions that create these message structs): Message<[u8; 0]>
, and i have ffi functions that return this type, which translates to Message<uint8_t[0]>
as a return type for those functions in the generated header file (using cbindgen).
But, compiling a C++ program with array of size 0 gives the warning:
the size of an array must be greater than 0
What rust type / technique can I use to get around this? *const u8
is not serializable, Option
does not exist in C++
#[repr(C)]
#[derive(Serialize, Deserialize)]
pub struct Message<T: Serialize> {
pub my_field: T
}
Upvotes: 3
Views: 366
Reputation: 459
Consider using Message<Option<&u8>>
:
lib.rs
:
use serde::{Deserialize, Serialize};
#[repr(C)]
#[derive(Debug, Deserialize, Serialize)]
pub struct Message<T: Serialize> {
pub my_field: T,
}
#[no_mangle]
pub extern "C" fn get_my_field(msg: Message<Option<&u8>>) -> u8 {
println!("{:?}", msg);
println!("get_field {}", serde_json::to_string(&msg).unwrap());
let a = msg.my_field.unwrap_or(&0);
*a
}
#[no_mangle]
pub extern "C" fn new_message(my_field: Option<&'_ u8>) -> Message<Option<&'_ u8>> {
Message { my_field }
}
lib.h
generated by cbindgen
:
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
template<typename T>
struct Message {
T my_field;
};
extern "C" {
uint8_t get_my_field(Message<const uint8_t*> msg);
Message<const uint8_t*> new_message(const uint8_t *my_field);
} // extern "C"
main.cpp
:
#include <lib.h>
int main()
{
Message<const uint8_t *> msg;
msg.my_field = NULL;
printf("%d\n", get_my_field(msg));
const uint8_t my_field = 1;
msg.my_field = &my_field;
printf("%d\n", get_my_field(msg));
auto msg2 = new_message(NULL);
printf("%d\n", get_my_field(msg2));
auto msg3 = new_message(&my_field);
printf("%d\n", get_my_field(msg3));
return 0;
}
Output:
Message { my_field: None }
get_field {"my_field":null}
0
Message { my_field: Some(1) }
get_field {"my_field":1}
1
Message { my_field: None }
get_field {"my_field":null}
0
Message { my_field: Some(1) }
get_field {"my_field":1}
1
Upvotes: 0
Reputation: 8514
I think there are only two ways to get around this warning:
T
that has a 1 byte sized memory representation. You can still make sure that the serialized representation has 0 size by skipping all fields (or using custom de-/serializers):#[repr(C)]
#[derive(Serialize, Default)]
pub struct Nil {
#[serde(skip)]
_dummy: u8,
}
#[no_mangle]
pub extern "C" fn mk_Nil() -> Nil {
Default::default()
}
Upvotes: 1
Reputation: 1537
Because type trait Deserialize
is implemented on all types Option<T>
given T
implement Deserialize
, you can use Option<T>
to capture optional data. You can see documentation here
#[repr(C)]
#[derive(Serialize, Deserialize)]
pub struct Message<T: Serialize> {
pub my_field: Option<T>
}
Upvotes: 0