Что означает "Sized не реализован"?
У меня возникла проблема с компиляцией кода на Rust, и я хотел бы получить помощь. Я написал следующий код:
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
, получаю следующую ошибку:
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
не нужен. Что мне делать, чтобы сохранить трейт 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
В более поздних версиях Rust появляется следующая ошибка:
error[E0277]: the size for values of type `dyn std::io::Write` cannot be known at compilation time
--!> src/main.rs:8:13
|
8 | handler(w)
| ^ doesn't have a size known at compile-time
Я не уверен, как разрешить эту проблему и продолжить использование обобщений в трейте. Заранее благодарю за помощь!
1 ответ(ов)
Во-первых, обратите внимание, что 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
, даже если вы потом не будете его использовать в этом случае — нет причин, по которым он должен быть размерным.
Почему println! не работает в юнит-тестах Rust?
Можно ли пометить функцию как устаревшую?
Как перебрать кластеры графем Unicode в Rust?
Как умножать/делить/складывать/вычитать числа разных типов?
Лучшие практики использования постоянных хеш-карт в Rust