Hello.
I have trouble binding C structs/unions with this structured enum in https://rust-unofficial.github.io/patterns/idioms/ffi/errors.html#structured-enums. That is, my question is how to implement them in C.
pub mod errors {
pub enum DatabaseError {
IsReadOnly,
IOError(std::io::Error),
FileCorrupted(String),
}
}
I've written a simplified version (with a flat enum):
lib.rs
pub mod errors {
#[derive(Clone, Copy)]
#[repr(C)]
pub enum DatabaseError {
IsReadOnly = 1,
IOError = 2,
FileCorrupted = 3,
}
}
impl From<errors::DatabaseError> for libc::c_int {
fn from(error: errors::DatabaseError) -> libc::c_int {
(error as i8).into()
}
}
pub mod c_api {
use super::errors::DatabaseError;
#[no_mangle]
pub unsafe extern "C" fn db_error_code(error: *const DatabaseError) -> libc::c_int {
let db_error: &DatabaseError = unsafe {
// SAFETY: the pointer's lifetime is greater than the current stack frame.
&*error
};
let res: libc::c_int = libc::c_int::from(*db_error);
res
}
}
#[cfg(test)]
mod tests {
use super::{c_api, errors::DatabaseError};
fn assert_db_error_code(error: DatabaseError, expected_result: i32) {
let result = unsafe { c_api::db_error_code(&error) };
assert_eq!(expected_result, result);
}
#[test]
fn db_error_as_is_read_only() {
assert_db_error_code(DatabaseError::IsReadOnly, 1);
}
#[test]
fn db_error_as_io_error() {
assert_db_error_code(DatabaseError::IOError, 2);
}
#[test]
fn db_error_as_file_corrupted() {
assert_db_error_code(DatabaseError::FileCorrupted, 3);
}
}
client.c
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
enum DatabaseError {
IsReadOnly = 1,
IOError = 2,
FileCorrupted = 3,
};
extern int32_t
db_error_code(enum DatabaseError *);
void assert_db_error_code(enum DatabaseError arg, int32_t expected_result)
{
int32_t result = db_error_code(&arg);
assert(expected_result == result);
}
void test_api()
{
{
enum DatabaseError arg = IsReadOnly;
int32_t expected_result = 1;
assert_db_error_code(arg, expected_result);
}
{
enum DatabaseError arg = IOError;
int32_t expected_result = 2;
assert_db_error_code(arg, expected_result);
}
{
enum DatabaseError arg = FileCorrupted;
int32_t expected_result = 3;
assert_db_error_code(arg, expected_result);
}
}
int main(void)
{
enum DatabaseError arg = IsReadOnly;
int32_t result = db_error_code(&arg);
printf("(result: %" PRIu32 ")\n",
result);
test_api();
return 0;
}