首页 > 解决方案 > Rust:将流读入缓冲区直到它“完成”

问题描述

我有一段代码使用以下模式:

    let mut request_buf: Vec<u8> = vec![];
    let mut buf = [0 as u8; 50]; // 50 byte read buffer
    
    while !req.parse(&request_buf).unwrap().is_complete() {
        match stream.read(&mut buf) {
            Ok(size) => {
                request_buf.extend(&buf[0..size]);

            },
            Err(_) => {
                // Handle err...
            }
        }
    }

基本上,代码应该从套接字读取数据到临时缓冲区buf并累积它直到 fn is_complete() 返回 true。

我遇到了借用检查器的问题,但我想不出替代方案。( cannot borrow request_buf as immutable because it is also borrowed as mutable)。

即使我尝试克隆 request_buf 来解决这个问题,这是非常低效的,temporary value dropped while borrowed无论我为克隆缓冲区的变量范围有多广,我都会得到一个。

这种模式在 Rust 中根本不可能实现吗?我觉得我已经尝试了所有我能想到的东西,Rust 无时无刻不在和我玩猫捉老鼠。

标签: rust

解决方案


问题是缓冲区必须Request有一个生命周期。'b因此调用将对象与缓冲区parse() 相关联,这意味着它现在持有. 这消除了获得与生命一样长的可变借用的可能性。Requestreqrequest_bufrequest_bufreq

这不仅仅是借用检查器的心血来潮:parse()意味着req(通常是为了提高效率)保持指向内部的切片的签名request_buf。可变借用request_buf将允许您缩小容器或将其增长到超过其当前容量,从而导致它重新分配。任何一种情况都会使所持有的切片无效req并导致崩溃。

reddit上的评论澄清了解析器不存储解析状态。您应该使用一个足够大的缓冲区,以至于部分解析很少见,如果发生这种情况,您只需从头开始,大概是通过创建一个新Request缓冲区并为其提供整个缓冲区(后者您已经在做)。例如:

fn parse(mut stream: impl Read) {
    let mut request_buf: Vec<u8> = vec![];
    let mut buf = [0 as u8; 16384]; // 16k read buffer

    let (req, headers) = loop {
        match stream.read(&mut buf) {
            Ok(size) => request_buf.extend(&buf[0..size]),
            Err(_) => {} // handle err,
        }
        let mut headers = [httparse::EMPTY_HEADER; 16];
        let mut req = Request::new(&mut headers);
        if req.parse(&request_buf).unwrap().is_complete() {
            break (req, headers);
        }
    };
}

推荐阅读