我正在编写一个处理TCP套接字的低级网络应用程序,我经常需要处理二进制数据流。当一些数据可用时,我将其读入u8
数组,然后将其包装到std::io::Cursor<&[u8]>
中,然后将其传递给处理程序。在处理程序中,我经常需要知道Cursor
中是否还有更多数据。
想象一下,handle
函数接收数据,然后使用handle_chunk
函数以块的形式处理它。为简单起见,假设块大小固定为10个字节;如果数据大小不能被10整除,则表示错误。这个简单的逻辑可以通过以下方式实现:
fn handle(mut data: Cursor<&[u8]>) {
while !data.empty() {
if let Err(err) = handle_chunk(&mut data) {
eprintln!("Error while handling data: {}", err);
}
}
}
fn handle_chunk(data: &mut Cursor<&[u8]>) -> Result<(), String> {
// Returns Err("unexpected EOF".to_string()) if chunk is incomplete
// ...
}
但是,Cursor
没有empty()
方法或任何其他能够告知是否有更多数据需要处理的方法。我能提出的工作解决方案是:
fn handle(data: Cursor<&[u8]>) {
let data = data.into_inner();
let len = data.len();
let mut data = Cursor::new(data);
while (data.position() as usize) < len - 1 {
if let Err(err) = handle_chunk(&mut data) {
eprintln!("Error while handling data: {}", err);
}
}
}
这看起来很丑陋而且不优雅。有更好的解决方案吗?也许Rust标准库中有一个不同的工具比Cursor
更适合这里?
答案 0 :(得分:2)
使用Cursor::get_ref
可以简化您的代码,以避免分解输入并将其重新组合在一起:
fn handle(mut data: Cursor<&[u8]>) {
let len = data.get_ref().len();
while (data.position() as usize) < len - 1 {
if let Err(err) = handle_chunk(&mut data) {
eprintln!("Error while handling data: {}", err);
}
}
}
现在,您还没有显示任何需要Cursor
的代码。很多时候,人们认为需要将&[u8]
转换为实现Read
的内容,但事实并非如此。 Read
已实施&'a [u8]
:
use std::io::Read;
fn handle(mut data: &[u8]) {
while !data.is_empty() {
if let Err(err) = handle_chunk(&mut data) {
eprintln!("Error while handling data: {}", err);
}
}
}
fn handle_chunk<R: Read>(mut data: R) -> Result<(), String> {
let mut b = [0; 10];
data.read_exact(&mut b).unwrap();
println!("Chunk: {:?}", b);
Ok(())
}
fn main() {
let d: Vec<u8> = (0..20).collect();
handle(&d)
}
通过mut data: &[u8]
并使用&mut data
,代码将更新切片变量以使其前进。我们不能轻易倒退。
empty()
方法
Rust样式表示empty
方法是动词 - 这会删除数据(如果可能的话)。您想要的方法应该被称为is_empty
,如切片所示。