是否可以编写一个调用 BufRead::fill_buf 的函数,直到中断错误不再发生而不使用不安全?
Is it possible to write a function that calls BufRead::fill_buf until an interrupted error no longer occurs without using unsafe?
是否可以编写一个调用 BufRead::fill_buf
的函数,直到 io::ErrorKind::Interrupted
不再出现而不使用 unsafe?
带有不安全代码的是:
use std::{
io::{self, BufRead},
slice,
};
fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
let (buf_ptr, buf_len) = loop {
match reader.fill_buf() {
Ok(buf) => (buf.as_ptr(), buf.len()),
Err(e) => {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
}
};
Ok(unsafe { slice::from_raw_parts(buf_ptr, buf_len) })
}
如果我尝试 return
或 break
Ok(buf)
,我会从借用检查程序中收到错误消息:
error[E0499]: cannot borrow `*reader` as mutable more than once at a time
--> src/lib.rs:8:15
|
6 | fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
| - let's call the lifetime of this reference `'1`
7 | let (buf_ptr, buf_len) = loop {
8 | match reader.fill_buf() {
| ^^^^^^ mutable borrow starts here in previous iteration of loop
9 | Ok(buf) => return Ok(buf),
| ------- returning this value requires that `*reader` is borrowed for `'1`
我尝试用递归替换循环,但错误仍然存在。我也尝试过使用#![feature(nll)]
,但它也不起作用。
你不能,也不应该出于下面突出显示的原因(它们与引用 return 或 unsafe
无关)。 fill_buf
并不像您认为的那样工作,这是由于文档中的一项重要警告:
Returns the contents of the internal buffer, filling it with more data
from the inner reader if it is empty.
换句话说,如果特征正确实施并遵循文档中规定的合同,则在没有 consume
的情况下对 fill_buf
的后续调用将是空操作。因此,如果您在任何调用 fill_buf
.
的任何地方忘记了该要求,那么只做一个而没有另一个会充满风险。
两种解决方案:
- 如果您正在阅读流的末尾(这是
BufReader
在许多其他语言中所做的),只需 BufRead::read_to_end()
、BufRead::read_line()
或 BufRead::read_until()
。如果你能识别一个分隔符,read_until
可以很容易地变成一个可迭代的结构
- 如果您尝试查看 数据并可能等待更多数据,则需要实现自己的特征。这不是经常需要的,因为大多数试图查看流的人都在寻找
Pattern
或 BufRead
涵盖的单个字节。
作为一个简短的总结,fill_buf
并没有按照您的想法行事,您不需要在不消耗缓冲区内部状态的情况下多次调用它。如果您不想这样做,那么 BufRead
的低级方法不是完成这项工作的工具。
我在这里找到了答案:
Rustc can't "deal" with conditional borrowing returns
所以,目前,在这种情况下,不安全是唯一的答案。
我认为今天在安全 Rust 中不可能循环到 return 对 fill_buf
的 return 的引用。 (至少我在放弃之前撞了一会儿脑袋。)
但是...您可以再打 fill_buf
一次。如果缓冲区已满,则调用应该 return Ok
,并且我希望成本最低。 (也许内联和优化会完全消除它。)如果那不是真的,那么底层的 BufRead
实现是错误的。
我刚刚在跳过转义字节的 BufRead
适配器中做了类似的事情(当当前块没有更多的非转义字节可以传递时,在底层流的 fill_buf
上循环)。
use std::io::{self, BufRead};
pub fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
while let Err(e) = reader.fill_buf() {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
reader.fill_buf()
}
不要注意另一个回答说你绝不能在没有 consume
的情况下调用 fill_buf
。这是错的。您应该简单地期望,如果您再次调用 fill_buf
而不是先调用 consume
,流将位于同一位置。您只需要在希望流前进时调用 consume
即可。 (而且我不知道你怎么会想到你应该在 fill_buf
return 错误之后调用 consume
的荒谬想法...)
是否可以编写一个调用 BufRead::fill_buf
的函数,直到 io::ErrorKind::Interrupted
不再出现而不使用 unsafe?
带有不安全代码的是:
use std::{
io::{self, BufRead},
slice,
};
fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
let (buf_ptr, buf_len) = loop {
match reader.fill_buf() {
Ok(buf) => (buf.as_ptr(), buf.len()),
Err(e) => {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
}
};
Ok(unsafe { slice::from_raw_parts(buf_ptr, buf_len) })
}
如果我尝试 return
或 break
Ok(buf)
,我会从借用检查程序中收到错误消息:
error[E0499]: cannot borrow `*reader` as mutable more than once at a time
--> src/lib.rs:8:15
|
6 | fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
| - let's call the lifetime of this reference `'1`
7 | let (buf_ptr, buf_len) = loop {
8 | match reader.fill_buf() {
| ^^^^^^ mutable borrow starts here in previous iteration of loop
9 | Ok(buf) => return Ok(buf),
| ------- returning this value requires that `*reader` is borrowed for `'1`
我尝试用递归替换循环,但错误仍然存在。我也尝试过使用#![feature(nll)]
,但它也不起作用。
你不能,也不应该出于下面突出显示的原因(它们与引用 return 或 unsafe
无关)。 fill_buf
并不像您认为的那样工作,这是由于文档中的一项重要警告:
Returns the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
换句话说,如果特征正确实施并遵循文档中规定的合同,则在没有 consume
的情况下对 fill_buf
的后续调用将是空操作。因此,如果您在任何调用 fill_buf
.
两种解决方案:
- 如果您正在阅读流的末尾(这是
BufReader
在许多其他语言中所做的),只需BufRead::read_to_end()
、BufRead::read_line()
或BufRead::read_until()
。如果你能识别一个分隔符,read_until
可以很容易地变成一个可迭代的结构 - 如果您尝试查看 数据并可能等待更多数据,则需要实现自己的特征。这不是经常需要的,因为大多数试图查看流的人都在寻找
Pattern
或BufRead
涵盖的单个字节。
作为一个简短的总结,fill_buf
并没有按照您的想法行事,您不需要在不消耗缓冲区内部状态的情况下多次调用它。如果您不想这样做,那么 BufRead
的低级方法不是完成这项工作的工具。
我在这里找到了答案:
Rustc can't "deal" with conditional borrowing returns
所以,目前,在这种情况下,不安全是唯一的答案。
我认为今天在安全 Rust 中不可能循环到 return 对 fill_buf
的 return 的引用。 (至少我在放弃之前撞了一会儿脑袋。)
但是...您可以再打 fill_buf
一次。如果缓冲区已满,则调用应该 return Ok
,并且我希望成本最低。 (也许内联和优化会完全消除它。)如果那不是真的,那么底层的 BufRead
实现是错误的。
我刚刚在跳过转义字节的 BufRead
适配器中做了类似的事情(当当前块没有更多的非转义字节可以传递时,在底层流的 fill_buf
上循环)。
use std::io::{self, BufRead};
pub fn fill_buf_and_ignore_interrupts(reader: &mut impl BufRead) -> io::Result<&[u8]> {
while let Err(e) = reader.fill_buf() {
if e.kind() != io::ErrorKind::Interrupted {
return Err(e);
}
}
reader.fill_buf()
}
不要注意另一个回答说你绝不能在没有 consume
的情况下调用 fill_buf
。这是错的。您应该简单地期望,如果您再次调用 fill_buf
而不是先调用 consume
,流将位于同一位置。您只需要在希望流前进时调用 consume
即可。 (而且我不知道你怎么会想到你应该在 fill_buf
return 错误之后调用 consume
的荒谬想法...)