我正在编写一个简单的基于TCP的回显服务器。当我尝试使用BufReader
和BufWriter
从TcpStream
读取和写入时,我发现通过值传递TcpStream
到BufReader::new()
会移动其所有权,因此我无法将其传递给BufWriter
。然后,我在这个线程中找到了一个解决问题的答案:
fn handle_client(stream: TcpStream) {
let mut reader = BufReader::new(&stream);
let mut writer = BufWriter::new(&stream);
// Receive a message
let mut message = String::new();
reader.read_line(&mut message).unwrap();
// ingored
}
这很简单并且有效。然而,我不太理解为什么这段代码能够工作。为什么我可以只传递一个不可变引用给
BufReader::new()
,而不是一个可变引用?整个程序可以在这里找到。
更多细节:
在上述代码中,我使用了
reader.read_line(&mut message)
。所以我打开了Rust标准库中BufRead
的源代码,并看到了这个:fn read_line(&mut self, buf: &mut String) -> Result<usize> {
// ignored
append_to_string(buf, |b| read_until(self, b'\n', b))
}
在这里,我们可以看到它将self(在我的情况下可能是&mut BufReader
)传递给了read_until()
。接下来,在同一文件中我找到了以下代码:
fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
-> Result<usize> {
let mut read = 0;
loop {
let (done, used) = {
let available = match r.fill_buf() {
Ok(n) => n,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e)
};
match memchr::memchr(delim, available) {
Some(i) => {
buf.extend_from_slice(&available[..i + 1]);
(true, i + 1)
}
None => {
buf.extend_from_slice(available);
(false, available.len())
}
}
};
r.consume(used);
read += used;
if done || used == 0 {
return Ok(read);
}
}
}
在这部分中,有两个地方使用了
BufReader
: r.fill_buf()
和 r.consume(used)
。我认为r.fill_buf()
是我想要看到的。因此,我去查看了Rust标准库中BufReader
的代码,并找到了以下内容:fn fill_buf(&mut self) -> io::Result<&[u8]> {
// ignored
if self.pos == self.cap {
self.cap = try!(self.inner.read(&mut self.buf));
self.pos = 0;
}
Ok(&self.buf[self.pos..self.cap])
}
看起来它使用self.inner.read(&mut self.buf)
从self.inner
读取数据。然后,我们来看一下BufReader
的结构和BufReader::new()
:
pub struct BufReader<R> {
inner: R,
buf: Vec<u8>,
pos: usize,
cap: usize,
}
// ignored
impl<R: Read> BufReader<R> {
// ignored
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: R) -> BufReader<R> {
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
}
// ignored
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
BufReader {
inner: inner,
buf: vec![0; cap],
pos: 0,
cap: 0,
}
}
// ignored
}
通过以上代码,我们可以知道inner
是一个实现了Read
接口的类型。在我的情况下,inner
可能是一个 &TcpStream
。
我了解Read.read()
的签名为:
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
在这里需要一个可变引用,但我只借了一个不可变引用。当程序到达self.inner.read()
在fill_buf()
中时,这会成为一个问题吗?