Подтвердить что ты не робот

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

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

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`
4b9b3361

Ответ 1

Признак Sized довольно особенный, поэтому он особенный, поскольку в большинстве ситуаций он связан с параметрами типа по умолчанию. Он представляет значения, которые имеют фиксированный размер, известный во время компиляции, например 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 и объекты признаков вообще, которые имеют немного более подробную информацию.

Ответ 2

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

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

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

  • Переключитесь на конкретный тип записи на 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) }
    }
    
  • Разрешить 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, даже если вы не используете его в этом случае - нет причин, по которым он должен быть определен.