Трейт 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,
}