Rust 源码阅读 - Read和BufReader trait
摘要关键字:rust,Read,BufReader,trait bound

    Std::io::Read 特征在rust中使用广泛,例如TcpStream,甚至对&[u8]字节流也实现了Read特性。这样我们在写socket程序的时候,就可以不限定参数为TcpStream类型,可以传入Read/Write泛型,对泛型进行读写。

    这样做有什么好处呢?一个典型的例子就是单元测试,参数为TcpStream的函数需要运行时socket环境才能测试其逻辑,但如果参数是Read 泛型,那么我们就可以通过&[u8]字节数组切片的输入对函数进行单元测试了!这就体现出了trait抽象的好处了。

    &[u8]的Read trait实现源码如下:

impl Read for &[u8] {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let amt = cmp::min(buf.len(), self.len());
        let (a, b) = self.split_at(amt);

        if amt == 1 {
            buf[0] = a[0];
        } else {
            buf[..amt].copy_from_slice(a);
        }

        *self = b;
        Ok(amt)
    }
    ...  ...

    关键点分析:首先impl的实现对象是&[u8],说明源字节数组切片是不可变的。read第一个参数是一个可变的引用,调用者需是可变类型。

    再看源码内部逻辑:比较两个buf的长度,取最小长度为本次读取的长度。然后拷贝该长度的切片到目的buffer中。最后,关键地方来了,*self=b,修改调用者自身,将自身指向剩下没有读完的数组切片上。再返回read的长度。

    代码构思很精巧。通过一个可变切片不停的在不可变数组上滑动,像游标卡尺一样,不停的滑动窗口,拷贝出其内容。

    于是我们可以构思出如下的代码,测试&[u8]的Read特征:

    

    测试结果正常:

    

    对了,还有BufReader特征,在read基础上做了缓冲处理,官方标准库说明:

    BufReader<R> can improve the speed of programs that make small and repeated read calls to the same file or network socket. It does not help when reading very large amounts at once, or reading just one or a few times. It also provides no advantage when reading from a source that is already in memory, like a Vec<u8>.

     也就是说,带缓冲的读写,对于小而频繁的read操作,可以提高效率,减少系统调用。例如http协议分析器反复对header进行read,就可以通过BufReader从缓冲区中读,减少底层read系统调用开销。如果是proxy批量数据流交换,那么就没什么效果。

    Read和BufReader需要结合使用场景使用。

本条目发布于2022-02-26, 共阅读 1275 次, 评论 0 条
属于标签: rust
0 条评论,说点什么吧