1

I am creating Rust bindings for a C library that defines lists of standard constant default values:

// C

typedef struct RMW_PUBLIC_TYPE rmw_qos_profile_t
{
  size_t depth;
  enum rmw_qos_reliability_policy_t reliability;
  // ...
} rmw_qos_profile_t;

enum RMW_PUBLIC_TYPE rmw_qos_reliability_policy_t
{
  RMW_QOS_POLICY_RELIABILITY_RELIABLE,
  RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
  // ...
};

// Global value that needs wrapping
static const rmw_qos_profile_t rmw_qos_profile_sensor_data =
{
  5,
  RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
  // ...
};

Using Bindgen, static Rust variables are generated:

// Rust

extern "C" {
    #[link_name = "\u{1}rmw_qos_profile_sensor_data"]
    pub static rmw_qos_profile_sensor_data: rmw_qos_profile_t;
}

but static global variables are highly inconvenient to work with in Rust, having to encase every access in an unsafe {} block. Especially when you do not need mutability.

I already wrapped the struct and enums in Rust:

// Rust

pub enum QoSReliabilityPolicy {
    Reliable = 0,
    BestEffort = 1,
}

impl From<rmw_qos_reliability_policy_t> for QoSReliabilityPolicy {
    fn from(raw: rmw_qos_reliability_policy_t) -> Self {
        match raw {
            rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_RELIABLE => QoSReliabilityPolicy::Reliable,
            rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT => QoSReliabilityPolicy::BestEffort,
        }
    }
}

pub struct QoSProfile {
    pub depth: usize,
    pub reliability: QoSReliabilityPolicy,
    // ...
}

impl From<rmw_qos_profile_t> for QoSProfile {
    fn from(qos_profile: rmw_qos_profile_t) -> Self {
        QoSProfile {
            depth: qos_profile.depth,
            reliability: qos_profile.reliability.into(),
            // ...
        }
    }
}

impl From<rmw_qos_profile_t> for QoSProfile {
    fn from(qos_profile: rmw_qos_profile_t) -> Self {
        QoSProfile {
            depth: qos_profile.depth,
            reliability: qos_profile.reliability.into(),
            // ...
        }
    }
}

Now, I am looking for a solution to expose the same pre-defined profiles, such as rmw_qos_profile_sensor_data, to my Rust users without having to duplicate the C values manually in Rust.

Currently I am duplicating the C code in Rust:

// Rust

// Works but unsatisfying
pub const QOS_PROFILE_SENSOR_DATA: QoSProfile = QoSProfile {
    depth: 5,
    reliability: QoSReliabilityPolicy::BestEffort,
    // ...
};

But this is not satisfying. When the upstream C library updates these values, users will experience inconsistent behaviour and bugs.

What are the possible solutions for conveniently wrapping these global constants ?

The ideal solution would:

  • Automatically update the values when the upstream C library changed
  • Expose global consts so that these values can be inlined by the compiler
  • If not possible, expose global immutable variables
  • If still not possible, at least not require unsafe

The problem that I have been facing is that, since static const C structures are stored in memory, they can't ben translated into a const so easily and this is probably why Bindgen translates it using the static keyword.

So, the possibilities that I can imagine, but don't know how to execute, are:

  • Have smarter parsing of the C code to generate Rust code ?
  • Use some form of macro ?
  • Initialize from the C lib's static memory in the prelude ?
  • Initialize from the C lib's static memory explicitly ?
  • Other solutions ?
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
deb0ch
  • 1,133
  • 2
  • 16
  • 25
  • Is there a key distinction between this question and [this one](https://stackoverflow.com/questions/60748960/how-do-i-convert-a-static-global-variable-generated-by-bindgen-into-a-global-con)? – E_net4 Mar 20 '20 at 14:49
  • My previous question is specifically about solving a compilation error for one particular solution attempt to this problem, error which as far as I understood is not even solvable. Here, however, I am giving broader context to ask for possible design tradeoffs, of how in general such a functionality could be wrapped in Rust in a way that is performant, maintainable and convenient for the end user. – deb0ch Mar 21 '20 at 14:13
  • Do the memory addresses of the `static const`s matter? Can the values of the `static const`s be hard-coded into the Rust code, or can they change between C library versions? – Solomon Ucko Mar 18 '22 at 17:43

0 Answers0