tokio::JoinSet and tokio::spawn vs hickory_resolver's futures that aren't `'static`

⚓ Rust    📅 2025-09-09    👤 surdeus    👁️ 8      

surdeus

Warning

This post was published 64 days ago. The information described in this article may have changed.

The code at the end of this post is cut down from a program that's supposed to use hickory_resolver to do a bunch of DNS lookups concurrently. It won't compile, with the error

error[E0597]: `resolver` does not live long enough
  --> src/bin/async-resolve-multi.rs:17:24
   |
10 |         let resolver = Resolver::builder_with_config(
   |             -------- binding `resolver` declared here
...
17 |             let task = resolver.lookup_ip(host);
   |                        ^^^^^^^^ borrowed value does not live long enough
18 |             futures.spawn(task);
   |             ------------------- argument requires that `resolver` is borrowed for `'static`
...
21 |     });
   |     - `resolver` dropped here while still borrowed

The issue appears to be that the future returned by resolver.lookup_ip contains an internal reference to the Resolver object, which makes it not 'static (since the Resolver object obviously does not have static lifetime).

I am at a loss for how to fix this. Advice for related problems seems to be to convert the reference that isn't 'static to an Arc<Box<>> or something along those lines, but that's not an option here because I don't control the code that's producing the troublesome reference. Does anyone have a suggestion?


use hickory_resolver::Resolver;
use hickory_resolver::config::ResolverConfig;
use hickory_resolver::name_server::TokioConnectionProvider;
use tokio::runtime::Runtime;
use tokio::task::JoinSet;

fn main() {
    let rt = Runtime::new().unwrap();
    let results = rt.block_on(async {
        let resolver = Resolver::builder_with_config(
            ResolverConfig::default(),
            TokioConnectionProvider::default()
        ).build();

        let mut futures = JoinSet::new();
        for host in ["www.example.com.", "www.google.com."] {
            let task = resolver.lookup_ip(host);
            futures.spawn(task);
        }
        futures.join_all().await
    });
    for res in results {
        match res {
            Ok(r) => {
                println!("{}:", r.query().name.to_utf8());
                for addr in r.iter() {
                    println!("\t{}", addr);
                }
            }
            Err(e) => {
                println!("error: {e}");
            }
        }
    }
}


Note: This question is closely related to Newb: Tokio::JoinSet and Hickory-Resolver::Resolver Tokio runtimes interfering. However, since that question was posted, the AsyncResolver interface has been removed from hickory_resolver. Also, I'm trying to use one Resolver object to make many DNS queries, in order to get the benefit of Resolver's internal caching.

4 posts - 2 participants

Read full topic

🏷️ Rust_feed