¿Qué significa "El tamaño no está implementado"?
Escribí el siguiente código:
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) }
}
Y luego rustc
en mi terminal:
$ 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
¿Por qué es necesario implementar esto Sized
? Me parece que Sized
no es necesario. ¿ Qué debo hacer mientras mantengo trait Handler
este argumento genérico?
En Rust 1.0, este código similar produce el mismo problema:
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")
}
Con el error:
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`
Las versiones posteriores de Rust tienen el error
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
...
11 | fn handler<W>(w: &mut W) -> io::Result<()>
| ------- - required by this bound in `handler`
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::io::Write`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
El Sized
rasgo es bastante especial, tan especial que es un límite predeterminado en los parámetros de tipo en la mayoría de las situaciones. Representa valores que tienen un tamaño fijo conocido en el momento de la compilación, como u8
(1 byte) u &u32
(8 bytes en una plataforma con punteros de 64 bits), etc. Estos valores son flexibles: se pueden colocar en la pila y mover al montón. y generalmente se transmite por valor, ya que el compilador sabe cuánto espacio necesita dondequiera que vaya el valor.
Los tipos que no tienen tamaño son mucho más restringidos, y un valor de tipo Writer
no tiene tamaño: representa, de manera abstracta, algún tipo no especificado que implementa Writer
, sin conocimiento de cuál es el tipo real. Como no se conoce el tipo real, no se puede conocer el tamaño: algunos tipos grandes son Writer
s, algunos tipos pequeños lo son. Writer
es un ejemplo de un objeto de rasgo, que por el momento solo puede aparecer en el código ejecutado detrás de un puntero. Los ejemplos comunes incluyen &Writer
, &mut Writer
o Box<Writer>
.
Esto explica por qué Sized
es el valor predeterminado: a menudo es lo que uno quiere.
En cualquier caso, para tu código, esto aparece porque estás usando handle
with h
, que es un archivo Fn(&mut Writer) -> IoResult<()>
. Si comparamos esto con el F: Fn(&mut W) -> IoResult<()>
tipo que Handle
se implementa, encontramos que W = Writer
, es decir, estamos tratando de usarlo handle
con el objeto de rasgo &mut Writer
, no &mut W
para algún tipo concreto W
. Esto es ilegal porque los W
parámetros tanto en el rasgo como en el impl tienen un Sized
límite predeterminado, si lo anulamos manualmente ?Sized
entonces todo funciona bien:
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) }
}
Y para el código 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")
}
También escribí una publicación de blog sobreSized
objetos de rasgos en general que tiene un poco más de detalle.
En primer lugar, tenga en cuenta que h
es de un tipo que implementa Fn(&mut Writer) -> IoResult<()>
.
h.handle
está siendo llamado; esto depende, entonces, de la Handler
implementación donde W
está Writer
(tenga en cuenta que W es Writer
un tipo sin tamaño). Por lo tanto, se &mut stdio::stdout()
convertirá en el &mut Writer
objeto de rasgo. Todo esto está muy bien en teoría, pero cuando lo llevamos a la implementación fracasa. Cuando se trata de restricciones, tienen un tamaño predeterminado, por lo que se queja de que Writer
el valor que intenta asignar W
no tiene un tamaño.
Hay dos soluciones principales aquí:
Cambie a usar el tipo de escritor concreto
h
para que esté tratando con un tipo de tamaño: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) } }
Permita
W
que sea un tipo sin tamaño. Esto es aceptable ya que sólo lo utiliza a través de una referencia&mut W
. Si desea utilizarlo como un tipo simple, por ejemplo, un método que tomaW
por valor, no sería suficiente.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) } }
Recomendaría admitir un tamaño sin tamaño W
incluso si no lo usa en este caso; no hay ninguna razón por la que deba ajustarse.