Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding Rust Vec<[T; N]> to C++ using cxx

Tags:

c++

rust

I'm writing a Rust wrapper for a C++ library using cxx.

One frequently encountered data type in this library are vectors of variable size, where each element is a vector of fixed size - e.g. an N-length vector containing N three-dimensional points, each made up of 3 doubles.

Looking at the cxx supported bindings, it seems like the obvious way is something like the below, binding Vec<[u64; 3]> to rust::Vec<std::array<uint64_t, 3>>:

// main.rs
include!("include/foo.h");
#[cxx::bridge]
pub mod ffi {

    unsafe extern "C++" {
        fn foo(x: Vec<[u64; 3]>);
    }
}
// foo.h
void foo(rust::Vec<std::array<uint64_t, 3>> x);

However, this produces the following error:

  error[cxxbridge]: unsupported element type of Vec
     ┌─ src/main.rs:17:19
     │
  17 │         fn foo(x: Vec<[u64; 3]>);
     │                   ^^^^^^^^^^^^^ unsupported element type of Vec

Solutions that do work, but aren't ideal:

  1. Creating a shared struct, like Point3D, and using Vec<Point3D>. Annoying because I'd need a new struct for every variation. It's also not clean, because (in the case of 3D points) I have data types in Rust and C++ already for representing points, so this type is really only used in the bridge.

  2. Flattening into a Vec<u64> and then unflattening to some Rust type. Fine if it's the only way, but feels a little messy.

I was hoping there would be a cleaner way, before I start repeating this pattern all over for all of the various methods I need to cover in my wrapper.

like image 713
chris838 Avatar asked Jan 23 '26 12:01

chris838


1 Answers

Unfortunately, C++ doesn't directly support vectors of arrays (Vec<[u64; 3]>). You can work around this limitation by defining a new wrapper type that encapsulates the fixed-size array.

#[cxx::bridge]
mod ffi {
    struct Point3D {
        data: [u64; 3],
    }

    extern "C++" {
        include!("include/foo.h");

        fn foo(x: Vec<Point3D>);
    }
}

impl From<[u64; 3]> for ffi::Point3D {
    fn from(arr: [u64; 3]) -> Self {
        ffi::Point3D { data: arr }
    }
}

impl From<ffi::Point3D> for [u64; 3] {
    fn from(point: ffi::Point3D) -> Self {
        point.data
    }
}

Then define the wrapper type in C++:

// foo.h
#include "rust/cxx.h"
#include <array>

struct Point3D {
    std::array<uint64_t, 3> data;
};

void foo(rust::Vec<Point3D> x);

Now implement the C++ function:

// foo.cpp
#include "foo.h"
#include <iostream>

void foo(rust::Vec<Point3D> x) {
    for (const auto& point : x) {
        std::cout << "Point: [" << point.data[0] << ", " << point.data[1] << ", " << point.data[2] << "]" << std::endl;
    }
}
like image 131
Abhishek Shivakumar Avatar answered Jan 25 '26 03:01

Abhishek Shivakumar