The path I would take with the error processing in this code would be to remove the let r = ...; Ok(r)
and have the match
compute the return value directly:
fn tryReceiveUdp(socket:UdpSocket) -> std::io::Result<bool> { let mut buf = [0; 1024]; match socket.recv_from(&mut buf) { Ok((bytes,src)) => { handleUdp(buf[..bytes].to_vec(),src)?; Ok(false) }, Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(true), Err(e) => Err(e), } }
This has more duplicated Ok
s, but it is easy to see at a glance whether each case is either a success or a failure.
As to the exact choice of error propagation operation Err(e) => Err(e)
: I generally prefer not to use the ?
operator with a value known to be an error. In this case, it's simple to not early-return at all and just let the match
pass the Err
out, but in the case where an early-return is necessary, I'd still rather write return Err(e);
or return result;
rather than using an ?
that appears to unconditionally return. I avoid that use of ?
for several reasons:
- it's potentially confusing to readers,
- it contains a pointless branch that can never be taken (though that will of course be optimized out), and
- it requires you to write something after it that type-checks as if control flow could continue past that point (the
;true
in your code).
Other things I notice in review:
- Your function takes the socket by value, so you'll only be able to receive one packet. Consider taking it by reference instead (
&UdpSocket
). - Consider importing modules to use shorter paths rather than repeating
std::
everywhere. - Rust functions should be named with
snake_case
, not camelCase
. - Use standard code formatting, as performed by
cargo fmt
.
With all of those changed:
use std::io; use std::net::UdpSocket; fn try_receive_udp(socket: &UdpSocket) -> io::Result<bool> { let mut buf = [0; 1024]; match socket.recv_from(&mut buf) { Ok((bytes, src)) => { handle_udp(buf[..bytes].to_vec(), src)?; Ok(false) } Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(true), Err(e) => Err(e), } }
true
when nothing happened is very unintuitive for atry*
function.\$\endgroup\$tryReceiveUdp(&socket)?.then(|| sleep(Duration::from_millis(100)));
\$\endgroup\$if !tryReceiveUdp(&socket)? { sleep(Duration::from_millis(100)) }
\$\endgroup\$bool
variable to indicate whether to sleep or not and check that at the end of the loop. I'm wondering what is the most rustic approach.\$\endgroup\$