This code works:
async fn player_create<'a>(
&'a self,
team_id: &'a str,
_input: &'a PlayerInput,
lambda: &'a Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
) -> Result<DomainPlayer> {
let tx = Arc::new(Mutex::new(self.pool.begin().await.unwrap()));
let shirt_next_value = Box::new({
let tx = tx.clone();
move |model: &'a str,
name: &'a str| -> Pin<Box<dyn Future<Output = Result<i64>> + Send>> {
let tx = tx.clone();
Box::pin(async move {
self::Shirt::shirt_get_next_and_increase(
self,
&mut *tx.lock().await,
team_id,
model,
name,
)
.await
})
}
});
let domain_player = lambda(PlayerCreateLambdaArgs {
shirt_next_value,
})
.await?;
let mut res = PgPlayer::insert(&mut *tx.lock().await, team_id, &domain_player).await?;
Arc::try_unwrap(tx).unwrap().into_inner().commit().await?;
Ok(res.into())
}
but I would like to remove the -> Pin<Box<dyn Future<Output = Result<i64>> + Send>> line, so I did:
async fn player_create<'a>(
&'a self,
team_id: &'a str,
_input: &'a PlayerInput,
lambda: &'a Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
) -> Result<DomainPlayer> {
let tx = Arc::new(Mutex::new(self.pool.begin().await.unwrap()));
let shirt_next_value = Box::new({
let tx = tx.clone();
move |model: &'a str, name: &'a str| {
let tx = tx.clone();
Box::pin(async move {
self::Shirt::shirt_get_next_and_increase(
self,
&mut *tx.lock().await,
team_id,
model,
name,
)
.await
})
}
});
let domain_player = lambda(PlayerCreateLambdaArgs {
shirt_next_value,
})
.await?;
let mut res = PgPlayer::insert(&mut *tx.lock().await, team_id, &domain_player).await?;
Arc::try_unwrap(tx).unwrap().into_inner().commit().await?;
Ok(res.into())
}
but if I remove it the error is:
error[E0271]: expected `[closure#src\main.rs:40:13: 41:33]` to be a closure that returns `Pin<Box<dyn std::future::Future<Output = Result<i64, common::error::Error>> + std::marker::Send>>`, but it returns `Pin<Box<impl std::future::Future<Output = Result<i64, common::error::Error>>>>`
|
46 | Box::pin(async move {
| _____________________________________-
47 | | self::Shirt::shirt_get_next_and_increase(
48 | | self,
49 | | &mut *tx.lock().await,
... |
54 | | .await
55 | | })
| |_________________- the found `async` block
...
63 | shirt_next_value,
| ^^^^^^^^^^^^^^^^^^^ expected trait object `dyn std::future::Future`, found opaque type
|
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 = Result<i64, common::error::Error>> + std::marker::Send>>`
found struct `Pin<Box<impl std::future::Future<Output = Result<i64, common::error::Error>>>>`
= note: required for the cast from `[closure#src\main.rs:40:13: 41:33]` to the object type `dyn FnOnce(&str, &str) -> Pin<Box<dyn std::future::Future<Output = Result<i64, common::error::Error>> + std::marker::Send>> + Sync + std::marker::Send`
Why?
The problem can be simplied to the following:
use std::any::Any;
fn test(a: &Box<dyn Any>) {}
fn main() {
let a = Box::new(5);
test(&a);
}
This is due to the fact that the type won't implicitly convert to dynamic dispatch.
You can solve it by specifying the conversion to the type you want.
use std::any::Any;
fn test(a: &Box<dyn Any>) {}
fn main() {
let a = Box::new(5) as _;
test(&a);
}
Related
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>>>>>>>`
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 }
}
I simplified a lot the code of my real project to narrowing down one issue I cannot understand how to fix.
Rust Explorer Playground here.
The code:
use std::{future::Future, pin::Pin, sync::Arc};
#[derive(Default)]
pub struct PlayerInput {
pub id: String,
pub name: String,
pub location: String,
// pub team: Box<Team>,
// others...
}
#[derive(Debug, sqlx::FromRow)]
pub struct DomainPlayer {
pub id: String,
pub name: String,
pub location: String,
pub shirt_number: i64,
// pub team: Box<Team>,
// others...
}
impl DomainPlayer {
fn new(id: &str, name: &str, location: &str, shirt_number: i64) -> Self {
Self {
id: id.to_string(),
name: name.to_string(),
location: location.to_string(),
shirt_number,
}
}
}
pub struct PlayerCreateLambdaArgs<'a> {
// other needed fields here
pub shirt_next_value: Box<
dyn FnOnce(&'a str) -> Pin<Box<dyn Future<Output = Result<i64, String>> + Send + 'a>>
+ Send
+ Sync
+ 'a,
>,
}
async fn player_create<'a>(
pool: Arc<sqlx::PgPool>,
_team_id: &'a str,
_input: &'a PlayerInput,
lambda: &(dyn for<'b> Fn(
PlayerCreateLambdaArgs<'b>,
) -> Pin<Box<dyn Future<Output = Result<DomainPlayer, String>> + Send + 'b>>
+ Sync
+ '_),
) -> Result<DomainPlayer, String> {
let mut tx = pool.begin().await.unwrap();
let domain_player = lambda(PlayerCreateLambdaArgs {
shirt_next_value: Box::new(|model: &str| {
Box::pin(shirt_get_next_and_increase(&mut tx, model))
}),
})
.await?;
let res =
sqlx::query_as::<_, DomainPlayer>("INSERT INTO player (...) VALUES (...) RETURNING *")
.bind(domain_player.id)
.bind(domain_player.shirt_number)
.fetch_one(&mut *tx)
.await
.unwrap();
Ok(res)
}
async fn shirt_get_next_and_increase(
tx: &mut sqlx::PgConnection,
model: &str,
) -> Result<i64, String> {
// Here I'm awaiting an async call for DB operations using the same DB transacion of the caller (_tx)...
dbg!(tx);
// use model here...
dbg!(model);
let res = 123;
Ok(res)
}
pub async fn handle(pool: Arc<sqlx::PgPool>, input: &PlayerInput) -> Result<DomainPlayer, String> {
let res = player_create(pool, "team", input, &|args| {
let input = input;
Box::pin(async move {
let shirt_number = (args.shirt_next_value)("player").await?;
let o = DomainPlayer::new(&input.id, &input.name, &input.location, shirt_number);
Ok(o)
})
})
.await?;
Ok(res)
}
#[tokio::main]
async fn main() -> Result<(), String> {
let pool = Arc::new(sqlx::PgPool::connect("fake_url").await.unwrap());
let new_player_input = PlayerInput {
id: "abc".to_string(),
name: "John".to_string(),
location: "Boston".to_string(),
};
let player = handle(pool.clone(), &new_player_input).await?;
dbg!(player);
Ok(())
}
The error:
error: lifetime may not live long enough
--> src/main.rs:104:9
|
100 | pub async fn handle(pool: Arc<sqlx::PgPool>, input: &PlayerInput) -> Result<DomainPlayer, String> {
| - let's call the lifetime of this reference `'1`
...
104 | / Box::pin(async move {
105 | | let shirt_number = (args.shirt_next_value)("player").await?;
106 | |
107 | | let o = DomainPlayer::new(&input.id, &input.name, &input.location, shirt_number);
108 | |
109 | | Ok(o)
110 | | })
| |__________^ returning this value requires that `'1` must outlive `'static`
Let's rewrite your handle a little bit, to make it easier to explain:
pub async fn handle(pool: Arc<sqlx::PgPool>, input: &PlayerInput) -> Result<DomainPlayer, String> {
let lambdaref: &(dyn for<'b> Fn(PlayerCreateLambdaArgs<'b>) -> Pin<Box<dyn Future<Output=Result<DomainPlayer, String>> + Send + 'b>> + Sync) = &|args: PlayerCreateLambdaArgs| {
let input = input;
Box::pin(async move {
let shirt_number = (args.shirt_next_value)("player").await?;
let o = DomainPlayer::new(&input.id, &input.name, &input.location, shirt_number);
Ok(o)
})
};
let res = player_create(pool, "team", input, lambdaref)
.await?;
Ok(res)
}
so lambdaref references a lambda, which takes a PlayerCreateLambdaArgs<'b> and produces a a boxed future guaranteed to live at least as long as 'b.
And it also captures input which we know lives at least as long as 'a.
So we can deduce, that 'a must live at least as long as 'b otherwise the boxed future couldn't live at least as long as 'b, as it needs access to input, too.
But 'b is not yet determined and can be chosen by the caller of the lamba. The caller of the lambda could choose to use a PlayerCreateLambdaArgs<'static> and therefore choose 'b to be 'static.
So rustc has to demand 'a to be 'static so the lambda can be called with every possible 'b.
If you can change player_create and PlayerCreateLambdaArgs, then you can
side step this issue. player_create already gets input and could just
hand it down to the lamba inside PlayerCreateLambdaArgs, too.
Like in https://www.rustexplorer.com/b/8vonk0
If you don't mind me saying, it looks all a bit convoluted, what is it, you really want to achieve?
Hi I try to pass my closure to mockall crate returning function in following way:
pub fn set_dialog_game_selection(dialogs: &mut Box<MockAsk>, steam_id: String) {
dialogs
.expect_ask_for_game_decision_if_needed_and_set_game_to_launch()
.returning(ask_for_game_decision_if_needed_return_mock(steam_id));
}
pub fn ask_for_game_decision_if_needed_return_mock<'x, 'y>(
steam_id: String,
) -> Box<dyn Fn(&'x mut REvilConfig, &'y mut REvilManagerState) -> ResultDialogsErr<()> + Send> {
let default = move |_: &'x mut REvilConfig, state: &'y mut REvilManagerState| {
state.selected_game_to_launch = Some(steam_id.clone());
Ok(())
};
return Box::new(default);
}
but I get
error[E0308]: mismatched types
--> src\tests\integration.rs:128:14
|
128 | .returning(ask_for_game_decision_if_needed_return_mock(steam_id.to_string()));
| ^^^^^^^^^ lifetime mismatch
|
= note: expected associated type `<dyn Fn(&mut configStruct::REvilConfig, &mut rManager_header::REvilManagerState) -> Result<(), error_stack::Report<DialogsErrors>> + Send as FnOnce<(&mut configStruct::REvilConfig, &mut rManager_header::REvilManagerState)>>::Output`
found associated type `<dyn Fn(&mut configStruct::REvilConfig, &mut rManager_header::REvilManagerState) -> Result<(), error_stack::Report<DialogsErrors>> + Send as FnOnce<(&mut configStruct::REvilConfig, &mut rManager_header::REvilManagerState)>>::Output`
note: the lifetime requirement is introduced here
--> src\dialogs\dialogs.rs:54:10
|
54 | ) -> ResultDialogsErr<()>;
| ^^^^^^^^^^^^^^^^^^^^
P.S I'm writting mod manager for Resident Evil game that's why "REvil" :p
P.S2 In the end I managed to rewrite it like:
pub fn set_dialog_game_selection(dialogs: &mut Box<MockAsk>, steam_id: String) {
dialogs
.expect_ask_for_game_decision_if_needed_and_set_game_to_launch()
.returning(move |_, state| {
set_game_decision(steam_id.clone(), state);
Ok(())
});
}
pub fn set_game_decision(steam_id: String, state: &mut REvilManagerState) {
state.selected_game_to_launch = Some(steam_id);
}
But why my first approach doeasn't work? :(
Function signature I'm trying to mock is as follow:
pub type ResultDialogsErr<T> = Result<T, DialogsErrors>;
fn ask_for_game_decision_if_needed_and_set_game_to_launch(
&mut self,
config: &mut REvilConfig,
state: &mut REvilManagerState,
) -> ResultDialogsErr<()>;
If you remove the manual lifetime annotations, it works:
pub fn ask_for_game_decision_if_needed_return_mock(
steam_id: String,
) -> Box<dyn Fn(&mut REvilConfig, &mut REvilManagerState) -> DynResult<()> + Send> {
let default = move |_: &mut REvilConfig, state: &mut REvilManagerState| {
state.selected_game_to_launch = Some(steam_id.clone());
Ok(())
};
return Box::new(default);
}
In the example below, openvpn_client is created before the on_poll_read and on_poll_write. Therefore, it should be destructed after them (Playground):
use core::task::{Context, Poll};
use hyper::client::connect::{Connected, Connection};
use std::sync::{Arc, Mutex};
pub type OnPollRead = Arc<
dyn Fn(&mut Context<'_>, &mut tokio::io::ReadBuf<'_>) -> Poll<std::io::Result<()>>
+ Send
+ Sync,
>;
pub type OnPollWrite = Arc<
dyn Fn(&mut Context<'_>, &[u8]) -> Poll<core::result::Result<usize, std::io::Error>>
+ Send
+ Sync,
>;
#[derive(Clone)]
pub struct CustomTransporter {
on_poll_read: Option<OnPollRead>,
on_poll_write: Option<OnPollWrite>,
}
pub struct OVPNClient {}
impl OVPNClient {
pub fn send(&self, buffer: &[u8]) {}
}
unsafe impl Send for OVPNClient {}
unsafe impl Send for CustomTransporter {}
impl CustomTransporter {
pub fn new(
on_poll_read: Option<OnPollRead>,
on_poll_write: Option<OnPollWrite>,
) -> CustomTransporter {
CustomTransporter {
on_poll_read: on_poll_read,
on_poll_write: on_poll_write,
}
}
}
fn main() {
let openvpn_client = Arc::new(Mutex::new(OVPNClient {}));
let on_poll_read = Arc::new(
|context: &mut Context, buffer: &mut tokio::io::ReadBuf| -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
},
);
let on_poll_write = Arc::new(
|context: &mut Context,
buffer: &[u8]|
-> Poll<core::result::Result<usize, std::io::Error>> {
openvpn_client.lock().unwrap().send(buffer);
Poll::Ready(Ok(0))
},
);
let connector = CustomTransporter::new(Some(on_poll_read), Some(on_poll_write));
}
Error:
error[E0597]: `openvpn_client` does not live long enough
--> src/main.rs:57:13
|
54 | / |context: &mut Context,
55 | | buffer: &[u8]|
56 | | -> Poll<core::result::Result<usize, std::io::Error>> {
| |_____________________________________________________________- value captured here
57 | openvpn_client.lock().unwrap().send(buffer);
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
62 | let connector = CustomTransporter::new(Some(on_poll_read), Some(on_poll_write));
| ------------- cast requires that `openvpn_client` is borrowed for `'static`
63 | }
| - `openvpn_client` dropped here while still borrowed
Why does this error happen?
I have a problem with a generic function (Rust Playground):
use core::any::Any;
use std::fmt;
use std::fmt::Debug;
type BoolPtr = Box<dyn Fn(String, bool) -> Result<(), String>>;
type U8Ptr = Box<dyn Fn(String, u8) -> Result<(), String>>;
type U16Ptr = Box<dyn Fn(String, u8) -> Result<(), String>>;
pub enum WriteCallbackClosure {
BOOL(BoolPtr),
U8(U8Ptr),
U16(U16Ptr),
}
pub fn create_cb_enum<T, Closure>(
var_name: &String,
var_type: &String,
callback: Closure,
) -> Option<WriteCallbackClosure>
where
Closure: 'static + Sized + std::panic::UnwindSafe + std::panic::RefUnwindSafe + Send + Sync,
T: Any,
Closure: Fn(String, T) -> Result<(), String>,
{
let box_cb = Box::new(callback);
if var_name == "BOOL" {
return Some(WriteCallbackClosure::BOOL(box_cb));
} else if var_name == "U8" {
return Some(WriteCallbackClosure::U8(box_cb));
}
return None;
}
fn main() {
let f1 = move |name, state: bool| {
println!("name: {}, state: {}", name, state);
return Ok(());
};
let v1 = create_cb_enum(&"v1_bool".to_string(), &"BOOL".to_string(), f1);
let f2 = move |name, state: u8| {
println!("name: {}, state: {}", name, state);
return Ok(());
};
let v2 = create_cb_enum(&"v2_u8".to_string(), &"U8".to_string(), f2);
}
The compiler suggests:
error[E0277]: expected a `std::ops::Fn<(std::string::String, bool)>` closure, found `Closure`
--> src/main.rs:28:48
|
28 | return Some(WriteCallbackClosure::BOOL(box_cb));
| ^^^^^^ expected an `Fn<(std::string::String, bool)>` closure, found `Closure`
|
= note: required for the cast to the object type `dyn std::ops::Fn(std::string::String, bool) -> std::result::Result<(), std::string::String>`
help: consider further restricting type parameter `Closure`
|
23 | Closure: Fn(String, T) -> Result<(), String>, Closure: std::ops::Fn<(std::string::String, bool)>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: expected a `std::ops::Fn<(std::string::String, u8)>` closure, found `Closure`
--> src/main.rs:30:46
|
30 | return Some(WriteCallbackClosure::U8(box_cb));
| ^^^^^^ expected an `Fn<(std::string::String, u8)>` closure, found `Closure`
|
= note: required for the cast to the object type `dyn std::ops::Fn(std::string::String, u8) -> std::result::Result<(), std::string::String>`
help: consider further restricting type parameter `Closure`
|
23 | Closure: Fn(String, T) -> Result<(), String>, Closure: std::ops::Fn<(std::string::String, u8)>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It's weird because function is a generic called Closure: Fn(String, T) -> Result<(), String>.
Updated to reflect non-static Fn (see original answer below)
Playground
type BoolPtr<'a> = Box<dyn 'a + FnMut(String, bool) -> Result<(), String>>;
type U8Ptr<'a> = Box<dyn 'a + FnMut(String, u8) -> Result<(), String>>;
type U16Ptr<'a> = Box<dyn 'a + FnMut(String, u16) -> Result<(), String>>;
pub enum WriteCallbackClosure<'a>
{
BOOL(BoolPtr<'a>),
U8(U8Ptr<'a>),
U16(U16Ptr<'a>),
}
trait WriteCallbackCreatorTrait<T> {
fn from_fn<'a, P> (v: P) -> Option<WriteCallbackClosure<'a>>
where P: 'a + Sized + FnMut(String, T) -> Result<(), String>;
}
impl WriteCallbackCreatorTrait<bool> for WriteCallbackClosure<'_> {
fn from_fn<'a, P>(v: P) -> Option<WriteCallbackClosure<'a>>
where P: 'a + Sized + FnMut(String, bool) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::BOOL(boxed);
return Some(closure);
}
}
impl WriteCallbackCreatorTrait<u8> for WriteCallbackClosure<'_> {
fn from_fn<'a, P>(v: P) -> Option<WriteCallbackClosure<'a>>
where P: 'a + Sized + FnMut(String, u8) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::U8(boxed);
return Some(closure);
}
}
impl WriteCallbackCreatorTrait<u16> for WriteCallbackClosure<'_> {
fn from_fn<'a, P>(v: P) -> Option<WriteCallbackClosure<'a>>
where P: 'a + Sized + FnMut(String, u16) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::U16(boxed);
return Some(closure);
}
}
fn main() {
let mut my_state = false;
let f1 = |name, state: bool| {
println!("name: {}, state: {}", name, state);
my_state = state;
return Ok(());
};
let _v1 = WriteCallbackClosure::from_fn(f1);
}
Original answer
It looks like you're looking for something like this (Playground)
type BoolPtr = Box<dyn Fn(String, bool) -> Result<(), String>>;
type U8Ptr = Box<dyn Fn(String, u8) -> Result<(), String>>;
type U16Ptr = Box<dyn Fn(String, u16) -> Result<(), String>>;
pub enum WriteCallbackClosure
{
BOOL(BoolPtr),
U8(U8Ptr),
U16(U16Ptr),
}
trait WriteCallbackCreatorTrait<T> {
fn from_fn<P> (v: P) -> Option<WriteCallbackClosure>
where P: 'static + Sized + Fn(String, T) -> Result<(), String>;
}
impl WriteCallbackCreatorTrait<bool> for WriteCallbackClosure {
fn from_fn<P>(v: P) -> Option<WriteCallbackClosure>
where P: 'static + Sized + Fn(String, bool) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::BOOL(boxed);
return Some(closure);
}
}
impl WriteCallbackCreatorTrait<u8> for WriteCallbackClosure {
fn from_fn<P>(v: P) -> Option<WriteCallbackClosure>
where P: 'static + Sized + Fn(String, u8) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::U8(boxed);
return Some(closure);
}
}
impl WriteCallbackCreatorTrait<u16> for WriteCallbackClosure {
fn from_fn<P>(v: P) -> Option<WriteCallbackClosure>
where P: 'static + Sized + Fn(String, u16) -> Result<(), String>
{
let boxed = Box::new(v);
let closure = WriteCallbackClosure::U16(boxed);
return Some(closure);
}
}
fn main() {
let f1 = move |name, state: bool| {
println!("name: {}, state: {}", name, state);
return Ok(());
};
let _v1 = WriteCallbackClosure::from_fn(f1);
let f2 = move |name, state: u8| {
println!("name: {}, state: {}", name, state);
return Ok(());
};
let _v2 = WriteCallbackClosure::from_fn(f2);
}
The original create_cb_enum doesn't specify the particular type for Closure's second parameter.