Help us understand the problem. What is going on with this article?

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

More than 5 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 が通るようになる。

参考

k_ui
ねこほしい
http://k-ui.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away