Struct with field which is a reference to another field

Hi, I'd like to create a struct where a dbus::blocking::Proxy is stored, since Proxy requires a connection I store that as well in my struct but the code below doesn't compile. While the compiler's error makes sense I don't know how to solve this. Any ideas?

struct DBusNotifier<'a> {
    conn: Box<Connection>,
    proxy: Box<dbus::blocking::Proxy<'static, &'a Connection>>,
}

impl<'a> DBusNotifier<'a> {
    fn new() -> Result<DBusNotifier<'a>, &'static str> {
        let conn = match Connection::new_session() {
            Ok(conn) => Box::new(conn),
            Err(e) => {
                error!("{}", e);
                return Err("cannot create a new connection");
            }
        };

        let proxy = Box::new(conn.with_proxy(
            "org.freedesktop.Notifications",
            "/org/freedesktop/Notifications",
            Duration::from_millis(2000),
        ));

        Ok(DBusNotifier {
            proxy: proxy,
            conn: conn,
        })
    }
}
   Compiling imap-notify v0.1.0 (/home/acardace/work/imap-notify)
error[E0515]: cannot return value referencing local data `*conn`
   --> src/dbus_notify.rs:110:9
    |
104 |           let proxy = Box::new(conn.with_proxy(
    |                                ---- `*conn` is borrowed here
...
110 | /         Ok(DBusNotifier {
111 | |             proxy: proxy,
112 | |             conn: conn,
113 | |         })
    | |__________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/dbus_notify.rs:112:19
    |
94  |   impl<'a> DBusNotifier<'a> {
    |        -- lifetime `'a` defined here
...
104 |           let proxy = Box::new(conn.with_proxy(
    |                                ---- borrow of `*conn` occurs here
...
110 | /         Ok(DBusNotifier {
111 | |             proxy: proxy,
112 | |             conn: conn,
    | |                   ^^^^ move out of `conn` occurs here
113 | |         })
    | |__________- returning this value requires that `*conn` is borrowed for `'a`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
1 Like

It is not possible for one field to store references into other fields in the same struct.

1 Like

It looks like connection is a public field on Proxy, so can you just use proxy.connection when you need it, rather than needing a separate reference?

That won’t work here since the connection is created in the same function as the proxy object. It would have to be changed to create the connection first and passed to this function by ref.

One option to a self referential struct is the owning_ref crate. I tried solving it here. I’m still not convinced it’s a safe solution to the problem but it seems to work. The types got a bit convoluted to get it all to work. But assuming it’s safe, the complication would be hidden behind a new type.

If you don't need to access to the conn field after creation of the struct, what might work is to call Box::leak and use that reference to construct the proxy. You don't need the conn field, in that case.

EDIT: I just realized, you still need to store the raw pointer in a conn field for proper destruction in a Drop implementation, which does require writing unsafe code.

I just looked up dbus crate (which I assume is what you use). You should be able to store the Box<Connection> directly inside the Proxy, instead of using a reference. This lets you avoid the self-referential struct issue altogether.

1 Like

Contrary to what people are saying, I think there is a way:

See this play.

The difference is that I'm only initializing the proxy, when the connection has already been placed.

Notice that this prevents you from moving the struct.

Either way, what @Phlopsi is proposing may be the more elegant way.

1 Like

Sure, but then you might as well just use two variables.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.