That the function I am trying to call, I could, if necessary allocate the memory in the Rust code as well, but there I do not know, how much I will need.
This is what I have... not compiling at the moment, but I am also not sure, whether it is the right approach:
error[E0308]: mismatched types
--> src/lib.rs:20:39
|
20 | let mut data = TestStruct{cp: 0, size: 0, float: 0.0, num: 0};
| ^ expected *-ptr, found usize
|
= note: expected type `*const i8`
found type `usize`
because 0 is not a pointer, but you can call std::ptr::null() instead (null_mut() for a *mut pointer).
Is that the only issue?
Be aware that only C++ can safely free the allocated buffer because it was created with new[]. You need to pass the pointer back to C++ so it can be deleted appropriately (or just leak it).
That was actually an important hint ;), I was not sure how close I was. With that I got that part working, my data types for the numbers do not fit yet, need to have a second look at it.
I was hoping that when my Struct in Rust goes out of scope, that the memory will be freed, leaking it, will not be an option in the real scenario I am going to have, as that code will be executed quite often.
In case I would want to allocate the memory in Rust, what would be the correct type for cp, so I can use it in C++?
Ok, so the above I got working (the number issues was, size should be a u64 instead of u8). So now I wanted to address the memory allocation issue and find a solution that the memory gets allocated in Rust. Here is my thought (not working):
The code compiles without errors, but when I execute the programm I get:
error: process didn't exit successfully: target\debug\test-dll-data.exe (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
By trying around, I know the violation is happening in the DLL code. So something is wrong with my data type, but at the moment I do not know what.
The C++ code expects a pointer, but you are providing an array of 15 zeros. I assume the access violation happens when the zero pointer is dereferenced. You need to pass a pointer instead. You could obtain a pointer by calling as_ptr(), but that requires that you make sure the array stays alive for the duration of the function. In particular, you wouldn't be able to declare the array inline.
Tangentially: you should use type aliases that are guaranteed to work for C interop, rather than guess at sizes, which is error-prone as you have discovered. std::os::raw contains c_float and c_int as well as c_char. The only one that's missing is size_t which is usize on all platforms. You can also use the libc crate, which contains size_t along with a lot of other useful things, and you might want to use it anyway if you're doing C FFI.
Also tangentially: strncpy is pretty tricky to use right, and while what you have works, it would be wrong if the destination buffer were shorter than the source string. Be more than the usual amount of careful when using strncpy.
Now, as for your problem: just because Rust can't deallocate the memory itself doesn't mean it can't deallocate it indirectly. It seems you need to call some C++ to figure out how much to allocate. There are a couple ways of dealing with this:
Define a function in Rust that can be called from C++ but returns a pointer to memory that can be released by Rust. Then inside test_func use that function instead of new char[15]. test_func will then allocate memory that can be freed by Rust (by turning it into a Vec, Box or String, and letting it go out of scope, for example). I'm sure there's some way to override the allocator that new[] uses; maybe you could do that. In this solution, Rust calls C++ which calls Rust.
Allocate the space in C++ using new, but also define a function in C++ that can be called from Rust and can be used to delete it. Call this function inside drop for TestStruct and pass it the pointer.
Which one works best for you basically depends on which parts of the interface you can control.
Thanks also for your tips, I actually was already considering the libc crate. Your first alternative looks a little complicate.... the second one might actually work in my case. With the working solution I would indeed make an educated guess about the maximal size, which would be ok. But for the learning purpose I will look into the your second approach as well !