Getting reference to private struct field?


#1

I’m using xml-rs library to generate XML, and at some point I want to get access to the underlying writer, so I can pipe some raw XML content directly to the output.

Essentially, I want to get a mutable reference to the sink field you can see here:

What would be the ways to do that? I can probably avoid doing that by using RefCell or something like that, but that would come at runtime cost.

I ended up doing unsafe transmute between EventWriter and W, assuming that W will be at the same offset (as it is the first field in the struct):

let v: &str = ...
let writer: &mut EventWriter<W> = ...
let writer: &mut W = unsafe { mem::transmute(writer) };
writer.write_all(v.as_bytes())?;

Is it a safe assumption to make? I guess, I can write a test and call it a day, and if it eventually breaks, I’ll figure out something else (although, could Rust choose different object layout between test and app code?)

P.S. I guess, maybe asking author of the library to add method like “write_raw” might be a good idea :slight_smile:


#2

What is the underlying impl that you’re using for the writer? If it’s something like a File then you can give it a &File and use another &File for your own purposes. However, interleaving “out of band” writes may create corrupted output (eg the EventWriter may buffer internally).

Can you create an event out of the raw xml and just use the proper API to write it?


#3

The compiler can re-order struct fields: http://camlorn.net/posts/April%202017/rust-struct-field-reordering.html

Since EventWriter isn’t declared with a repr that would give you a fixed field layout, I would recommend against transmuting an EventWriter<W> in the hope of getting the sink field.

Moreover, the documentation for std::transmute states that both types must have the same size. I’m sure there are other reasons why trying to transmute like this is not advisable.


#4

Ok, I see.

On the size thing, shouldn’t they be the same size? Both are references.

On the event/parsing thing, I don’t really want to parse and then write it back. That would be a significant performance hit due to the nature of the data I have (large blocks of XHTML stored inside string attribute in JSON, which I need to write as XML).

I guess I can simply switch to using raw writer, though. There is not much I really need for the XML serialization (no namespaces, etc)…


#5

You’re right: most of the time, a reference to W and a reference to EventWriter<W> will be the same size. The compiler would complain if &W is a fat pointer, which would differ in width from &EventWriter<W>.

Are you sure that sharing the underlying Write impl via Rc or a similar mechanism is going to have too much overhead? It might be worth doing that until a better solution (like an extension to the xml-rs API) comes along.


#6

Well, in the end it turned out that quick-xml is much faster anyway, and it does allow for streaming raw XML data. And even if it does the parsing, it is still reasonably fast.

So, I switched to quick-xml instead.