rust 为什么缓冲区总是以'&amp;mut [u8]'而不是'&amp;mut [MaybeUninit ]'的形式传递< u8>?

7lrncoxx  于 2022-12-19  发布在  其他
关注(0)|答案(2)|浏览(260)
let receiver: Receiver = ...;
let mut buf = [0; 1024]; // allocate a buf that is definitely too big
let bytes_received = reciever.recv(&mut buf);

这是我见过的一种比较常见的模式。要访问数据,我们可能会做如下操作

let data = buf[0..bytes_received];

这是一个很好的模式,但我的问题是我们总是将整个数组清零,即使我们不需要。这不是一个昂贵的操作,但总是忽略它似乎很奇怪。
我建议的另一个选择是

use std::mem::{MaybeUninit, transmute};

struct Receiver {
    // just for demonstration...
    data: &'static str, 
}

trait Receive {
    // this is the only required method, since `recv` can be derived from it
    fn recv_maybe_uninit(&mut self, buf: &mut [MaybeUninit<u8>]) -> &[u8];
    
    fn recv(&mut self, buf: &mut [u8]) -> usize {
        self.recv_maybe_uninit(unsafe { transmute(buf) }).len()
    }
}

impl Receive for Receiver {
    fn recv_maybe_uninit(&mut self, buf: &mut [MaybeUninit<u8>]) -> &[u8] {
        for (i, byte) in self.data.as_bytes().iter().enumerate() {
            buf[i] = MaybeUninit::<u8>::new(*byte);
            if i == buf.len() {
                return unsafe { transmute(buf) }
            }
        }
        unsafe { transmute(&buf[0..self.data.as_bytes().len()]) }
    }
}

fn main() {
    let mut receiver = Receiver {
        data: "Hello, world!",
    };
    let mut buf = [MaybeUninit::uninit(); 1024];
    let data = receiver.recv_maybe_uninit(&mut buf);

    println!("{:?}", std::str::from_utf8(data).unwrap());
}

如果recv_maybe_uninit是默认值,我们可以很容易地推导出recv
也许更好的实现是让缓冲区实现一个trait,允许向其中写入u8...
不管怎么说,为什么不用这种风格呢?

vfwfrxfs

vfwfrxfs1#

也许你看到&mut [u8]用于这种接口的最大原因是因为这是标准库所做的。

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    ...
}

标准库不使用&[MaybeUninit<u8>]的原因是MaybeUninit在1.36.0版本之前并不存在,而Read在1.0.0版本中可用。但是这需要一个默认的实现来初始化未初始化的数组,这样它就可以被传递给read/read_exact
另一个原因可能是你几乎是在强迫trait实现者使用unsafe来处理MaybeUninit(或者至少遵从其他使用unsafe的实现)。如果将来有类似这样的函数,这一点可能会得到改善,但目前还没有安全支持。
话虽如此,你想要的 * 是 * 在其他地方完成的,比如时雄的AsyncRead trait,它没有直接使用&mut [MaybeUninit<u8>],而是一个ReadBuf,它是一个安全的 Package 器,包裹着一个部分初始化的字节切片,在我看来,这给了你两个世界中最好的。
有一个RFC将这种实现添加到标准库中:RFC 2930 -读取缓冲区。
我建议在创建一个新的类读API时遵循这种结构。

nfzehxib

nfzehxib2#

首先,MaybeUninitis quite new。特别是对于数组来说,大多数API仍然是每晚的。这些API应该随着rust版本的增加而改变吗?我不认为这是理想的。
其次,如果图书馆用户需要这个功能,他们可以自己做得很好。

let mut buf: [u8, 1024] = unsafe {std::mem::transmute([MaybeUninit::<u8>::uninit(); 1024]) };
recv.recv(&mut buf)

相关问题