请考虑以下代码:
use std::io::{self, BufRead, Read};
fn main() {
let mut stdin = io::stdin();
let mut content_length = 0;
for line_wrapped in stdin.lock().lines() {
let line = line_wrapped.unwrap();
if line == "" {
let mut buf = vec![0u8; content_length];
stdin.read_exact(&mut buf).unwrap();
print!("{:?}", buf);
}
if line.starts_with("Content-Length: ") {
content_length = line
.split("Content-Length: ")
.nth(1)
.unwrap()
.parse()
.unwrap();
}
}
}
编译器输出:
error[E0502]: cannot borrow `stdin` as mutable because it is also borrowed as immutable
--> src/main.rs:11:13
|
7 | for line_wrapped in stdin.lock().lines() {
| ----- immutable borrow occurs here
...
11 | stdin.read_exact(&mut buf).unwrap();
| ^^^^^ mutable borrow occurs here
...
22 | }
| - immutable borrow ends here
有没有办法在保持程序的类似结构的同时修复错误(在.lines()
内读取)?
答案 0 :(得分:2)
在相同流的缓冲和非缓冲读取之间交替可能非常棘手。如果您没有lock
标准输入来调用lines()
,则用于实现StdinLock
的内部缓冲区可能会超出空行的\n
,并且随后的read_exact
电话不会在正确的地方开始。
所以你只需要lock
一次,你必须在给你read_exact
的同一个缓冲读卡器上调用Lines
,以确保没有字节丢失。乍一看,这看起来不可能:lines()
按值self
获取,因此一旦调用它,就无法在同一个对象上调用read_exact
。但是你可以使用一些技巧。
BufRead
contains此一揽子impl
的文档:
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B
&mut
引用了实现BufRead
的内容实现BufRead
。因此,您可以对&mut
进行临时StdinLock
引用,在上致电lines()
,及时将Lines
丢弃至read_exact
将有效负载转换为buf
,然后使用另一个&mut
引用和另一个Lines
重新开始。
这种方法需要添加另一个循环,使用标志has_header
来指示是否打破外循环。它不是很漂亮,但也许你可以使用它。
let stdin = io::stdin();
let mut stdin_buf = stdin.lock();
'record: loop {
let mut content_length = 0;
let mut has_header = false;
'header: for line_wrapped in (&mut stdin_buf).lines() {
let line = line_wrapped.unwrap();
if line.starts_with("Content-Length: ") {
content_length = line["Content-Length: ".len()..].parse().unwrap();
}
if line.is_empty() {
has_header = true;
break 'header;
}
}
if has_header {
let mut buf = vec![0u8; content_length];
stdin_buf.read_exact(&mut buf).unwrap();
println!("{:?}", buf);
} else {
break 'record;
}
}
最后一点:当Content-Length
标题不存在时,不清楚会发生什么。如果您的原始代码有效,它将重用先前定义的值(无论最后的内容长度是多少,或者对于第一个记录是0)。我的版本一直使用0。在Rust中使用Option
来表示可能未初始化的值(例如content_length
)是惯用的,因此如果缺少None
标题,则将其初始化为Content-Length
可能是更好的选择是一个需要检测的错误。