"Sized is not implemented" 是什么意思?

What does "Sized is not implemented" mean?

我写了下面的代码:

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
}

然后在我的终端中 rustc

$ rustc writer_handler.rs
writer_handler.rs:8:15: 8:43 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Writer`
writer_handler.rs:8     let _ = h.handle(&mut stdio::stdout());
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
writer_handler.rs:8:15: 8:43 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Writer`
writer_handler.rs:8     let _ = h.handle(&mut stdio::stdout());
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

为什么 Writer 需要实施 Sized?在我看来 Sized 是不需要的。在保持 trait Handler 具有此通用参数的同时我应该做什么?


在 Rust 1.0 中,这段类似的代码会产生同样的问题:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

出现错误:

error[E0277]: the trait bound `std::io::Write: std::marker::Sized` is not satisfied
 --> src/main.rs:8:5
  |
8 |     handler(w)
  |     ^^^^^^^ `std::io::Write` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `std::io::Write`
  = note: required by `handler`

Rust 的更高版本有错误

error[E0277]: the size for values of type `dyn std::io::Write` cannot be known at compilation time
  --> src/main.rs:8:13
   |
8  |     handler(w)
   |             ^ doesn't have a size known at compile-time
...
11 | fn handler<W>(w: &mut W) -> io::Result<()>
   |    ------- - required by this bound in `handler`
   |
   = help: the trait `std::marker::Sized` is not implemented for `dyn std::io::Write`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

首先,请注意 h 是实现 Fn(&mut Writer) -> IoResult<()>.

的类型

h.handle 正在调用;那么,这取决于 Handler 实现,其中 WWriter——请仔细注意:W Writer,一个未调整大小的类型。 &mut stdio::stdout() 因此将被转换为 &mut Writer 特征对象。理论上这一切都很好,但是当回到实施中时就失败了。当涉及约束时,默认情况下它们的大小,因此它抱怨 Writer,您试图为 W 分配的值未调整大小。

这里有两个主要的解决方案:

  1. 切换到在 h 上使用具体的编写器类型,这样你就可以处理一个大小合适的类型:

    use std::io::{IoResult, Writer, stdio, LineBufferedWriter};
    use std::io::stdio::StdWriter;
    
    fn main() {
        let h = |&: w: &mut LineBufferedWriter<StdWriter>| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    
  2. 允许 W 成为未调整大小的类型。这是可以接受的,因为您仅通过引用 &mut W 使用它。如果您希望将其用作裸类型,例如一个按值取 W 的方法,它不会做。

    use std::io::{IoResult, Writer};
    use std::io::stdio;
    
    fn main() {
        let h = |&: w: &mut Writer| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W: ?Sized> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W: ?Sized, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    

我建议支持未调整大小的 W,即使在这种情况下您不使用它——没有理由 需要 调整大小。

Sized trait 非常特殊,在大多数情况下它是类型参数的默认绑定。它表示在编译时具有已知固定大小的值,例如 u8(1 字节)或 &u32(在具有 64 位指针的平台上为 8 字节)等。这些值是灵活的:它们可以被放置在堆栈上并移动到堆上,并且通常按值传递,因为编译器知道它需要多少space - 无论值在哪里。

未调整大小的类型受到更多限制,类型 Writer 的值未调整大小:它抽象地表示一些实现 Writer 的未指定类型,不知道实际类型是什么。由于不知道实际类型,因此无法知道大小:一些大类型是 Writers,一些小类型是。 Writer 是特征对象的一个​​示例,目前它只能出现在指针后面的已执行代码中。常见示例包括 &Writer&mut WriterBox<Writer>.

这解释了为什么 Sized 是默认值:它通常是人们想要的。

无论如何,对于您的代码,这是弹出的,因为您正在使用 handleh,这是一个 Fn(&mut Writer) -> IoResult<()>。如果我们将此与 Handle 实现的 F: Fn(&mut W) -> IoResult<()> 类型相匹配,我们发现 W = Writer,也就是说,我们试图将 handle 与特征对象 &mut Writer,而不是某些具体类型 W&mut W。这是非法的,因为 trait 和 impl 中的 W 参数默认有一个 Sized 绑定,如果我们用 ?Sized 手动覆盖它,那么一切正常:

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W: ?Sized> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W: ?Sized, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
}

对于 Rust 1.0 代码:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W: ?Sized>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

我还写了一个 blog post about Sized 和 trait objects in general,其中有更多细节。