任何一门编程语言中 IO操作都是非常常见的内容. IO操作抽象了对实体的读写,该实体可以是任意类型,比如文件,内存,或者Socket。
典型的IO操作包含 读写操作, 大多数编程语言还会提供基于字符串的读写, 和基于缓冲的读写以提高性能
以Java为例,Java中定义了InputStream/OutputStream接口,包含一序列读写二进制的方法。
Reader/Writer则 提供了操作字符串的方法
BufferedInputStream/BufferedOutputStream,BufferedReader/BufferedWriter 提供了基于缓冲的读写
Rust也对IO操作提供了丰富的支持,位于std::io包下面
Rust标准库中的IO操作类主要有
Read/Write, BufRead trait
BufReader/BufWriter struct
相比于Java, 个人认为Rust的IO标准库API设计明显缺乏对称性和美感
比如说 Read trait中包含了对字节类型的读方法,也包含了直接读字符串的方法
但作为 对应的Write trait, 却只有对字节类型的写方法,而没有写字符串的方法
另外读方面有 BufRead trait, 但写的却没有 BufWrite trait
总体感觉API设计明显不如Java清晰
Rust 标准库中,IO的具体实现有File,Cursor(实现了BufRead), 和 TcpStream
下面是个例子
use std::io;
use std::io::prelude::*;
use std::fs::File;
fn main() -> io::Result<()> {
let mut f = File::open("foo.txt")?;
let mut buffer = [0; 10];
// read up to 10 bytes
let n = f.read(&mut buffer)?;
println!("The bytes: {:?}", &buffer[..n]);
Ok(())
}
对于BufWriter来说,需要注意2点
1. BufWriter对于内存的读写没有任何优势,这时可以直接用Write. 只对多次分批写入非内存对象时,有性能优势。
2. 在BufWriter被drop之前,需要手动的调用flush, 以确保数据已经写入。因为BufWriter 被drop时,虽然也会调用flush,但如果flush失败,rust是不会有任何错误通知的。
使用BufWriter的例子
第一种,不使用BufWriter, 性能会比较差
use std::io::prelude::*;
use std::net::TcpStream;
let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
for i in 0..10 {
stream.write(&[i+1]).unwrap();
}
第二种, 使用BufWriter, 性能会好很多
use std::io::prelude::*;
use std::io::BufWriter;
use std::net::TcpStream;
let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
for i in 0..10 {
stream.write(&[i+1]).unwrap();
}
stream.flush().unwrap();
Rust标准库非常的小,没有提供基于内存实现的Read/Write, 只能跑去第三方crate Bytes了
该crate自己定义了一组 Read/Write, Reader/Writer,方便操作内存数据
use bytes::Buf;
use std::io;
let mut buf = b"hello world".reader();
let mut dst = vec![];
io::copy(&mut buf, &mut dst).unwrap();
let buf = buf.into_inner();
assert_eq!(0, buf.remaining());