¿Qué significa "El tamaño no está implementado"?

Resuelto k-ui asked hace 9 años • 2 respuestas

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 rustcen 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 Sizedno es necesario. ¿ Qué debo hacer mientras mantengo trait Handlereste 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>
k-ui avatar Jan 20 '15 18:01 k-ui
Aceptado

El Sizedrasgo 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 Writerno 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 Writers, algunos tipos pequeños lo son. Writeres 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 Writero Box<Writer>.

Esto explica por qué Sizedes el valor predeterminado: a menudo es lo que uno quiere.

En cualquier caso, para tu código, esto aparece porque estás usando handlewith h, que es un archivo Fn(&mut Writer) -> IoResult<()>. Si comparamos esto con el F: Fn(&mut W) -> IoResult<()>tipo que Handlese implementa, encontramos que W = Writer, es decir, estamos tratando de usarlo handlecon el objeto de rasgo &mut Writer, no &mut Wpara algún tipo concreto W. Esto es ilegal porque los Wparámetros tanto en el rasgo como en el impl tienen un Sizedlímite predeterminado, si lo anulamos manualmente ?Sizedentonces 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.

huon avatar Jan 20 '2015 12:01 huon

En primer lugar, tenga en cuenta que hes de un tipo que implementa Fn(&mut Writer) -> IoResult<()>.

h.handleestá siendo llamado; esto depende, entonces, de la Handlerimplementación donde Westá Writer(tenga en cuenta que W es Writer un tipo sin tamaño). Por lo tanto, se &mut stdio::stdout()convertirá en el &mut Writerobjeto 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 Writerel valor que intenta asignar Wno tiene un tamaño.

Hay dos soluciones principales aquí:

  1. Cambie a usar el tipo de escritor concreto hpara 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) }
    }
    
  2. Permita Wque 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 toma Wpor 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 Wincluso si no lo usa en este caso; no hay ninguna razón por la que deba ajustarse.

Chris Morgan avatar Jan 20 '2015 12:01 Chris Morgan