ideas for propagating Option or Result to C code

Viewed 199

Writing a big library that will be called from C# via c interop. Trying to get a nice clean interface right from the start.

I wonder if anybody has ideas for propagating Option or Result to c callers. I like the rust way and would like to carry those semantics over if possible.

1 Answers

Based on the comment by Stargateur you can use some repr(C) struct and union combinations. Due to some limitations on union we require the underlying types to implement Copy.

#[repr(C)]
union COptionEnum<T>
where
    T: Copy,
{
    value: T,
    none: (),
}

#[repr(C)]
struct COption<T>
where
    T: Copy,
{
    discriminant: u32,
    value: COptionEnum<T>,
}

However these are not particularly ergonomic, so to ease the pain, you can provide conversions to and from Option. These look like this:

impl<T> From<Option<T>> for COption<T>
where
    T: Copy,
{
    fn from(v: Option<T>) -> COption<T> {
        match v {
            None => COption {
                discriminant: 0,
                value: COptionEnum { none: () },
            },
            Some(v) => COption {
                discriminant: 1,
                value: COptionEnum { value: v },
            },
        }
    }
}

impl<T> From<COption<T>> for Option<T>
where
    T: Copy,
{
    fn from(v: COption<T>) -> Option<T> {
        match v.discriminant {
            0 => None,
            1 => Some(unsafe { v.value.value }),
            _ => panic!("Invalid COption"),
        }
    }
}

Finally, you can use these in a relatively painless manner.

#[repr(C)]
#[derive(Copy, Clone)]
struct AType {
    a: i32,
    b: u64,
}

fn main() {
    let v1 = Some(AType { a: 1, b: 2 });
    let cv1: COption<AType> = v1.into();
    let w1: Option<AType> = cv1.into();
}

A complete copy is in the playground

Related