Я пишу простой TCP-based эхо-сервер. Когда я попытался использовать BufReader
и BufWriter
для чтения и записи в TcpStream
, я обнаружил, что передача TcpStream
в BufReader::new()
по значению перемещает его право собственности, так что я не мог передать его BufWriter
. Тогда я нашел ответ в this thread, который решает эту проблему:Почему я могу просто передать неизменяемую ссылку на BufReader вместо изменчивой ссылки?
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)
. Поэтому я открыл исходный код 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()
. Поэтому я пошел к коду 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()
?
дубликата [Почему можно реализовать читать неизменяемая ссылка на файл?] (http://stackoverflow.com/q/31503488/155423). – Shepmaster
@Shepmaster Я изначально думал, что TcpStream работает по-другому из файла. Но, прочитав ответ Лукаса Кальбердотта, я внезапно понял, что за этим стоит то же самое. Спасибо за вашу ссылку. –