Search code examples
rustyew

async hook in yew, expected struct `Pin<Box<(dyn, found struct `Pin<Box<impl


Minimal Reproducible example:

use std::pin::Pin;
use std::rc::Rc;
use yew::prelude::*;


// pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T>  + 'a>>;

pub struct UseSignTxHandle {
    api_call: Rc<dyn Fn() -> Pin<Box<dyn std::future::Future<Output = Option<String>>>>>,
}

impl UseSignTxHandle {
    pub fn api_call(&self) {
        (self.api_call)();
    }
}

impl Clone for UseSignTxHandle {
    fn clone(&self) -> Self {
        Self {
            api_call: self.api_call.clone(),
        }
    }
}

#[hook]
pub fn use_sign_tx_handle() -> UseSignTxHandle
{
    let state: UseStateHandle<Option<String>> = use_state(|| None);
    let phrase_option = state.clone();
    let api_call = {
        let phrase_option = phrase_option.clone();
        Rc::new(move || {
            let phrase_option = phrase_option.clone();
            let pin = Box::pin( async {
                if let Some(seed) = *phrase_option {
                    let events = "Hello World".to_string();
                    Some(events)
                } else {
                    None
                }
            });
            pin
            })
           
    };
    UseSignTxHandle { api_call }
}

Error:

UseSignTxHandle { api_call }
   |                         ^^^^^^^^ expected trait object `dyn std::future::Future`, found opaque type
   |
  ::: /home/amiya/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/mod.rs:72:43
   |
72 |   pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
   |                                             ------------------------------- the found opaque type
   |
   = note: expected struct `Pin<Box<(dyn std::future::Future<Output = std::option::Option<std::string::String>> + 'static)>>`
              found struct `Pin<Box<impl std::future::Future<Output = std::option::Option<std::string::String>>>>`
   = note: required for the cast from `[closure@src/components/accounts/hooks/sign_tx_handle.rs:35:17: 35:24]` to the object type `dyn Fn() -> Pin<Box<(dyn std::future::Future<Output = std::option::Option<std::string::String>> + 'static)>>`

Trying to build async function as hook in yew:

use crate::components::accounts::account_store::PhraseStore;
use crate::components::accounts::functions::get_from_seed_sr;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use subxt::{tx::PairSigner, PolkadotConfig};
use yew::prelude::*;
use yewdux::prelude::*;
use subxt::blocks::ExtrinsicEvents;
use subxt::config::WithExtrinsicParams;
use subxt::tx::BaseExtrinsicParams;
use subxt::tx::PlainTip;
use subxt::SubstrateConfig;

// pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T>  + 'a>>;

pub struct UseSignTxHandle {
    api_call: Rc<dyn Fn() -> Pin<Box<dyn std::future::Future<Output = std::option::Option<ExtrinsicEvents<WithExtrinsicParams<SubstrateConfig, BaseExtrinsicParams<SubstrateConfig, PlainTip>>>>>>>>,
}

impl UseSignTxHandle {
    pub fn api_call(&self) {
        (self.api_call)();
    }
}

impl Clone for UseSignTxHandle {
    fn clone(&self) -> Self {
        Self {
            api_call: self.api_call.clone(),
        }
    }
}

#[hook]
pub fn use_sign_tx_handle<T>(tx: T) -> UseSignTxHandle
where
    T: subxt::tx::TxPayload + 'static,
{
    let (store, _) = use_store::<PhraseStore>();
    let phrase_option = store.mnemonic_phrase.clone();
    let api_call = {
        let phrase_option = phrase_option.clone();
        Rc::new(move || {
            let phrase_option = phrase_option.clone();
            let pin = Box::pin(async {
                let client =
                    subxt::client::OnlineClient::<PolkadotConfig>::from_url("ws://127.0.0.1:9944")
                        .await
                        .unwrap();
                if let Some(seed) = phrase_option {
                    let pair = get_from_seed_sr(&seed);
                    let signer = PairSigner::new(pair);
                    let result = client
                        .tx()
                        .sign_and_submit_then_watch_default(&tx, &signer)
                        .await
                        .unwrap()
                        .wait_for_finalized()
                        .await
                        .unwrap();
                    let events = result.fetch_events().await.unwrap();
                    Some(events)
                } else {
                    None
                }
            });
            pin
        })
    };
    UseSignTxHandle { api_call }
}

Gives error:

 UseSignTxHandle { api_call }
   |                         ^^^^^^^^ expected trait object `dyn std::future::Future`, found opaque type

note: expected struct `Pin<Box<(dyn std::future::Future<Output = std::option::Option<ExtrinsicEvents<WithExtrinsicParams<SubstrateConfig, BaseExtrinsicParams<SubstrateConfig, PlainTip>>>>> + 'static)>>`
         found struct `Pin<Box<impl std::future::Future<Output = std::option::Option<ExtrinsicEvents<WithExtrinsicParams<SubstrateConfig, BaseExtrinsicParams<SubstrateConfig, PlainTip>>>>>>>`

Solution

  • As I suspected the as _ works. It is needed to convert the concrete Pin<Box<impl Future<…>>> into a trait object Pin<Box<dyn Future<…>>> expected by UseSignTxHandle:

    #[hook]
    pub fn use_sign_tx_handle<T>(tx: T) -> UseSignTxHandle
    where
        T: subxt::tx::TxPayload + 'static,
    {
        let state: UseStateHandle<Option<String>> = use_state(|| None);
        let api_call = {
            Rc::new(move || {
                let state = state.clone();
                Box::pin(async move { state.as_ref().map(|_| "Hello World".to_string()) }) as _
            })
        };
        UseSignTxHandle { api_call }
    }