将 &[u8] 转换为标准::io::读取导致大小问题



尝试提供&[u8]作为需要Read的函数的参数似乎不像我预期的那样工作,如下面的示例所示。

use std::io::Read;
fn main() {
    let bytes: &[u8] = &[1, 2, 3, 4];
    print_reader(&bytes);
}
fn print_reader(reader: &(Read + Sized)) {
    for byte in reader.bytes() {
        println!("{}", byte.unwrap());
    }
}

编译错误:

error: the trait bound `std::io::Read + Sized: std::marker::Sized` is not satisfied [--explain E0277]
 --> <anon>:9:24
9 |>     for byte in reader.bytes() {
  |>                        ^^^^^
note: `std::io::Read + Sized` does not have a constant size known at compile-time
error: the trait bound `std::io::Read + Sized: std::marker::Sized` is not satisfied [--explain E0277]
 --> <anon>:9:5
9 |>     for byte in reader.bytes() {
  |>     ^
note: `std::io::Read + Sized` does not have a constant size known at compile-time
note: required because of the requirements on the impl of `std::iter::Iterator` for `std::io::Bytes<std::io::Read + Sized>`
error: aborting due to 2 previous errors

锈操场

以下特性实现可以在std::slice文档中找到:

impl<'a> Read for &'a [u8] .

认为这是一个相当无益的错误消息。我来解释一下:


First:你不能有一个trait对象&Sized。这违反了第一条对象安全规则,也没有真正的意义。添加Sized特性绑定的唯一原因是使用所有Sized类型的特殊属性(例如将其保存在堆栈上)。看看这个例子,它尝试使用属性:

fn foo(x: &Sized) {
    let y = *x;
}

y的尺寸是多少?与其他trait对象一样,编译器无法知道。所以我们不能将Sized的唯一目的用于trait对象。因此,特性对象&Sized是无用的,不能真正存在。

在这种情况下,错误信息至少告诉了我们正确的事情:

error: the trait `std::marker::Sized` cannot be made into an object [--explain E0038]
 --> <anon>:7:1
7 |> fn foo(x: &Sized) {
  |> ^
note: the trait cannot require that `Self : Sized` 

此外:我怀疑您添加了+ Sized绑定来解决相同的错误,该错误在您使用参数reader: &Read时已经出现。下面是来自详细错误描述的一个重要见解:

一般来说,Self : Sized用来表示trait不应该作为trait对象使用。

Read::bytes的限制是有意义的,因为Bytes迭代器对每个字节调用Read::read()一次。如果这个函数调用是虚拟/动态的,那么函数调用的开销将远远高于read处理字节的实际过程。


所以…为什么你需要把Read作为一个trait对象呢?通常,通过泛型处理这个问题就足够了(而且在任何情况下都要快得多):

fn print_reader<R: Read>(reader: R) {
    for byte in reader.bytes() {
        println!("{}", byte.unwrap());
    }
}

这避免了动态分派,并且可以很好地与类型检查器和优化器配合使用。

最新更新