Fighting with zbus implementation

Hi I'm 2 Weeks old in Rust now and I already learned a lot about programming in rust.
ATM I working on implementing a NetworkManager-connection with zbus into my code.

Reading and writing properties thru a proxy works fine.
Implementing Methods without return value is working.
BUT
Implementing a Method with the return value with the type ObjectPath breaks my brain.

So this is the implementing code:

use zbus::{dbus_proxy, Connection};
use zvariant::ObjectPath;

#[dbus_proxy(
    interface = "org.freedesktop.NetworkManager",
    default_service ="org.freedesktop.NetworkManager",
    default_path ="/org/freedesktop/NetworkManager",
)]
trait nmManager<'_> {
    // properties
    #[dbus_proxy(property)]
    fn version(&self) -> zbus::Result<String>;
    #[dbus_proxy(property)]
    fn state(&self) -> zbus::Result<u32>;
    #[dbus_proxy(property)]
    fn active_connections(&self) -> zbus::Result<Vec<ObjectPath>>;
    #[dbus_proxy(property)]
    fn set_wireless_enabled(&self, arg1: bool) -> zbus::Result<()>;

    // methods
    fn enable(&self, enable: bool) -> zbus::Result<()>;
    fn deactivate_connection(&self, active_connection: ObjectPath<'_>) -> zbus::Result<()>; 

    // this wont work
    fn activate_connection(&self, connection: ObjectPath<'_>, device: ObjectPath<'_>, specific_object: ObjectPath<'_>) -> zbus::Result<ObjectPath>; // return active_connection 
}

This is a example how I want to use the proxy later:

pub async fn read_some_props() -> zbus::Result<()> {
    let connection = Connection::system().await?;

    let proxy = nmManagerProxy::new(&connection).await?;
    println!("Version: {}", proxy.version().await?);
    println!("State: {}", proxy.state().await?);
    proxy.enable(true).awai?t;
    
    Ok(())
}

The 'activate_connection' part is the problematic one.
The Compiler says:

error: implementation of Deserialize is not general enough
...
= note: zvariant::ObjectPath<'_> must implement Deserialize<'0>, for any lifetime '0...
= note: ...but zvariant::ObjectPath<'_> actually implements Deserialize<'1>, for some specific lifetime '1

It might be a problem with lifetime but I'm not 100% sure about this.
I would love to not use lifetime to often cause it 1. breaks my head and 2. I would like to keep it simple.
If you could explain the problem to me and push me into the right direction would be super helpful.

So best greetings Karl

Hi, zbus maintainer here. :wave:

Proxy methods can only return owned types with 'static lifetimes. So instead of returning ObjectPath, you need to return an OwnedObjectPath.

1 Like

I'm not familiar with the zbus and zvariant crates, so I don't know what the dbus_proxy macro is expecting, however from the error I can see it is somehow trying to deserialize into a ObjectPath. The problem however is that ObjectPath has a lifetime so it is likely borrowing from the deserialization source, but this won't be alive when the ObjectPath is returned. The solution is to have ObjectPath not borrow from it, which requires changing the type, or using another type that's equivalent but doesn't borrow. Since it is defined in the zvariant crate I tried looking at its documentation and found a type named OwnedObjectPath, which might be what you need.

1 Like

Thx for that crazy fast response.
Works like a charm!

I will need to lock up "OwnedObjectPath" for 100% understanding this but I'm very happy with this!
I searched a lot thru the net to get thsi running and the zbus book gives a good start.
It might be worth to mention this in it.

Thank you again have a perfect week.

NP, someone who knows me pointed me to it. :slight_smile:

You're right. PRs welcome. BTW zbus is not something a Rust newbie should be using IMO. From personal experience, if you learn Rust by diving in, you're in for a lot of pain. I highly recommend this blog post to everyone new to Rust for this very reason.

Oh and we've a Matrix channel you can ask all zbus questions in.

I see your Point here. I already have quite some experience in C,C++ and C#.
That helps a lot to understand and accept pointer and memory allocations and still think very object oriented. Unfortunately Networkmanager does not apply to have another robust and easy to handle or nice documented API in rust. Nevertheless I think this part of the project will be the most challenging.

Thanks for the advice with the matrix channel. I think i came up already with the next question. :grin:

That's great and it does help to know other languages (especially C++) but they are also detrimental to learning Rust. Please do read that blog post because that's one of the points it makes.

1 Like