dbus2 / zbus-old

Rust D-Bus crate.
https://gitlab.freedesktop.org/dbus/zbus
Other
49 stars 13 forks source link

Array should provide concrete-type element getters #273

Open zeenix opened 2 years ago

zeenix commented 2 years ago

In GitLab by @WhyNotHugo on Apr 20, 2022, 23:52

I need to create an array with a struct. I basically, want to do this:

STRUCT "sv" {
        STRING "ExecStart";
        VARIANT "a(sasb)" {
                ARRAY "(sasb)" {
                        STRUCT "sasb" {
                                STRING "alacritty";
                                ARRAY "s" {
                                        STRING "alacritty";
                                };
                                BOOLEAN true;
                        };
                };
        };
};

But Array seems to assume that only Variants will be put into it, so I've only been able to construct arrays with variant(struct), which is not entirely the same.

Is this possible an oversight in the API?

zeenix commented 2 years ago

But Array seems to assume that only Variants will be put into it, so I've only been able to construct arrays with variant(struct), which is not entirely the same.

Array is for arrays in a Value (as in VARIANT type in D-Bus). For just arrays, you just use Vec or slice.

zeenix commented 2 years ago

Actually look at the code again, Array only keeps the elements as Value to allow arbitrary types dynamically. All elements are however, expected to be the same type and encoded/decoded as such. There are conversions provided to convert between Vec and Array but also between Vec and Value directly so you rarely need to use Array directly.

Having said that, I think it's a bit of an oversight that Array::get gets you all elements as Value but Dict::get gets you a specific element and converts it into a concrete type for you. At least Array should provide a way to get an element with a concrete type.

zeenix commented 2 years ago

In GitLab by @WhyNotHugo on Apr 21, 2022, 12:19

Right, so it seems that if I want an array of non-variants, I need to use Array::new and pass a signature. It works, but it's eventually pretty ugly:

    let es = Structure::from((command[0].clone(), command, true));

    let mut a = Array::new(Signature::try_from("(sasb)").unwrap());
    a.append(Value::Structure(es)).unwrap();

    let props: Vec<(&str, Value)> = vec![
        ("ExecStart", Value::Array(a)),
    ];

Array generics

I think it really makes sense for Array to be a generic type, so I can use Array<Mysig>. This would allow something like:

let a : Array<MySignature> = vec!(...).into();

Note that an Array<Variant> would still be valid (for arrays with mixed variant types).

Signature macro

That aside, a macro for Signature would be super useful; if the signature string is invalid, I'd prefer a compile-time failure in this case, and not a runtime failure.

Both together would allow for something nice and simple (but also more compile-time validations):

let a : Array<signature!("(sasb)")> = vec!(...).into
zeenix commented 2 years ago

Right, so it seems that if I want an array of non-variants, I need to use Array::new and pass a signature. It works, but it's eventually pretty ugly:

but why don't use use a Vec instead? If you need to put it into a variant, you can convert back and forth to/from a Value.

zeenix commented 2 years ago

As I wrote, need to use Array directly should be very rare.

zeenix commented 2 years ago

And Array can't be a generic type, because that'd need Value to be a generic type too and that couldn't work for various reasons.

zeenix commented 2 years ago

In GitLab by @WhyNotHugo on Apr 21, 2022, 18:45

I haven't been able to use Vec directly.

Using a Vec<Structure> (e.g.: let a = vec!(es);):

error[E0308]: mismatched types
  --> src/main.rs:71:36
   |
71 |         ("ExecStart", Value::Array(a)),
   |                                    ^ expected struct `zbus::zvariant::Array`, found struct `Vec`
   |
   = note: expected struct `zbus::zvariant::Array<'_>`
              found struct `Vec<zbus::zvariant::Structure<'_>>`
error[E0277]: the trait bound `zbus::zvariant::Structure<'_>: Type` is not satisfied
  --> src/main.rs:71:38
   |
71 |         ("ExecStart", Value::Array(a.into())),
   |                                      ^^^^ the trait `Type` is not implemented for `zbus::zvariant::Structure<'_>`
   |
   = note: required because of the requirements on the impl of `From<Vec<zbus::zvariant::Structure<'_>>>` for `zbus::zvariant::Array<'_>`
   = note: required because of the requirements on the impl of `Into<zbus::zvariant::Array<'_>>` for `Vec<zbus::zvariant::Structure<'_>>`

Using let a = vec!(Value::from(es)); builds, but sends an array of variants, not an array of structs, so it's the wrong data type an doesn't work.

I can't get an array of non-variants to work. I don't see how it's possible; once data is put into an array, some information is lost. Specifically, when later serialising there's no way to determine if it's an array of structs, or an array of variants [which are structs].


And Array can't be a generic type, because that'd need Value to be a generic type too and that couldn't work for various reasons.

I think my idea was not clear: Array can be generic in the sense that one can use something like:

array_of_ints: Array<u32>
array_of_structs: Array<Structure>
array_of_variants: Array<Value>

The generic parameter for array would just need to implement the Into<Value> trait.

zeenix commented 2 years ago

Using a Vec<Structure> (e.g.: let a = vec!(es);):

Same thing about Structure, you're not expected to be using it directly. Use a struct or tuple directly if you need that.

zeenix commented 2 years ago

In GitLab by @WhyNotHugo on Apr 21, 2022, 21:13

Ooooh! This turned out to be so much simpler!

    let exec_start = (command[0].clone(), command, true);

    let props: Vec<(&str, Value)> = vec![
        ("RemainAfterExit", remain_after_exit.into()),
        ("ExecStart", vec![exec_start].into()),
        ("ExitType", exit_type.to_string().into()),
    ];