I found this interesting problem. I first implemented this as a trait, then I started getting undefined behaviour. Then I noticed that it might be because I was serializing, then getting the pointer to a vector that does not exist anymore. I had the idea then of doing a macro, because then it's text substitution:
#[macro_export]
macro_rules! as_buffer {
($e:expr) => {
{
let t = || -> Result<(*const u8, usize), AsBufferError> {
let mut buffer: Vec<u8> = Vec::new();
$e.serialize(&mut buffer)?;
let buffer_pointer = buffer.as_slice().as_ptr();
Ok((buffer_pointer, buffer.len()))
};
t()
}
};
}
pub use as_buffer;
but I accidentally created a new scope, for which the vector also vanishes.
Is there a way to do this stuff in a macro such that the vector continues alive so I can use the pointer right after the let (pointer, len) = as_buffer(my_struct).unwrap()?
The let buffer_pointer = buffer.as_slice().as_ptr() bit means you'll return a reference to the data managed by buffer, but buffer is automatically dropped when the closure returns. This is a textbook use-after-free and if you were using references instead of pointers the compiler would have told you what is wrong.
The correct solution is to hoist buffer into a higher scope and make sure no pointers to it outlive the buffer variable.
Why do you want a pointer to a serialized version of your struct in the first place?
yes, I noticed that, but I don't know how to do it in a macro, because I need to put the thing on the macro inside {} which creates a new scope. I tried:
#[macro_export]
macro_rules! as_buffer {
($e:expr) => {
{
let mut buffer: Vec<u8> = Vec::new();
$e.serialize(&mut buffer).unwrap();//temporary unwrap, dont know what to do here. The closure was so I could get either Ok or Err, but anyways
let buffer_pointer = buffer.as_slice().as_ptr();
Result::<(*const u8, usize), AsBufferError>::Ok((buffer_pointer, buffer.len()))
}
};
}
pub use as_buffer;
and I get the same error because everything is inside the {} I guess
Also keep in mind that you'll be getting a pointer to the serialized version of your struct. If you just want a pointer to your struct in memory you can skip the serialization step:
let object: Foo = ...;
let ptr = &object as *const Foo as *const u8;
let len = std::mem::size_of::<Foo>();
I just wanted a frictionless way of getting a pointer. It's for a testing unit where rust passes the pointer to C++. I wouldn't use pointers for actual code. So, I have lots of test cases where I repeat that code inside the macro, and I wanted to use a simple solution.
let (pointer, len) = as_buffer(my_struct, Vec::new()).unwrap()
Getting a raw pointer in general is highly rare requirement in Rust. But you may have your own rare use case which requires it. To give you better suggestions we need to know why you want the raw pointers.
You can pass a reference to C++ later with as_ptr(), while keeping ownership in Rust. (And making sure C++ does not keep references that outlive the call.)