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:
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)
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.
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.
@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.