I think this might be a better medium for this question than the Discord channel.... apologies for the cross post.
While some useful direction came from Discord group, the question in case 3. below is still open, and I'd like to understand how the borrow checker can accept a function that used all three of the channel Sender
functions - if there is a limit on what combinations of the three can be used in one function the docs don't yet set out those limits.
The tokio::aync::oneshot::channel
sender (say tx
) has 'interesting' functions when you're using it to shutdown a task by passing it to a handler function (as part of launching that task) - wrapped in an Option
, say some_tx
. The handler signature looks like this:
// This is the simplest signal handling in tokio.
// We don't pass anything between the thread running `main` and the thread
// `tokio::spawn`ed to run this function.
pub async fn handle_signal(some_tx: Option<tokio::sync::oneshot::Sender<String>>) {
//let tx = some_tx;
tokio::signal::ctrl_c().await.expect("Handle ctl-c (Tokio built in)");
println!("Trapped signal clt-c to quit.");
//let Some(tx) = some_tx.take();
if let Some(tx) = some_tx {
match tx.send("Gracefully".to_string()){
Ok(()) => println!("The message may be received."),
Err(e) => println!("The message will never be received: {:?}",e),
}
}
if let Some(tx) = some_tx {
// Wait for the associated `rx` handle to be `rx.close()` or`drop(rx)`ed
tx.closed().await;
println!("The receiver (rx) is closed or dropped.");
}
println!("Exiting!");
std::process::exit(1);
}
}
Within that handler function a reasonable expectation might be to use three of the Sender
functions: send()
, closed()
and is_closed()
All three of those have different signatures.
Meaning you have invoke/call each of them by accessing the tx
inside the Option
in different ways:
- for
tx.send()
: useif let Some(tx) = some_tx { tx.send() }
orsome_tx.map(|tx| tx.send())
- for
tx.is_closed()
: usesome_tx.as_ref().map(|tx| tx.is_closed()
- for
tx.closed().await
: use ... this is where I'm stuck.
To clarify why this is in the beginners thread... I wanted to write a hello world
type of CLI app that respected user signals (say ctl-c
) and gracefully shutdown.
Turns out understanding the detail of the graceful shutdown part is actually quite hard.
The borrow checker doesn't like the current version:
|
23 | if let Some(tx) = some_tx {
| -- value moved here
...
29 | if let Some(tx) = some_tx {
| ^^ value used here after move
|
= note: move occurs because value has type `tokio::sync::oneshot::Sender<String>`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving `some_tx.0`
|
23 | if let Some(ref tx) = some_tx {
| ^^^
error[E0596]: cannot borrow `tx` as mutable, as it is not declared as mutable
--> regatta/examples/05-pages-stream-4-ok-sig-c.rs:31:13
|
29 | if let Some(tx) = some_tx {
| -- help: consider changing this to be mutable: `mut tx`
30 | // Wait for the associated `rx` handle to be `rx.close()` or`drop(rx)`ed
31 | tx.closed().await;
| ^^ cannot borrow as mutable
Some errors have detailed explanations: E0382, E0596.
For more information about an error, try `rustc --explain E0382`.