Ich schreibe einen einfachen TCP-basierten Echo-Server. Als ich versuchte, BufReader
und BufWriter
zu verwenden, um von einem TcpStream
zu lesen und zu schreiben, fand ich, dass das Übergeben eines TcpStream
zu BufReader::new()
wertmäßig seinen Besitz verschiebt, damit ich es zu einem BufWriter
nicht übergeben konnte. Dann fand ich eine Antwort in this thread, die das Problem löst:Warum kann ich einen unveränderlichen Verweis auf BufReader statt einer veränderbaren Referenz übergeben?
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
}
Das ist einfach und es funktioniert. Ich kann jedoch nicht ganz verstehen, warum dieser Code funktioniert. Warum kann ich einfach eine unveränderliche Referenz an BufReader::new()
anstelle einer veränderbaren Referenz übergeben?
Das gesamte Programm kann here gefunden werden.
Details
In dem obigen Code, habe ich reader.read_line(&mut message)
. Also öffnete ich den Quellcode von BufRead
in Rust Standard-Bibliothek und sah dies:
fn read_line(&mut self, buf: &mut String) -> Result<usize> {
// ignored
append_to_string(buf, |b| read_until(self, b'\n', b))
}
Hier können wir sehen, dass es die Selbst geht (die ein &mut BufReader
in meinem Fall sein kann) zu 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);
}
}
}
In diesem Teil gibt es zwei Orte mit den BufReader
: Als nächst ich den folgenden Code in der gleichen Datei gefunden r.fill_buf()
und r.consume(used)
. Ich dachte r.fill_buf()
ist was ich sehen will. Deshalb ging ich in den Code von BufReader
in Rust Standardbibliothek und diese gefunden:
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])
}
Es scheint, wie es self.inner.read(&mut self.buf)
verwendet die Daten von self.inner
zu lesen. Dann haben wir einen Blick auf die Struktur von BufReader
nehmen und die 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
}
Aus dem obigen Code können wir wissen, dass inner
ein Typ ist, der Read
implementiert. In meinem Fall kann die inner
eine &TcpStream
sein.
wusste, dass ich die Unterschrift von Read.read()
ist:
fn read(&mut self, buf: &mut [u8]) -> Result<usize>
Es eine Referenz wandelbar erfordert hier, aber ich es nur einen unveränderlich Bezug verliehen. Soll das ein Problem sein, wenn das Programm self.inner.read()
in fill_buf()
erreicht?
Duplizieren von [Warum ist es möglich umzusetzen lesen Lesen Sie auf ein unveränderlicher Verweis auf Datei?] (http://stackoverflow.com/q/31503488/155423). – Shepmaster
@Shempmaster Ich dachte ursprünglich, dass TcpStream anders funktioniert als File. Aber nachdem ich Lukas Kalbertodts Antwort gelesen hatte, wurde mir plötzlich klar, dass die Idee dahinter dieselbe ist. Danke für deinen Link. –