* I was using SWIG 15+20 years ago to bind C/C++ to Perl and Java and it still exists.*
(OK, who broke the * for italics markup?)
I've used it. It took the entire thing you want to call and generated one giant file of munged C. That's sort of where that idea takes you.
But the author is on to something. There's a C to Rust translator. It sucks, because it works by emulating C pointer arithmetic in unsafe Rust, using its own set of primitives along the lines of "offset this pointer by this much". Now you have ugly, unsafe Rust.
What's needed is something that infers the meaning of an ambiguous function call from the code. Something that reasons like this:
Function call:
int read(char* buf, size_t n)
Analysis:
Is "buf" an array, or a reference to one item?
Examine C code for "read". Is it ever used in an array context, like
subscripting or pointer arithmetic? Possible results are "no, definitely not an array", "yes, definitely an array", and "can't decide, code is too confusing". One would like the third case to be rare.
If it's an array, how big is it? Possible results are inferred from the highest subscript seen, as an expression. Is it an expression on some variable at the interface? If it can be determined that the length of buf is n, the function call can be changed to a Rust slice for which .len() will work.
Pointer arithmetic needs to be analyzed by symbolic execution, treating each pointer as an (array, pointer into array) pair. If the array associated with a pointer never changes during the lifetime of the pointer, tracked all the way down the call tree, then the pointer can be represented as a subscript.
Subarrays passed by pointer become slices.
This won't work all the time. Sometimes you'll need hints from the user.
It would be tempting to guess using something like GPT-3, doing the conversion to safe code, and seeing if it worked. Most of these things are idioms, which is how humans convert them. Translation can be wrong in two ways - subscripts going out of range and being caught, and things using too much memory because a worst case overestimated something.
All the heavy work is in figuring out the equivalent, but not identical, data representation. Once that's figured out, converting the executable code is reasonably straightforward. There's already a dumb C to Rust translator for most of that.