Hi, Alex, October 22, 2024 at 10:58 AM, "Alex Bennée" wrote: > How easy would it be to expose a Rust API? I'm curious because now we > are looking to integrate Rust into QEMU we could consider transitioning > to a Rust API for plugins. It has been done before: > > https://github.com/novafacing/qemu-rs/tree/main/qemu-plugin-sys > > and > > https://github.com/novafacing/qemu-rs/tree/main/qemu-plugin > > I'm curious as to what it would look like. I don't know how tenable it > would be to run 2 plugin APIs side-by-side long term though. We would > probably want to make a choice. Also how would that affect other C like > APIs like python?
I'm maybe not an expert w.r.t. plugins with Rust, but here are my thoughts: Calling C code from Rust is obviously not an issue. For ideomatic Rust (not littered with "unsafe") you want an abstraction over that, but as Qemu is C you need that somewhere anyway. Things that are generally easy to handle are opaque types behind pointers (this is probably true for most language binding) and C strings, as long as you can figure out who owns them and how long they live. Things in the current API which make things a bit awkward are (probably) unions and Glib-types such as the GArray returned by qemu_plugin_get_registers. Also, you can use Rust functions for callbacks, but ideally you want to allow using all types implementing the Fn trait, e.g. closures carrying some context. For that, you need to transport that context from the point of registration to the callback, i.e. you need some udata. Not all callbacks have udata, but looking closer the ones lacking it are those you register only once, which means we could have some "static" storage for those context. It's not ideal, but not a show-stopper either. I didn't check how the qemu-plugin crate handles that situation. With a native Rust interface, you would not have those problems. However, for plugins you would need a dylib interface, which comes with restrictions. In particular, you cannot use generics in the interface. To allow for the very extension we want the interface would make heavy use of Box<dyn SomeTrait>, in particular Box<dyn Fn(...) -> ...>. The awkward thing about those is that you cannot simply convert them into a void pointer because the "dyn" means fat pointers are involved: unlike in C++, the vtable is embedded in the "pointer". Since we want to invoke those from the C side, we need another level of indirection which lets us operate on an opaque Rust type through non-generic functions that then does the thing we want to the boxed thing. Ironically, you don't have the same problem with a Rust plugin developed against a C interface because you can just use a generic function unpacking (i.e. casting) the context and throw its instantiation's pointer over the fence. So... I'm not sure about the benefits of a native Rust plugin API compared to the qemu-plugin crate or something similar. Especially considering that we would want to use the very same callback registry in the back, anyway. That is, if you want feature parity between the different plugin APIs. There are some things that would make language bindings easier in general, but some of those would involve breaking changes and may not be worth it. Regards, Julian