分からなくて 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
- この件についての私の質問