On Wed, Jun 11, 2025 at 10:57 AM Paolo Bonzini <pbonz...@redhat.com> wrote:
> Yes. If using serde the implementation of the traits is very small,
> and basically the same for all types. If not using serde, it would
> need some (or most) of the infrastructure in Marc-André's original
> series.

Looking more at it, the Rust->QObject and QObject->Rust parts *can* be
done with serde (following the model of serde_json's Value
(de)serializer) but the Rust<->C part has a problem.

To recap, Rust->C is the serialization and corresponds to output
visitors. C->Rust is the deserialization and corresponds to input
visitors.

For serialization, serde has a push model where the generated code
looks like this:

      let mut state =
          Serializer::serialize_struct(serializer, "S", 2);
      SerializeStruct::serialize_field(&mut state, "a", &self.a)?;
      SerializeStruct::serialize_field(&mut state, "b", &self.b)?;
      SerializeStruct::end(state)

whereas QAPI has a pull model where visit_type_* drives the process
and requests the fields one by one.

For deserialization, serde has a pull model where the generated code
asks for the field names one by one:

    fn visit_map<__A>(self, mut __map: __A)
        while let Some(key) =
            MapAccess::next_key::<__Field>(&mut __map)? {
                match __key { ... }
        }
    }

whereas QAPI has a push model where visit_type_* again drives the
process and sends fields one by one.

For commands this is not a problem because the real underlying
transformation is QObject->QObject and the intermediate steps (to and
from QObject) can use serde.

However, QOM property getters/setters (especially, but not
exclusively, for properties with compound types) remain a problem
since these use callbacks with a Visitor* argument. I see three
possibilities:

1) everything is done through an intermediate QObject step (e.g. for a
setter: Visitor->QObject with an input visitor, and QObject->Rust with
serde deserialization).
    + easy, Rust only sees serde
    + QMP commands use a single conversion step
    - inefficient

2) everything is done through an intermediate C step (e.g. for a
setter: Visitor->C with a visit_type_* function, and C->Rust with
generated code that does not need to use serde). There is still a
double conversion step, but it's more efficient than option 1
    + one framework (visitor)
    - double conversion for the QMP commands
    - lots of generated code

3) generating a Rust visit_type_* implementation as well, either in
qapi-gen (3a) or through a procedural macro (3b). This should not be
hard to write but it would remove a lot of the advantages from using
serde.
    + efficient
    + preserves single conversion for QMP commands
    - two frameworks

I am leaning towards option 1, i.e. keep using serde but only cover
conversions to and from QObject. The reason is that one future usecase
for Rust in QEMU is the UEFI variable store; that one also has some
Rust<->JSON conversions and could be served by either QObject or
serde_json. Either way, it'd be nice for the UEFI variable store to
remain within the Rust serde ecosystem and allow sharing code between
QEMU and Coconut SVSM. But I'm not so sure...

Paolo


Reply via email to