我写了以下代码:
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`
答案 0 :(得分:36)
Sized
特征相当特殊,特别是在大多数情况下它是类型参数的默认绑定。它表示在编译时已知固定大小的值,如u8
(1字节)或&u32
(在具有64位指针的平台上为8个字节)等。这些值很灵活:它们可以被放置在堆栈上并移动到堆上,并且通常按值传递,因为编译器知道它需要多少空间。
不是大小的类型受到更多限制,类型Writer
的值不是大小的:它抽象地表示一些实现Writer
的未指定类型,不了解实际类型是什么。由于实际类型不为人所知,因此无法知道大小:某些大类型是Writer
,有些是小类型。 Writer
是特征对象的一个示例,此时,它只能出现在指针后面的已执行代码中。常见示例包括&Writer
,&mut Writer
或Box<Writer>
。
这解释了为什么Sized
是默认值:它通常是人们想要的。
在任何情况下,对于您的代码,这会弹出,因为您使用handle
h
Fn(&mut Writer) -> IoResult<()>
,F: Fn(&mut W) -> IoResult<()>
。如果我们将Handle
类型与W = Writer
类型匹配,我们会发现handle
,即我们正在尝试将&mut Writer
与特征对象一起使用对于某些具体类型&mut W
,W
,而不是W
。这是非法的,因为特征和impl中的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) }
}
手动覆盖它,那么一切正常:
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")
}
对于Rust 1.0代码:
{{1}}
我还写了一个blog post about Sized
和特征对象,它们有更多的细节。
答案 1 :(得分:5)
首先,请注意h
是一种实现Fn(&mut Writer) -> IoResult<()>
的类型。
h.handle
;这取决于Handler
W
Writer
Writer
的实施情况 - 请注意:W 是 &mut stdio::stdout()
,一种未经过类型化的类型。因此,&mut Writer
将被转换为Writer
特征对象。这在理论上非常好,但是当被带回实施时就会倒塌。当涉及到约束时,默认情况下它们会被调整大小,因此它会抱怨W
,您尝试为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) }
}
上的具体编写器类型,以便处理大小类型:
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
我建议支持未经过身份验证的{{1}},即使您在这种情况下不使用它 - 也没有理由需要进行调整。