分からなくて Stackoverflow で聞いてようやく理解できたのでメモを兼ねて書く。
このエラーと戦う:
error: the trait `core::marker::Sized` is not implemented for the type ...
下記のようなコードを書いた。Writer
は trait なので、実行時のサイズがよく分からずコールスタックには載せられないので参照(&mut
)を使おうと考えた:
use std::io::{stdio, IoResult, Writer};
#[allow(unstable)]
fn main() {
let h = |&: w: &mut Writer| -> IoResult<()> {
writeln!(w, "foo")
};
let _ = h.handle(&mut stdio::stdout());
}
trait Handler<W: Writer> {
fn handle(&self, &mut W) -> IoResult<()>;
}
impl<W: Writer, F: Fn(&mut W) -> IoResult<()>> Handler<W> for F {
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());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
となる。要らないはずの Sized
を要求されている。
Sized
っていうのは「実行時にその型はどれだけメモリの確保が必要かわかっている」ってことを表現した trait で、struct には Sized
が自動的に実装された状態になるが、trait には Sized
が実装されない。trait では実行時にどの構造体がくる分からず、サイズが分からないからだ。
確かに Writer
は trait なので Sized
が実装されていない。
しかしそんなの分かっていて、そのために &mut
を付けて参照にしていたのに・・・
原因
型パラメータには暗黙的に Sized が付いてくるため。
上記の例で言うと trait Handler<W>
と impl Handler<W> for ...
は暗黙的に以下のような意味になる:
trait Handler<W: Writer + Sized> {
impl<W: Writer + Sized, F: Fn(&mut W) -> IoResult<()>> Handler<W> for F {
このように、勝手に Sized
をつけてくるため、Sized
がない Writer
を引数にしている h
を使おうとすると:
error: the trait `core::marker::Sized` is not implemented for the type `std::io::Writer`
というエラーがでてしまう。
対処方法
暗黙的に付く Sized
を除くような記述をする。
それが ?Sized
という trait。これは「Sized
でも Sized
じゃなくてもいいよ、気にしないよ」という意味の(擬似?) trait らしい。
例として挙げたコードだと下記のように修正する:
use std::io::{stdio, IoResult, Writer};
#[allow(unstable)]
fn main() {
let h = |&: w: &mut Writer| -> IoResult<()> {
writeln!(w, "foo")
};
let _ = h.handle(&mut stdio::stdout());
}
// ↓これ!!!!
trait Handler<W: Writer + ?Sized> {
fn handle(&self, &mut W) -> IoResult<()>;
}
// ↓これ!!!!
impl<W: Writer + ?Sized, F: Fn(&mut W) -> IoResult<()>> Handler<W> for F {
fn handle(&self, w: &mut W) -> IoResult<()> {
(*self)(w)
}
}
こうすることで、暗黙的に付く Sized
を避けられ、サイズの要求をされることなく rustc
が通るようになる。
参考
-
rfcs/0490-dst-syntax.md at master · rust-lang/rfcs
- いくつか議論の余地がまだある様子
-
rust - What does "Sized is not implemented" mean? - Stack Overflow
- この件についての私の質問