Native Rust way to deal with C's void* data

Hey everyone!

I am trying to rewrite a C project in Rust by rethinking every aspect of it. It's not a second system syndrome issue though, as I am doing this to improve my Rust coding by learning how to think in Rust itself.

And in this effort I have found and interesting situation where I am not quite sure how to treat those void pointers data. Look at this code:

// Image type, bpp always RGBA (32bit)
// NOTE: Data stored in CPU memory (RAM)
typedef struct Image {
    void *data;             // Image raw data
    int width;              // Image base width
    int height;             // Image base height
    int mipmaps;            // Mipmap levels, 1 by default
    int format;             // Data format (PixelFormat type)
} Image;

// Wave type, defines audio wave data
typedef struct Wave {
    unsigned int sampleCount;       // Total number of samples
    unsigned int sampleRate;        // Frequency (samples per second)
    unsigned int sampleSize;        // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
    unsigned int channels;          // Number of channels (1-mono, 2-stereo)
    void *data;                     // Buffer data pointer
} Wave;

typedef struct Music {
    AudioStream stream;             // Audio stream
    unsigned int sampleCount;       // Total number of samples
    bool looping;                   // Music looping enable

    int ctxType;                    // Type of music context (audio filetype)
    void *ctxData;                  // Audio context data, depends on type
} Music;

I wonder what would be the safest, 'no FFI' and best performant solution to deal with cases like that :thinking:

struct Image { data, format, other }

translates to

struct Image { data: ImageData, other }
enum ImageData { Format1(data), Format2(data), etc }

and the data can be typed, i.e. instead of char* you can use struct RGB/RGBA, etc..

If the type is statically known, you can use generics for it:

struct Music<SomeData> {
    context: SomeData,

And for cases where void* is used as a context for callbacks, use closures instead. impl FnMut() in Rust.

1 Like

Whenever you see something like...

struct Foo {
  int format;
  void *data;

... You want to reach for a Rust enum.

The only way Foo's data field can be used is by switch-ing on the format field and casting data to the appropriate type. If you squint, you'll see that this is just a poor man's match.