Rust

不要なはずの Sized を要求してくる

More than 3 years have passed since last update.

分からなくて 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 が通るようになる。


参考