io.ReadFull
的逻辑会从底层的 io.Reader
中直接返回错误。
在 io.Reader
返回 io.ErrUnexpectedEOF
的情况下,调用者无法区分以下两种情况:
- 底层的
io.Reader
突然结束并处于截断状态,或者 - 底层的
io.Reader
正确结束且没有更多数据。
为了避免这种歧义,我们应该 Package 底层的任何 io.ErrUnexpectedEOF
。
然而,我怀疑这是一种破坏性更改,我们能做的最好的事情就是记录这一点。
\cc @josharian@catzkorn
4条答案
按热度按时间xpcnnkqh1#
CC @ianlancetaylor, @rsc。
hwazgwia2#
我同意我们不能在这一点上改变这个:
底层的读取错误永远不会被 Package 在这些实用函数中。
我很好奇你是如何使用io.ReadFull的,这会有什么影响。
ReadFull(n)的目的是说:“我确定有n个字节要被读取,我想读取它们。”
如果底层的读取器只有,比如说,n-1个字节,
它并不真正关心底层的读取器在这些n-1个字节之后认为是否有一个“好的EOF”或“坏的EOF”。
无论如何,ReadFull知道这是一个坏的EOF。
从另一个Angular 来看,实际上只期望ReadFull、ReadAtLeast等返回ErrUnexpectedEOF。
一般的读取器实现可能不应该从普通的Read中返回一个普通的ErrUnexpectedEOF。
相反,它应该返回一个更详细的错误。
所以问题可能出在你正在使用的特定Reader上:
它不应该在一开始就从Read中返回一个裸露的ErrUnexpectedEOF。
sg3maiej3#
我好奇你是如何使用io.ReadFull的,这很重要。
我们有一个(池化的)缓冲区来读取,以及一个未知大小的数据源。我们想尽可能多地将数据读入缓冲区,直到缓冲区满(哎呀,拿一个大一点的缓冲区)或者我们到达EOF。
我认为这个问题中的代码之所以使用ReadFull,是因为它是io包中最明显的方便函数,可以让你使用缓冲区。而且它在这个问题上运行得很好。
修复方法是编写自己的自定义读取循环。这可能是正确的答案,但发现ReadFull中有一个隐藏的陷阱,因此出现了这个问题。这可能是io包的一个不错的补充。(Read的语义意味着对于所有使用io.Reader的情况,都需要read帮助器。)
所以问题可能出在你正在使用的特定Reader上:
它不应该在一开始就从Read中返回裸露的ErrUnexpectedEOF。
激发此问题的案例是底层的reader,即net/http.Request.Body。如果在读取过程中连接关闭,它会返回ErrUnexpectedEOF。我们也无法改变这一点。
sshcrbum4#
@josharian描述的代码看起来像这样:
这段代码真的很奇怪,因为错误条件完全颠倒了(即,nil是错误,而
io.EOF
和io.ErrUnexpectedEOF
是成功)。我们有一个截断错误,如果底层读取器返回io.ErrUnexpectedEOF
,那么我们将其视为成功(糟糕)。我们最终编写了自己的
io.ReadFull
类似功能的反向语义:使用
ReadUntilEOF
,我们之前的代码片段变得更加易读:我怀疑我们需要将尽可能多的
r
读入缓冲区的用例是独一无二的。我想知道是否有一个合理的用例,需要一个具有反向语义的io.ReadFull
函数(即,io.ReadUntilEOF
)。