I'm wrapping a C library in Rust, and many of its functions take parameters by pointers to structs, which themselves often have pointers to other structs. In the interest of reducing overhead, I'd like to provide the ability to cache the results of marshaling the Rust data into the C structs.
Here's an example of how the C library might expect some parameters:
#[repr(C)]
struct Foo {
x: i32,
y: f32
}
#[repr(C)]
struct Bar {
p_foo: *const Foo,
z: bool
}
And how I'd imagine an owning, "cached" version would look:
struct Cached {
foo: Option<Foo>,
bar: Bar
}
The p_foo
field of bar
would be constructed to point to Some
value within foo
, or a null pointer if there was None
.
The issue, here, of course, is that if a value of Cached
was to be moved, a straight memcpy
would be inappropriate and bar.p_foo
would additionally need to be redirected. This would be easy to ensure in C++, with its definable move semantics, but does Rust offer a solution besides "don't set bar.p_foo
until it's used"? While it would certainly work to do it that way, I don't imagine that these cached values will be moved more than (or even close to the frequency that) they are reused, and there is a bit of work involved to set up these pointers, especially if the nesting/chaining is deep/long. I'd also rather not Box
the substructures up on the heap.
To clarify, here's what I can write in C++, which I would like to replicate in Rust:
struct Foo {
int x;
float y;
};
struct Bar {
Foo const*pFoo;
bool z;
};
// bear with me while I conjure up a Maybe for C++
class Cached {
public:
// would have appropriate copy constructor/assignment
Cached(Cached &&other) {
m_foo = other.m_foo;
m_bar = other.m_bar;
if(m_foo.isJust()) {
m_bar.pFoo = &m_foo.value();
} // else already nullptr
}
// similar move assignment
private:
Maybe<Foo> m_foo;
Bar m_bar;
};