Что значит "Размер не реализован"?

Я написал следующий код:

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    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());
                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Почему это Writer требуется для реализации Sized? Мне кажется, что Sized не нужен Что я должен делать, сохраняя trait Handler иметь этот общий аргумент?


В Rust 1.0 этот похожий код создает ту же проблему:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

С ошибкой:

error[E0277]: the trait bound `std::io::Write: std::marker::Sized` is not satisfied
 --> src/main.rs:8:5
  |
8 |     handler(w)
  |     ^^^^^^^ `std::io::Write` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `std::io::Write`
  = note: required by `handler`

2 ответа

Решение

Sized trait довольно особенный, настолько особенный, что в большинстве случаев это ограничение по умолчанию для параметров типа. Он представляет значения, которые имеют фиксированный размер, известный во время компиляции, например u8 (1 байт) или &u32 (8 байт на платформе с 64-разрядными указателями) и т. Д. Эти значения являются гибкими: они могут быть помещены в стек и перемещены в кучу, и, как правило, передаются по-значению, так как компилятор знает, сколько места ему нужно, где всякий раз, когда значение идет.

Типы, которые не имеют размера, намного более ограничены, и значение типа Writer не имеет размера: абстрактно представляет некоторый неопределенный тип, который реализует Writer, без знания того, что является фактическим типом. Поскольку фактический тип неизвестен, размер не может быть известен: некоторые большие типы Writer s, некоторые маленькие типы Writer это один из примеров объекта признака, который в данный момент может появляться только в исполняемом коде за указателем. Общие примеры включают &Writer, &mut Writer, или же Box<Writer>,

Это объясняет почему Sized по умолчанию: часто это то, что нужно.

В любом случае, для вашего кода это всплывает, потому что вы используете handle с h, который является Fn(&mut Writer) -> IoResult<()>, Если мы сопоставим это с F: Fn(&mut W) -> IoResult<()> введите это Handle реализуется, потому что мы находим, что W = Writer мы пытаемся использовать handle с чертой объекта &mut Writer не &mut W для какого-то конкретного типа W, Это незаконно, потому что W параметры как в признаке, так и в значении по умолчанию имеют Sized привязать, если мы вручную переопределим его ?Sized тогда все работает нормально

use std::io::{IoResult, Writer};
use std::io::stdio;

fn main() {
    let h = |&: w: &mut Writer| -> IoResult<()> {
        writeln!(w, "foo")
    };
    let _ = h.handle(&mut stdio::stdout());
}

trait Handler<W: ?Sized> where W: Writer {
    fn handle(&self, &mut W) -> IoResult<()>;
}

impl<W: ?Sized, F> Handler<W> for F
where W: Writer, F: Fn(&mut W) -> IoResult<()> {
    fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
}

И для кода Rust 1.0:

use std::io::{self, Write};

fn main() {
    handle(&mut io::stdout());
}

fn handle(w: &mut Write) -> io::Result<()> {
    handler(w)
}

fn handler<W: ?Sized>(w: &mut W) -> io::Result<()>
where
    W: Write,
{
    writeln!(w, "foo")
}

Я также написал пост в блоге о Sized и черт объектов в целом, который имеет немного больше деталей.

Прежде всего, обратите внимание, что h имеет тип, который реализует Fn(&mut Writer) -> IoResult<()>,

h.handle называется; это зависит, следовательно, от Handler реализация где W является Writer - отметьте это внимательно: Writer нестандартный тип. &mut stdio::stdout() поэтому будет приведен к &mut Writer черта объекта. Это все очень хорошо в теории, но при возврате к реализации падает. Когда дело доходит до ограничений, они имеют размер по умолчанию, и поэтому жалуется, что Writer значение, которое вы пытаетесь присвоить W, не размером.

Здесь есть два основных решения:

  1. Переключитесь на использование конкретного типа писателя на h так что вы имеете дело с типом размера:

    use std::io::{IoResult, Writer, stdio, LineBufferedWriter};
    use std::io::stdio::StdWriter;
    
    fn main() {
        let h = |&: w: &mut LineBufferedWriter<StdWriter>| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    
  2. Разрешать W быть нестандартным типом. Это приемлемо, так как вы используете его только через ссылку &mut W, Если вы хотите использовать его как голый тип, например, метод, принимающий W по стоимости, это не будет делать.

    use std::io::{IoResult, Writer};
    use std::io::stdio;
    
    fn main() {
        let h = |&: w: &mut Writer| -> IoResult<()> {
            writeln!(w, "foo")
        };
        let _ = h.handle(&mut stdio::stdout());
    }
    
    trait Handler<W: ?Sized> where W: Writer {
        fn handle(&self, &mut W) -> IoResult<()>;
    }
    
    impl<W: ?Sized, F> Handler<W> for F
    where W: Writer, F: Fn(&mut W) -> IoResult<()> {
        fn handle(&self, w: &mut W) -> IoResult<()> { (*self)(w) }
    }
    

Я бы порекомендовал поддержать негабаритных W даже если вы не используете это в этом случае - нет причин, по которым его нужно измерять.

Другие вопросы по тегам