为什么我可以将不可变引用传递给 BufReader,而不是可变引用?
Why can I just pass an immutable reference to BufReader, instead of a mutable reference?
我正在编写一个简单的基于 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()
的不可变引用而不是可变引用?
整个程序可以找到here。
更多详情
在上面的代码中,我使用了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))
}
在这里我们可以看到它将自我(在我的例子中可能是 &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>
这里需要一个mutable引用,但我只借给它一个immutable引用。当程序在 fill_buf()
中到达 self.inner.read()
时,这应该是一个问题吗?
速答:我们将 &TcpStream
作为 R: Read
传递,而不是 TcpStream
。因此 Read::read
中的 self
是 &mut & TcpStream
,而不是 &mut TcpStream
。 Read
是 &TcpStream
的实现,如您所见 in the documentation。
看看这个工作代码:
let stream = TcpStream::connect("...").unwrap();
let mut buf = [0; 100];
Read::read(&mut (&stream), &mut buf);
请注意 stream
甚至没有绑定为 mut
,因为我们使用它是不可变的,只是对不可变引用有一个可变引用。
Next,你可能会问为什么Read
可以为&TcpStream
实现,因为需要变异something[=51] =] 在读取操作期间。
这是美好的 Rust 世界☮ 结束的地方,邪恶的 C-/operating 系统世界开始的地方。例如,在 Linux 上,您有一个简单的整数作为流的 "file descriptor"。您可以将其用于流上的所有操作,包括读取和写入。由于您按值传递整数(它也是 Copy
类型),因此您是否具有对整数的可变或不可变引用并不重要,因为您可以直接复制它。
因此,操作系统或 Rust std
实现必须完成最少量的同步,因为通过不可变引用进行变异通常很奇怪且很危险。这种行为称为 "interior mutability",您可以阅读更多有关它的信息...
我正在编写一个简单的基于 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()
的不可变引用而不是可变引用?
整个程序可以找到here。
更多详情
在上面的代码中,我使用了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))
}
在这里我们可以看到它将自我(在我的例子中可能是 &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>
这里需要一个mutable引用,但我只借给它一个immutable引用。当程序在 fill_buf()
中到达 self.inner.read()
时,这应该是一个问题吗?
速答:我们将 &TcpStream
作为 R: Read
传递,而不是 TcpStream
。因此 Read::read
中的 self
是 &mut & TcpStream
,而不是 &mut TcpStream
。 Read
是 &TcpStream
的实现,如您所见 in the documentation。
看看这个工作代码:
let stream = TcpStream::connect("...").unwrap();
let mut buf = [0; 100];
Read::read(&mut (&stream), &mut buf);
请注意 stream
甚至没有绑定为 mut
,因为我们使用它是不可变的,只是对不可变引用有一个可变引用。
Next,你可能会问为什么Read
可以为&TcpStream
实现,因为需要变异something[=51] =] 在读取操作期间。
这是美好的 Rust 世界☮ 结束的地方,邪恶的 C-/operating 系统世界开始的地方。例如,在 Linux 上,您有一个简单的整数作为流的 "file descriptor"。您可以将其用于流上的所有操作,包括读取和写入。由于您按值传递整数(它也是 Copy
类型),因此您是否具有对整数的可变或不可变引用并不重要,因为您可以直接复制它。
因此,操作系统或 Rust std
实现必须完成最少量的同步,因为通过不可变引用进行变异通常很奇怪且很危险。这种行为称为 "interior mutability",您可以阅读更多有关它的信息...