Как использовать карту для Box >? - PullRequest
3 голосов
/ 17 апреля 2020

У меня есть следующий фрагмент кода:

fn stream_it(&self) -> Box<dyn Stream<Item=()>> {
   Box::new(self.some_field)
}

fn consume_it(&self) {
   let a = self.stream_it().map(|i| i);
}

И я получаю ошибку компиляции:

error: the `map` method cannot be invoked on a trait object
   --> ...
    |
69  |       let a = self.stream_it().map(|i| i);
    |                                ^^^
    | 
   ::: ...
    |
257 |         Self: Sized,
    |               ----- this has a `Sized` requirement
    |
    = note: other candidates were found in the following traits, perhaps add a `use` for one_of_them:
            candidate #1: `use futures_util::future::future::FutureExt;`
            candidate #2: `use futures_signals::signal::signal::SignalExt;`
            candidate #3: `use futures_util::stream::stream::StreamExt;`
            candidate #4: `use futures_signals::signal_vec::SignalVecExt;`
            candidate #5: `use async_std::stream::stream::StreamExt;`

Я понимаю, что требование Sized необходимо, но Я не знаю, как это выполнить. Можно ли даже нанести на карту поток объекта?

1 Ответ

5 голосов
/ 17 апреля 2020

Если вы посмотрите на определение StreamExt::map, которое автоматически реализуется типами, которые реализуют Stream, вы увидите это:

fn map<T, F>(self, f: F) -> Map<Self, F>
where
    F: FnMut(Self::Item) -> T

Другими словами, map становится владельцем self. Но Box<dyn Stream> не реализует Stream, поэтому вместо этого он попытается использовать значение dyn Stream, содержащееся в нем. Но это объект trait, который имеет тип без размера и никогда не может быть параметром self без ссылки, поэтому ваша ошибка компилятора.

Решением здесь было бы изменить тип возвращаемого значения на <a href="https://doc.rust-lang.org/stable/std/pin/struct.Pin.html" rel="noreferrer">Pin</a><Box<dyn Stream<Item = ()>>>, который делает реализации Stream:

use std::pin::Pin;

fn stream_it() -> Pin<Box<dyn Stream<Item=()>>> {
//                ^-- add Pin<...> here
   Box::pin(futures::stream::iter(vec![(), (), ()]))
//      ^-- use Box::pin instead of Box::new
}

В качестве альтернативы, вы также можете изменить тип возврата на Box<dyn Stream<Item = ()> + <a href="https://doc.rust-lang.org/stable/std/marker/trait.Unpin.html" rel="noreferrer">Unpin</a>>, который также реализует Stream, но добавляет некоторые дополнительные требования к потоку, который возвращается (а именно, что он реализует Unpin и, следовательно, безопасно перемещаться в памяти), поэтому он, как правило, менее предпочтителен.

...