我想封装一个支持异步的recvmsg接口,使用了smol库和nix库,定义了如下的结构体:
use smol::Async;
use std::os::unix::net::UnixStream ;
pub struct UnixSeqPacket(Async<UnixStream>);
nix库提供的recvmsg接口的原型如下:
pub fn recvmsg<'a>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> Result<RecvMsg<'a>>
smol::Async提供一个用于异步读取的接口,如下:
pub async fn read_with_mut<R>(
&mut self,
op: impl FnMut(&mut T) -> io::Result<R>,
) -> io::Result<R>
我想封装的recvmsg打算使用和nix库一致的接口原型,如下最直接的封装会产生编译错误:
pub async fn recvmsg<'a>(&mut self, iov: &[IoVec<&mut [u8]>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> io::Result<RecvMsg<'a>>
{
self.0.read_with_mut(|s|
recvmsg(s.as_raw_fd(), iov, cmsg_buffer, flags)
.map_err( |err|
match err {
nix::Error::Sys(nix::errno::EWOULDBLOCK) =>
io::Error::from(io::ErrorKind::WouldBlock),
_ => io::Error::new( io::ErrorKind::Other,
format!("fail to recvmsg, err is {}", err)),
}
)
).await
}
编译错误如下:
error[E0507]: cannot move out of `cmsg_buffer`, a captured variable in an `FnMut` closure
--> src/unix_seqpacket.rs:115:41
|
95 | mut cmsg_buffer: Option<&'a mut Vec<u8>>,
| --------------- captured outer variable
...
115 | recvmsg(s.as_raw_fd(), iov, cmsg_buffer, flags)
| ^^^^^^^^^^^
| |
| move occurs because `cmsg_buffer` has type `std::option::Option<&mut std::vec::Vec<u8>>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `cmsg_buffer.as_ref()`
错误原因是 cmsg_buffer: Option<&'a mut Vec> 包含了可变的引用,所以在closure中使用时不能Copy,只能Move。但是smol::Async的接口read_with_mut需要传入的是FnMut,而closure将变量Move后它的类型就是FnOnce了,不能是FnMut了。
我目前能想到的解决方法是使用unsafe,将cmsg_buffer: Option<&'a mut Vec> 在clousre中复制多份,如下所示:
pub async fn recvmsg<'a>(&mut self, iov: &[IoVec<&mut [u8]>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> io::Result<RecvMsg<'a>>
{
self.0.read_with_mut(|s| {
let cmsg_buffer_tmp = cmsg_buffer.as_mut()
.map(|buf| { unsafe { std::mem::transmute_copy(buf) } } );
recvmsg(s.as_raw_fd(), iov, cmsg_buffer_tmp, flags)
.map_err( |err|
match err {
nix::Error::Sys(nix::errno::EWOULDBLOCK) =>
io::Error::from(io::ErrorKind::WouldBlock),
_ => io::Error::new( io::ErrorKind::Other,
format!("fail to recvmsg, err is {}", err)),
}
) }
).await
}
请各位帮忙看下是否有更好的解决方法,谢谢!
1
共 2 条评论, 1 页
评论区
写评论cmsg_buffer 不是必读的,所以使用Option。即使不用Option包裹一层,由于可变引用不能copy,只能move,处理起来仍然有类似的问题。
--
👇
zhouzuoji: 原因是闭包这个匿名结构用move的方式捕获了cmsg_buffer, 但是在闭包内的函数调用里把匿名结构的成员以move方式传递给read_with_mut函数,而总所周知move结构的某个成员是不可行的。
其实我很奇怪的是为什么cmsg_buffer的类型会用Option<&mut Vec>? 为什么要包一层Option? 难道要进行读取你还能传个None?
原因是闭包这个匿名结构用move的方式捕获了cmsg_buffer, 但是在闭包内的函数调用里把匿名结构的成员以move方式传递给read_with_mut函数,而总所周知move结构的某个成员是不可行的。
其实我很奇怪的是为什么cmsg_buffer的类型会用Option<&mut Vec>? 为什么要包一层Option? 难道要进行读取你还能传个None?