pub unsafe trait LegacyCType: Sized + Copy + CType {
    type OPAQUE_KIND: T;

    fn c_short_name_fmt(fmt: &mut Formatter<'_>) -> Result;
    fn c_define_self(definer: &mut dyn Definer) -> Result<()>;
    fn c_var_fmt(fmt: &mut Formatter<'_>, var_name: &str) -> Result;
    fn csharp_define_self(definer: &mut dyn Definer) -> Result<()>;

    fn c_short_name() -> ImplDisplay<Self> { ... }
    fn c_var(var_name: &str) -> ImplDisplay<'_, Self> { ... }
    fn legacy_csharp_marshaler() -> Option<String> { ... }
    fn csharp_ty() -> String { ... }
    fn csharp_var(var_name: &str) -> String { ... }
}
Expand description

One of the two core traits of this crate (with ReprC).

CType is an unsafe trait that binds a Rust type to a C typedef.

To optimise compile-times, the C typedef part is gated behind the headers cargo feature, so when that feature is not enabled, the trait may “look” like a marker trait, but it isn’t.

That’s why manually implementing this trait is strongly discouraged, although not forbidden:

  • If you trully want a manual implementation of CType (e.g., for an “opaque type” pattern, i.e., a forward declaration), then, to implement the trait so that it works no matter the status of the safer_ffi/headers feature, one must define the methods as if feature was present, but with a #[::safer_ffi::cfg_headers] gate slapped on each method.

Safety

The Rust type in an extern "C" function must have the same layout and ABI as the defined C type, and all the bit-patterns representing any instance of such C type must be valid and safe bit-patterns for the Rust type.

For the most common types, there are only two reasons to correctly be a CType:

  • being a primitive type, such as an integer type or a (slim) pointer.

    • This crates provides as many of these implementations as possible.
  • and recursively, a non-zero-sized #[repr(C)] struct of CType fields.

    • the CType! macro can be used to wrap a #[repr(C)] struct definition to safely and automagically implement the trait when it is sound to do so.

Note that types such as Rust’s bool are ruled out by this definition, since it has the ABI of a u8 <-> uint8_t, and yet there are many bit-patterns for the uint8_t type that do not make valid bools.

For such types, see the ReprC trait.

Required Associated Types

Required Methods

A short-name description of the type, mainly used to fill “placeholders” such as when monomorphising generics structs or arrays.

This provides the implementation used by [CType::c_short_name]().

There are no bad implementations of this method, except, of course, for the obligation to provide a valid identifier chunk, i.e., the output must only contain alphanumeric digits and underscores.

For instance, given T : CType and const N: usize > 0, the type [T; N] (inline fixed-size array of N consecutive elements of type T) will be typedef-named as:

write!(fmt, "{}_{}_array", <T as CType>::c_short_name(), N)

Generally, typedefs with a trailing _t will see that _t trimmed when used as a short_name.

Implementation by CType!:

A non generic struct such as:

CType! {
    #[repr(C)]
    struct Foo { /* fields */ }
}

will have Foo as its short_name.

A generic struct such as:

CType! {
    #[repr(C)]
    struct Foo[T] where { T : CType } { /* fields */ }
}

will have Foo_xxx as its short_name, with xxx being T’s short_name.

Necessary one-time code for [CType::c_var]() to make sense.

Some types, such as char, are part of the language, and can be used directly by [CType::c_var](). In that case, there is nothing else to define, and all is fine.

  • That is the default implementation of this method: doing nothing.

But most often than not, a typedef or an #include is required.

In that case, here is the place to put it, with the help of the provided Definer.

Idempotent

Given some definer: &mut dyn Definer, the c_define_self(definer) call must be idempotent w.r.t. code generated. In other words, two or more such calls must not generate any extra code w.r.t the first call.

This is easy to achieve thanks to definer:

// This ensures the idempotency requirements are met.
definer.define_once(
    // some unique `&str`, ideally the C name being defined:
    "my_super_type_t",
    // Actual code generation logic, writing to `definer.out()`
    &mut |definer| {
        // If the typdef recursively needs other types being defined,
        // ensure it is the case by explicitly calling
        // `c_define_self(definer)` on those types.
        OtherType::c_define_self(definer)?;
        write!(definer.out(), "typedef ... my_super_type_t;", ...)
    },
)?
Safety

Given that the defined types may be used by [CType::c_var_fmt](), the same safety disclaimers apply.

Examples
i32

The corresponding type for i32 in C is int32_t, but such type definition is not part of the language, it is brought by a library instead: <stdint.h> (or <inttypes.h> since it includes it).

unsafe impl CType for i32 {
    #[::safer_ffi::cfg_headers]
    fn c_define_self (definer: &'_ mut dyn Definer)
      -> io::Result<()>
    {
        definer.define_once("<stdint.h>", &mut |definer| {
            write!(definer.out(), "\n#include <stdint.h>\n")
        })
    }

    // ...
}
#[repr(C)] struct Foo { x: i32 }
#[repr(C)]
struct Foo {
    x: i32,
}

unsafe impl CType for i32 {
    #[::safer_ffi::cfg_headers]
    fn c_define_self (definer: &'_ mut dyn Definer)
      -> io::Result<()>
    {
        definer.define_once("Foo_t", &mut |definer| {
            // ensure int32_t makes sense
            <i32 as CType>::c_define_self(definer)?;
            write!(definer.out(),
                "typedef struct {{ {}; }} Foo_t;",
                <i32 as CType>::c_var("x"),
            )
        })
    }

    // ...
}

The core method of the trait: it provides the implementation to be used by [CType::c_var], by bringing a Formatter in scope.

This provides the implementation used by [CType::c_var]().

The implementations are thus much like any classic Display impl, except that:

  • it must output valid C code representing the type corresponding to the Rust type.

  • a var_name may be supplied, in which case the type must use that as its “variable name” (C being how it is, the var name may need to be inserted in the middle of the types, such as with arrays and function pointers).

Safety

Here is where the meat of the safety happens: associating a Rust type to a non-corresponding C definition will cause Undefined Behavior when a function using such type in its ABI is called.

Examples
i32
unsafe impl CType for i32 {
    #[::safer_ffi::cfg_headers]
    fn c_var_fmt (
        fmt: &'_ mut fmt::Formatter<'_>,
        var_name: &'_ str,
    ) -> fmt::Result
    {
        write!(fmt, "int32_t {}", var_name)
    }

    // ...
}
Option<extern "C" fn (i32) -> u32>
unsafe impl CType for Option<extern "C" fn (i32) -> u32> {
    #[::safer_ffi::cfg_headers]
    fn c_var_fmt (
        fmt: &'_ mut fmt::Formatter<'_>,
        var_name: &'_ str,
    ) -> fmt::Result
    {
        write!(fmt, "uint32_t (*{})(int32_t)", var_name)
    }

    // ...
}
[i32; 42]
unsafe impl CType for [i32; 42] {
    #[::safer_ffi::cfg_headers]
    fn c_var_fmt (
        fmt: &'_ mut fmt::Formatter<'_>,
        var_name: &'_ str,
    ) -> fmt::Result
    {
        let typedef_name = format_args!("{}_t", Self::c_short_name());
        write!(fmt, "{} {}", typedef_name, var_name)
    }

    // Since `c_var_fmt()` requires a one-time typedef, overriding
    // `c_define_self()` is necessary:
    #[::safer_ffi::cfg_headers]
    fn c_define_self (definer: &'_ mut dyn Definer)
      -> fmt::Result
    {
        let typedef_name = &format!("{}_t", Self::c_short_name());
        definer.define_once(typedef_name, &mut |definer| {
            // ensure the array element type is defined
            i32::c_define_self(definer)?;
            write!(definer.out(),
                "typedef struct {{ {0}; }} {1};\n",
                i32::c_var("arr[42]"), // `int32_t arr[42]`
                typedef_name,
            )
        })
    }

    // etc.
}

Extra typedef code (e.g. [LayoutKind.Sequential] struct ...)

Provided Methods

Convenience function for callers / users of types implementing CType.

The Display logic is auto-derived from the implementation of [CType::c_short_name_fmt]().

Convenience function for callers / users of types implementing CType.

The Display logic is auto-derived from the implementation of [CType::c_var_fmt]().

Optional marshaler attached to the type (e.g., [MarshalAs(UnmanagedType.FunctionPtr)])

Type name (e.g., int, string, IntPtr)

Convenience function for formatting {ty} {var} in CSharp.

Implementations on Foreign Types

Simplified for lighter documentation, but the actual impls include up to 9 function parameters.

Simplified for lighter documentation, but the actual impls include up to 9 function parameters.

Simplified for lighter documentation, but the actual impls include up to 9 function parameters.

Simplified for lighter documentation, but the actual impls range from 1 up to 32, plus a bunch of significant lengths up to 1024.

Simplified for lighter documentation, but the actual impls range from 1 up to 32, plus a bunch of significant lengths up to 1024.

Implementors