Spawn a tokio thread running a function that takes &str

I'm building an application that uses thrussh to run an SSH server. The function in thrussh to run the server takes a parameter, addr, to listen on, as &str:

pub async fn run<H: Server + Send + 'static>(
    config: Arc<Config>,
    addr: &str,
    mut server: H,
) -> Result<(), std::io::Error> {

I need to be able to configure the address at runtime, for example to "0.0.0.0:2222" or "127.0.0.1:22", but when I try to spawn this in a tokio thread I get hit with the borrow checker:

let addr = format!("{}:{}", config.bind_address, config.bind_port);

-----
error[E0597]: `addr` does not live long enough
   --> src/ssh.rs:175:47
    |
175 |     tokio::spawn(thrussh::server::run(config, &addr, sh));
    |                  -----------------------------^^^^^-----
    |                  |                            |
    |                  |                            borrowed value does not live long enough
    |                  argument requires that `addr` is borrowed for `'static`
176 | }
    | - `addr` dropped here while still borrowed

Is there a way to pass an &str, or is Box::leak the only option here?

spawn takes ownership of everything so you cannot really pass borrowed stuff to it.
Consider executing from Runtime directly via block_on
Or use LocalSet's block_on (but it needs reference to runtime)

P.s. there is also Handle

Assuming you have the value in a variable addr: String [1] and don’t need it anymore after the call to spawn, you should be able to do something like tokio::spawn(async move { thrussh::server::run(config, &addr, sh).await })

If this thrussh::server::run call is really your entry point to anything async, then the approach to use Runtime::block_on manually – as @DoumanAsh suggested above – is reasonable, too.


  1. Edit: I just now noticed the format! call, so yes the type checks out. If you do need it multiple times instead, you could clone it. Or even put it into an Arc, etc… ↩︎

1 Like

Shouldn't it be async move { (...).await }? Otherwise, it seems that we "spawn" the future that does nothing except creating another future.

1 Like

I need the current thread to continue once it's spawned the thrussh server so I can't .await or block_on its completion.

By using await inside the block passed to tokio::spawn, you "block" (advance, in fact) only the spawned future itself. The code which called spawn will continue as usual.

1 Like

Ah, clever! I'll give that a go.

@steev For context, think of an async { } block as something very similar to a closure. It, too, captures variables, and creates some object that can be “called” [1], and the contained code is not executed immediately. (They even both have support for the move keyword in order to capture all variables by move.) Just a closure creates an Fn/FnMut/FnOnce, while an async block creates a Future.


  1. or in the case of a Future “awaited”, or “polled”, if you will ↩︎

2 Likes

Got it, thanks. Final code that seems to work:

let addr = format!("{}:{}", config.bind_address, config.bind_port);
tokio::spawn(async move { thrussh::server::run(config, &addr, sh).await });
1 Like

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.