Async Runtime Support

nmrs is built on async Rust and uses zbus for D-Bus communication. While the examples in this book use Tokio, nmrs works with any async runtime.

Tokio is the most commonly used runtime and the one used in all examples:

use nmrs::NetworkManager;

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;
    let networks = nm.list_networks().await?;
    println!("{} networks found", networks.len());
    Ok(())
}

Add to your Cargo.toml:

[dependencies]
nmrs = "2.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

Single-Threaded Tokio

For lightweight applications, you can use the current-thread runtime:

#[tokio::main(flavor = "current_thread")]
async fn main() -> nmrs::Result<()> {
    let nm = nmrs::NetworkManager::new().await?;
    // ...
    Ok(())
}

This is what nmrs-gui uses internally.

async-std

use nmrs::NetworkManager;

#[async_std::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;
    let networks = nm.list_networks().await?;
    println!("{} networks found", networks.len());
    Ok(())
}
[dependencies]
nmrs = "2.2"
async-std = { version = "1", features = ["attributes"] }

smol

use nmrs::NetworkManager;

fn main() -> nmrs::Result<()> {
    smol::block_on(async {
        let nm = NetworkManager::new().await?;
        let networks = nm.list_networks().await?;
        println!("{} networks found", networks.len());
        Ok(())
    })
}
[dependencies]
nmrs = "2.2"
smol = "2"

GLib/GTK (for GUI applications)

When building GTK4 applications, use the GLib main context:

#![allow(unused)]
fn main() {
use nmrs::NetworkManager;

// Inside a GTK application's async context
glib::MainContext::default().spawn_local(async {
    let nm = NetworkManager::new().await.unwrap();

    let networks = nm.list_networks().await.unwrap();
    for net in &networks {
        println!("{}: {}%", net.ssid, net.strength.unwrap_or(0));
    }
});
}

This is how nmrs-gui integrates nmrs into its GTK4 interface.

How It Works

nmrs uses zbus for D-Bus communication, which itself uses an internal async runtime. When you call NetworkManager::new().await, zbus establishes a connection to the system D-Bus. This connection is runtime-agnostic — zbus handles the async I/O internally.

The NetworkManager struct is Clone and Send, so you can share it across tasks regardless of which runtime you use.

Thread Safety

NetworkManager is:

  • Clone — clones share the same D-Bus connection (cheap)
  • Send — can be moved across threads
  • Sync — can be shared via Arc (though Clone is usually simpler)

However, concurrent connection operations are not supported. Don't call connect() from multiple tasks simultaneously. Use is_connecting() to check if a connection is in progress.

Next Steps