0

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

1

У меня возникла проблема с компиляцией кода на 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 ответ(ов)

0

Во-первых, обратите внимание, что h — это тип, который реализует Fn(&mut Writer) -> IoResult<()>.

Метод h.handle вызывается, и это зависит от реализации Handler, где W является Writer — обратите внимание на это: W это Writer, размерный тип. Таким образом, &mut stdio::stdout() будет приведён к объекту трейта &mut Writer. Это прекрасно в теории, но, когда мы переходим к реализации, возникают проблемы. Дело в том, что по умолчанию размеры типов должны быть известны, и возникает ошибка, что Writer, значение, которое вы пытаетесь присвоить W, не имеет фиксированного размера.

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

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь