Macro crossbeam_channel::select [−][src]
macro_rules! select { (@parse_list ($($head:tt)*) () ) => { ... }; (@parse_list ($($head:tt)*) (default => $($tail:tt)*) ) => { ... }; (@parse_list ($($head:tt)*) ($case:ident $args:tt => $body:expr, $($tail:tt)*) ) => { ... }; (@parse_list ($($head:tt)*) ($case:ident $args:tt => $body:block; $($tail:tt)*) ) => { ... }; (@parse_list ($($head:tt)*) ($case:ident $args:tt => $body:block $($tail:tt)*) ) => { ... }; (@parse_list ($($head:tt)*) ($case:ident $args:tt => $body:expr) ) => { ... }; (@parse_list ($($head:tt)*) ($case:ident $args:tt => $body:expr,) ) => { ... }; (@parse_list ($($head:tt)*) ($($tail:tt)*) ) => { ... }; (@parse_list_error1 recv $($tail:tt)*) => { ... }; (@parse_list_error1 send $($tail:tt)*) => { ... }; (@parse_list_error1 default $($tail:tt)*) => { ... }; (@parse_list_error1 $t:tt $($tail:tt)*) => { ... }; (@parse_list_error1 $($tail:tt)*) => { ... }; (@parse_list_error2 $case:ident) => { ... }; (@parse_list_error2 $case:ident => $($tail:tt)*) => { ... }; (@parse_list_error2 $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*)) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) =>) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $body:expr; $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => recv($($a:tt)*) $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => send($($a:tt)*) $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => default($($a:tt)*) $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $f:ident($($a:tt)*) $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $f:ident!($($a:tt)*) $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $f:ident![$($a:tt)*] $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $f:ident!{$($a:tt)*} $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) => $body:tt $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident($($args:tt)*) $t:tt $($tail:tt)*) => { ... }; (@parse_list_error3 $case:ident $args:tt $($tail:tt)*) => { ... }; (@parse_list_error3 $($tail:tt)*) => { ... }; (@parse_list_error4 $($tail:tt)*) => { ... }; (@parse_case ($($recv:tt)*) ($($send:tt)*) $default:tt () $labels:tt ) => { ... }; (@parse_case $recv:tt $send:tt $default:tt $cases:tt () ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($r:expr) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($r:expr, $m:pat) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($rs:expr, $m:pat, $r:pat) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($r:expr,) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($r:expr, $m:pat,) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($rs:expr, $m:pat, $r:pat,) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv($($args:tt)*) => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case ($($recv:tt)*) $send:tt $default:tt (recv $t:tt => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send($s:expr, $m:expr) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send($ss:expr, $m:expr, $s:pat) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send($s:expr, $m:expr,) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send($ss:expr, $m:expr, $s:pat,) => $body:tt, $($tail:tt)*) ($label:tt $($labels:tt)*) ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send($($args:tt)*) => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt ($($send:tt)*) $default:tt (send $args:tt => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt $send:tt () (default() => $body:tt, $($tail:tt)*) ($($labels:tt)*) ) => { ... }; (@parse_case $recv:tt $send:tt ($($default:tt)+) (default() => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt $send:tt $default:tt (default($($args:tt)*) => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt $send:tt $default:tt (default $t:tt => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@parse_case $recv:tt $send:tt $default:tt ($case:ident $args:tt => $body:tt, $($tail:tt)*) $labels:tt ) => { ... }; (@codegen_declare (($i:tt $var:ident) recv($rs:expr, $m:pat, $r:pat) => $body:tt, $($tail:tt)*) $recv:tt $send:tt $default:tt ) => { ... }; (@codegen_declare (($i:tt $var:ident) send($ss:expr, $m:pat, $s:pat) => $body:tt, $($tail:tt)*) $recv:tt $send:tt $default:tt ) => { ... }; (@codegen_declare () $recv:tt $send:tt $default:tt ) => { ... }; (@codegen_fast_path (($i:tt $var:ident) recv($rs:expr, $m:pat, $r:pat) => $body:tt,) () () $handles:ident ) => { ... }; (@codegen_fast_path (($recv_i:tt $recv_var:ident) recv($rs:expr, $m:pat, $r:pat) => $recv_body:tt,) () (($default_i:tt $default_var:ident) default() => $default_body:tt,) $handles:ident ) => { ... }; (@codegen_fast_path $recv:tt $send:tt $default:tt $handles:ident ) => { ... }; (@codegen_main_loop $recv:tt $send:tt $default:tt $handles:ident ) => { ... }; (@codegen_container (($i:tt $var:ident) recv($rs:expr, $m:pat, $r:pat) => $body:tt,) () ) => { ... }; (@codegen_container () (($i:tt $var:ident) send($ss:expr, $m:expr, $s:pat) => $body:tt,) ) => { ... }; (@codegen_container $recv:tt $send:tt ) => { ... }; (@codegen_push $handles:ident (($i:tt $var:ident) recv($rs:expr, $m:pat, $r:pat) => $body:tt, $($tail:tt)*) $send:tt ) => { ... }; (@codegen_push $handles:ident () (($i:tt $var:ident) send($ss:expr, $m:expr, $s:pat) => $body:tt, $($tail:tt)*) ) => { ... }; (@codegen_push $handles:ident () () ) => { ... }; (@codegen_has_default () ) => { ... }; (@codegen_has_default (($i:tt $var:ident) default() => $body:tt,) ) => { ... }; (@codegen_finalize $token:ident $index:ident $selected:ident $handles:ident (($i:tt $var:ident) recv($rs:expr, $m:pat, $r:pat) => $body:tt, $($tail:tt)*) $send:tt $default:tt ) => { ... }; (@codegen_finalize $token:ident $index:ident $selected:ident $handles:ident () (($i:tt $var:ident) send($ss:expr, $m:expr, $s:pat) => $body:tt, $($tail:tt)*) $default:tt ) => { ... }; (@codegen_finalize $token:ident $index:ident $selected:ident $handles:ident () () (($i:tt $var:ident) default() => $body:tt,) ) => { ... }; (@codegen_finalize $token:ident $index:ident $selected:ident $handles:ident () () () ) => { ... }; (@$($tokens:tt)*) => { ... }; ($($case:ident $(($($args:tt)*))* => $body:expr $(,)*)*) => { ... }; ($($tokens:tt)*) => { ... }; }
Waits on a set of channel operations.
This macro allows declaring a set of channel operations and blocking until any one of them
becomes ready. Finally, one of the operations is executed. If multiple operations are ready at
the same time, a random one is chosen. It is also possible to declare a default
case that
gets executed if none of the operations are initially ready.
If you need to dynamically add cases rather than define them statically inside the macro, use
Select
instead.
Receiving
Receiving a message from two channels, whichever becomes ready first:
use std::thread; use crossbeam_channel as channel; let (s1, r1) = channel::unbounded(); let (s2, r2) = channel::unbounded(); thread::spawn(move || s1.send("foo")); thread::spawn(move || s2.send("bar")); // Only one of these two receive operations will be executed. select! { recv(r1, msg) => assert_eq!(msg, Some("foo")), recv(r2, msg) => assert_eq!(msg, Some("bar")), }
Sending
Waiting on a send and a receive operation:
use std::thread; use crossbeam_channel as channel; let (s1, r1) = channel::unbounded(); let (s2, r2) = channel::unbounded(); s1.send("foo"); // Since both operations are initially ready, a random one will be executed. select! { recv(r1, msg) => assert_eq!(msg, Some("foo")), send(s2, "bar") => assert_eq!(r2.recv(), Some("bar")), }
Default case
A special kind of case is default
, which gets executed if none of the operations can be
executed, i.e. they would block:
use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel as channel; let (s, r) = channel::unbounded(); thread::spawn(move || { thread::sleep(Duration::from_secs(1)); s.send("foo"); }); // Don't block on the receive operation. select! { recv(r) => panic!(), default => println!("The message is not yet available."), }
Iterators
It is possible to have arbitrary iterators of senders or receivers in a single send
or recv
case:
use std::thread; use std::time::{Duration, Instant}; use crossbeam_channel as channel; let (s1, r1) = channel::unbounded(); let (s2, r2) = channel::unbounded(); s1.send("foo"); s2.send("bar"); let receivers = vec![r1, r2]; // Both receivers are initially ready so one of the two receive operations // will be chosen randomly. select! { // The third argument to `recv` is optional and is assigned a // reference to the receiver the message was received from. recv(receivers, msg, from) => { for (i, r) in receivers.iter().enumerate() { if r == from { println!("Received {:?} from the {}-th receiver.", msg, i); } } } }
Syntax
An invocation of select!
consists of a list of cases. Consecutive cases are delimited by a
comma, but it's not required if the preceding case has a block expression (the syntax is very
similar to match
statements).
The following invocation illustrates all the possible forms cases can take:
select! { recv(r1) => body1, recv(r2, msg2) => body2, recv(r3, msg3, from3) => body3, send(s4, msg4) => body4, send(s5, msg5, into5) => body5, default => body6, }
Input expressions: r1
, r2
, r3
, s4
, s5
, msg4
, msg5
, body1
, body2
, body3
,
body4
, body5
, body6
Output patterns: msg2
, msg3
, msg4
, msg5
, from3
, into5
Types of expressions and patterns (generic over types A
, B
, C
, D
, E
, and F
):
r1
: one ofReceiver<A>
,&Receiver<A>
, orimpl IntoIterator<Item = &Receiver<A>>
r2
: one ofReceiver<B>
,&Receiver<B>
, orimpl IntoIterator<Item = &Receiver<B>>
r3
: one ofReceiver<C>
,&Receiver<C>
, orimpl IntoIterator<Item = &Receiver<C>>
s4
: one ofSender<D>
,&Sender<D>
, orimpl IntoIterator<Item = &Sender<D>>
s5
: one ofSender<E>
,&Sender<E>
, orimpl IntoIterator<Item = &Sender<E>>
msg2
:Option<B>
msg3
:Option<C>
msg4
:D
msg5
:E
from3
:&Receiver<C>
into5
:&Sender<E>
body1
,body2
,body3
,body4
,body5
,body6
:F
Pattern from3
is bound to the receiver in r3
from which msg3
was received.
Pattern into5
is bound to the sender in s5
into which msg5
was sent.
There can be at most one default
case.
Execution
- All sender and receiver arguments (
r1
,r2
,r3
,s4
, ands5
) are evaluated. - If any of the
recv
orsend
operations are ready, one of them is executed. If multiple operations are ready, a random one is chosen. - If none of the
recv
andsend
operations are ready, thedefault
case is executed. If there is nodefault
case, the current thread is blocked until an operation becomes ready. - If a
recv
operation gets executed, the message pattern (msg2
ormsg3
) is bound to the received message, and the receiver pattern (from3
) is bound to the receiver from which the message was received. - If a
send
operation gets executed, the message (msg4
ormsg5
) is evaluated and sent into the channel. Then, the sender pattern (into5
) is bound to the sender into which the message was sent. - Finally, the body (
body1
,body2
,body3
,body4
,body5
, orbody6
) of the executed case is evaluated. The wholeselect!
invocation evaluates to that expression.
Note: If evaluation of msg4
or msg5
panics, the process will be aborted because it's
impossible to recover from such panics. All the other expressions are allowed to panic,
however.