Hello,
I am working on a function to wrap the Nix crate's recvmmsg into an async function for use in a Tokio application. I'm struggling with lifetime errors in my current implementation and I was hoping someone could help me understand how to annotate this appropriately or if I am doing this correctly at all.
The idea is that the user of this function would provide a mutable buffer that contains space for capacity
fixed-size datagrams. I am having a hard time getting the lifetime annotations on that user-provided buffer to correctly propagate through to the returned value of the function.
Also pasted here for clarity:
use nix::sys::{
socket::{MsgFlags, RecvMmsgData, RecvMsg},
uio::IoVec,
};
use tokio::net::UdpSocket;
use thiserror::Error;
type CustomResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
const DatagramSize: usize = 1472
#[derive(Error, Debug)]
enum RecvmmsgError {
#[error("no datagrams ready to be read")]
WouldBlock,
#[error("unknown")]
Unknown,
}
async fn recvmmsg<'a>(
s: &UdpSocket,
buf: &'a mut [u8],
capacity: usize,
) -> CustomResult<Vec<RecvMsg<'a>>> {
let fd = s.as_raw_fd();
let f = move || {
let mut msgs: Vec<_> = buf
.chunks_exact_mut(DatagramSize)
.map(|buf| [IoVec::from_mut_slice(&mut buf[..])])
.map(|iov| RecvMmsgData {
iov,
cmsg_buffer: None,
})
.collect();
match nix::sys::socket::recvmmsg(fd, &mut msgs, MsgFlags::empty(), None) {
Ok(r) => Ok(r),
// nix gives back raw errno values - map them to what tokio expects (std::io::Error)
Err(nix::errno::Errno::EAGAIN) => {
Err(std::io::Error::from(std::io::ErrorKind::WouldBlock))
}
Err(_) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"unknown error in recvmmsg",
)),
}
};
s.readable().await?;
match s.try_io(tokio::io::Interest::READABLE, f) {
Ok(messages) => return Ok(messages),
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
Err(Box::new(RecvmmsgError::WouldBlock))
}
Err(e) => return Err(Box::new(RecvmmsgError::Unknown)),
}
}
The error I get:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/recvmmsg.rs:396:14
|
396 | .chunks_exact_mut(DatagramSize)
| ^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
--> src/recvmmsg.rs:394:13
|
394 | let f = move || {
| ^^^^^^^
note: ...so that closure can access `buf`
--> src/recvmmsg.rs:395:32
|
395 | let mut msgs: Vec<_> = buf
| ^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> src/recvmmsg.rs:387:19
|
387 | async fn recvmmsg<'a>(
| ^^
note: ...so that the types are compatible
--> src/recvmmsg.rs:391:38
|
391 | ) -> CustomResult<Vec<RecvMsg<'a>>> {
| ______________________________________^
392 | | let fd = s.as_raw_fd();
393 | |
394 | | let f = move || {
... |
427 | | // }
428 | | }
| |_^
= note: expected `Result<Vec<RecvMsg<'a>>, Box<(dyn std::error::Error + Send + Sync + 'static)>>`
found `Result<Vec<RecvMsg<'_>>, Box<dyn std::error::Error + Send + Sync>>`