Трейт IntoFuture
#![allow(unused)] fn main() { pub trait IntoFuture { type Output; type IntoFuture: Future<Output = Self::Output>; // Обязательный метод fn into_future(self) -> Self::IntoFuture; } }
Преобразование в Future.
Реализуя IntoFuture для типа, вы определяете, как он будет преобразован в фьючер.
Десугаризация .await
Ключевое слово .await десугаризуется в вызов IntoFuture::into_future перед опросом фьючера до завершения. IntoFuture реализован для всех T: Future, что означает, что метод into_future будет доступен для всех фьючеров.
#![allow(unused)] fn main() { use std::future::IntoFuture; let v = async { "meow" }; let mut fut = v.into_future(); assert_eq!("meow", fut.await); }
Асинхронные билдеры
При ручной реализации фьючеров часто возникает выбор между реализацией Future или IntoFuture для типа. Реализация Future является хорошим выбором в большинстве случаев. Но реализация IntoFuture наиболее полезна при реализации типов "асинхронных билдеров", которые позволяют изменять свои значения несколько раз перед использованием .await.
use std::future::{ready, Ready, IntoFuture}; /// Умножает два числа в конечном счете pub struct Multiply { num: u16, factor: u16, } impl Multiply { /// Создает новый экземпляр `Multiply`. pub fn new(num: u16, factor: u16) -> Self { Self { num, factor } } /// Устанавливает число для умножения на множитель. pub fn number(mut self, num: u16) -> Self { self.num = num; self } /// Устанавливает множитель для умножения числа. pub fn factor(mut self, factor: u16) -> Self { self.factor = factor; self } } impl IntoFuture for Multiply { type Output = u16; type IntoFuture = Ready<Self::Output>; fn into_future(self) -> Self::IntoFuture { ready(self.num * self.factor) } } // ПРИМЕЧАНИЕ: Rust пока не имеет функции `async fn main`, эта функциональность // в настоящее время существует только в экосистеме. async fn run() { let num = Multiply::new(0, 0) // инициализирует билдер с number: 0, factor: 0 .number(2) // изменяет число на 2 .factor(2) // изменяет множитель на 2 .await; // преобразует в фьючер и ожидает assert_eq!(num, 4); }
Использование в границах трейтов
Использование IntoFuture в границах трейтов позволяет функции быть обобщенной как для Future, так и для IntoFuture. Это удобно для пользователей функции, так как при использовании им не нужно делать дополнительный вызов IntoFuture::into_future для получения экземпляра Future:
#![allow(unused)] fn main() { use std::future::IntoFuture; /// Преобразует вывод фьючера в строку. async fn fut_to_string<Fut>(fut: Fut) -> String where Fut: IntoFuture, Fut::Output: std::fmt::Debug, { format!("{:?}", fut.await) } }
Обязательные ассоциированные типы
type Output
Результат, который фьючер произведет при завершении.
type IntoFuture: Future<Output = Self::Output>
В какой тип фьючера мы преобразуем это значение?
Обязательные методы
fn into_future(self) -> Self::IntoFuture
Создает фьючер из значения.
Примеры
Базовое использование:
#![allow(unused)] fn main() { use std::future::IntoFuture; let v = async { "meow" }; let mut fut = v.into_future(); assert_eq!("meow", fut.await); }
Реализаторы
#![allow(unused)] fn main() { impl<F> IntoFuture for F where F: Future, }