Что значит "Размер не реализован"?
Я написал следующий код:
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
, не размером.
Здесь есть два основных решения:
Переключитесь на использование конкретного типа писателя на
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
даже если вы не используете это в этом случае - нет причин, по которым его нужно измерять.