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

  • 22
    Like
  • 0
    Comment
More than 1 year has 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 が通るようになる。

参考