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.
Related
I'm new to rust's async programming and encountered something weird.
I want to introduce async in traits without boxing overhead.
I'm using nightly with the #![feature(type_alias_impl_trait)] feature.
Here's minimized code:
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
"test".to_owned().super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output=()>
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a;
fn test_async<P: AsRef<Path>>(&self, p: P) -> Self::Fut<'_, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a,
Self: 'a;
fn super_async<P: AsRef<Path>>(&self, p: P) -> Self::SuperFut<'_, P> {
async move {
self.test_async(p).await;
()
}
}
}
Then I got the error message:
error: implementation of `AsRef` is not general enough
--> src/main.rs:45:5
|
45 | / tokio::spawn(async move {
46 | | "test".to_owned().super_async("x").await
47 | | });
| |______^ implementation of `AsRef` is not general enough
|
= note: `AsRef<Path>` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
= note: ...but `AsRef<Path>` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
I don't understand the error message. If I remove tokio::spawn or impl SuperAsync only for String, then the error disappers. Any help?
I was able to implement this:
#![feature(type_alias_impl_trait)]
use std::path::Path;
use std::future::Future;
type Error = Box<dyn std::error::Error>;
#[tokio::main]
async fn main() -> Result<(), Error> {
tokio::spawn(async move {
"test".to_owned().super_async("x").await
});
Ok(())
}
trait AsyncTrait {
type Fut<'a, P>: Future<Output=()>
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&self, p: &'a P) -> Self::Fut<'a, P>;
}
impl AsyncTrait for String {
type Fut<'a, P> = impl Future<Output = ()> + 'a
where
Self: 'a,
P: AsRef<Path> + 'a + ?Sized;
fn test_async<'a, P: AsRef<Path> + ?Sized>(&self, p: &'a P) -> Self::Fut<'a, P> {
async move {
let bs = p.as_ref();
()
}
}
}
trait SuperAsync: AsyncTrait {
type SuperFut<'a, P>: Future<Output=()>
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P>;
}
impl<T: AsyncTrait> SuperAsync for T {
type SuperFut<'a, P> = impl Future<Output = ()> + 'a
where
P: AsRef<Path> + 'a + ?Sized,
Self: 'a;
fn super_async<'a, P: AsRef<Path> + ?Sized>(&'a self, p: &'a P) -> Self::SuperFut<'a, P> {
async move {
self.test_async(p).await;
()
}
}
}
I changed the definition a little bit, now lifetime 'a is associated to self and P, also changing P into a reference, so we can also associate his and the AsRef lifetimes to to 'a.
By changing P into a reference, we enforce that we are not consuming it, and the data that this reference points to will live longer then the future returned. Maybe there is a way to receive a owned P value and ensure it's lifetime, but I couldn't figure that out.
The ?Sized was added because P solves into the str type (not &str) that is unsized.
I am trying to implement a Visitor pattern in Rust.
I am not able to find a way to support two visitors which return different return value types.
Playground link
trait Visited<R> {
fn accept (self: &Self, v: &dyn Visitor<R>) -> R;
}
trait Visitor<R> {
fn visit_a(&self, a: &A<R>) -> R;
fn visit_b(&self, b: &B) -> R;
}
I've implemented two data structures that can be visited.
// ---- A ----
struct A<R> {
value: String,
b: Box<dyn Visited<R>>,
}
impl<R> Visited<R> for A<R> {
fn accept (&self, v: &dyn Visitor<R>) -> R {
v.visit_a(self)
}
}
// ---- B ----
struct B {
value: i32,
}
impl<R> Visited<R> for B {
fn accept(&self, v: &dyn Visitor<R>) -> R {
v.visit_b(self)
}
}
This worked okay when I just had a concrete visitor.
struct Visitor1 {}
impl Visitor<String> for Visitor1 {
fn visit_a(&self, a: &A<String>) -> String {
let b = a.b.accept(self);
format!("visitor1.visit_a(): {} {}", a.value, b)
}
fn visit_b(&self, b: &B) -> String {
format!("visitor1.visit_b(): {}", b.value)
}
}
However, the whole point of the Visitor pattern is to let multiple algorithm to be applied against the data structure.
When I wanted to add another visitor, I couldn't figure out how to make it work.
struct Visitor2 {}
impl Visitor<i32> for Visitor2 {
fn visit_a(&self, a: &A<i32>) -> i32 {
123
}
fn visit_b(&self, b: &B) -> i32 {
456
}
}
fn main() {
let a = A {
value: "HELLO".to_string(),
b: Box::new(B{ value: 32 })
};
let v1 = Visitor1{};
let s: String = a.accept(&v1);
println!("{}", s);
let v2 = Visitor2{};
let v: i32 = a.accept(&v2);
println!("{}", v);
}
The type of a is inferred as A<String>, and the a.accept(&v2) caused a type mismatch error.
I'd like to tell a to be visited by Visitor1 and Visitor2. How can I do this?
If you return only 'static types, you can use type erasure. The idea is to create a trait, ErasedVisitor, that is not generic and instead return Box<dyn Any>, and implement this trait for all Visitors and use it internally. This mean, though, that you cannot use a generic parameter, only an associated type (otherwise you get "unconstrained type parameter":
trait Visited {
fn accept_dyn(&self, v: &dyn ErasedVisitor) -> Box<dyn Any>;
fn accept<V: Visitor>(&self, v: &V) -> V::Result
where
Self: Sized,
{
*self.accept_dyn(v).downcast().unwrap()
}
}
impl<T: ?Sized + Visited> Visited for &'_ T {
fn accept_dyn(&self, v: &dyn ErasedVisitor) -> Box<dyn Any> {
T::accept_dyn(&**self, v)
}
}
impl<T: ?Sized + Visited> Visited for &'_ mut T {
fn accept_dyn(&self, v: &dyn ErasedVisitor) -> Box<dyn Any> {
T::accept_dyn(&**self, v)
}
}
impl<T: ?Sized + Visited> Visited for Box<T> {
fn accept_dyn(&self, v: &dyn ErasedVisitor) -> Box<dyn Any> {
T::accept_dyn(&**self, v)
}
}
trait Visitor {
type Result: 'static;
fn visit_a(&self, a: &A) -> Self::Result;
fn visit_b(&self, b: &B) -> Self::Result;
}
trait ErasedVisitor {
fn visit_a(&self, a: &A) -> Box<dyn Any>;
fn visit_b(&self, b: &B) -> Box<dyn Any>;
}
impl<V: ?Sized + Visitor> ErasedVisitor for V {
fn visit_a(&self, a: &A) -> Box<dyn Any> {
Box::new(<Self as Visitor>::visit_a(self, a))
}
fn visit_b(&self, b: &B) -> Box<dyn Any> {
Box::new(<Self as Visitor>::visit_b(self, b))
}
}
struct A {
value: String,
b: Box<dyn Visited>,
}
impl Visited for A {
fn accept_dyn(&self, v: &dyn ErasedVisitor) -> Box<dyn Any> {
v.visit_a(self)
}
}
Playground.
But if you can, the best way is to use static polymorphism (generics) instead of dynamic dispatch.
If you want your Visited trait to support multiple return types, you cannot tie the return type to the object itself. Currently, the return type is a generic, so every object has the return type hard-coded into it.
To solve this, you can remove the return type generic from the Visited trait and instead attach it to the accept function.
This has one drawback though: You can no longer create trait objects with this trait. That makes sense, because once cast to a dyn Visited, Rust no longer knows which type it is, making it impossible for the compiler to compile the accept function for all required types. It looses the knowledge between the compilated function and the types that it will get called with.
Normally, this is fine, but in your case, A holds a Box<dyn Visited>. This is impossible, due to the reasons described above.
So if we change Box<dyn Visited> to B, (and do some refactoring with the generics), we can get it to work:
trait Visited {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret;
}
trait Visitor {
type Ret;
fn visit_a(&self, a: &A) -> Self::Ret;
fn visit_b(&self, b: &B) -> Self::Ret;
}
// ---- A ----
struct A {
value: String,
b: B,
}
impl Visited for A {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret {
v.visit_a(self)
}
}
// ---- B ----
struct B {
value: i32,
}
impl Visited for B {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret {
v.visit_b(self)
}
}
struct Visitor1 {}
impl Visitor for Visitor1 {
type Ret = String;
fn visit_a(&self, a: &A) -> String {
let b = a.b.accept(self);
format!("visitor1.visit_a(): {} {}", a.value, b)
}
fn visit_b(&self, b: &B) -> String {
format!("visitor1.visit_b(): {}", b.value)
}
}
struct Visitor2 {}
impl Visitor for Visitor2 {
type Ret = i32;
fn visit_a(&self, _a: &A) -> i32 {
123
}
fn visit_b(&self, _b: &B) -> i32 {
456
}
}
fn main() {
let a = A {
value: "HELLO".to_string(),
b: B { value: 32 },
};
let v1 = Visitor1 {};
let s: String = a.accept(&v1);
println!("{}", s);
let v2 = Visitor2 {};
let v: i32 = a.accept(&v2);
println!("{}", v);
}
visitor1.visit_a(): HELLO visitor1.visit_b(): 32
123
We can make this a little more convenient and make the actual type a generic of A. In contrast to a dyn Visited, this allows Rust to resolve its exact type at compile time, making it possible to compile it.
trait Visited {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret;
}
trait Visitor {
type Ret;
fn visit_a<T: Visited>(&self, a: &A<T>) -> Self::Ret;
fn visit_b(&self, b: &B) -> Self::Ret;
}
// ---- A ----
struct A<T> {
value: String,
b: T,
}
impl<T: Visited> Visited for A<T> {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret {
v.visit_a(self)
}
}
// ---- B ----
struct B {
value: i32,
}
impl Visited for B {
fn accept<V: Visitor>(&self, v: &V) -> V::Ret {
v.visit_b(self)
}
}
struct Visitor1 {}
impl Visitor for Visitor1 {
type Ret = String;
fn visit_a<T: Visited>(&self, a: &A<T>) -> String {
let b = a.b.accept(self);
format!("visitor1.visit_a(): {} {}", a.value, b)
}
fn visit_b(&self, b: &B) -> String {
format!("visitor1.visit_b(): {}", b.value)
}
}
struct Visitor2 {}
impl Visitor for Visitor2 {
type Ret = i32;
fn visit_a<T: Visited>(&self, _a: &A<T>) -> i32 {
123
}
fn visit_b(&self, _b: &B) -> i32 {
456
}
}
fn main() {
let a = A {
value: "HELLO".to_string(),
b: B { value: 32 },
};
let v1 = Visitor1 {};
let s: String = a.accept(&v1);
println!("{}", s);
let v2 = Visitor2 {};
let v: i32 = a.accept(&v2);
println!("{}", v);
}
visitor1.visit_a(): HELLO visitor1.visit_b(): 32
123
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);
}
I'd like to use a future which returns a Vec<String>, iterate over this in a future-stream and give the values to another future and the result of this future should be handled. The complete thing should be a future, too.
What's the way to go? I've tried different approaches and with all I've got type problems, which I don't understand.
Why there are these nested future result type signatures? Shouldn't this become the final result? Why doesn't the compiler know the types?
error[E0631]: type mismatch in closure arguments
--> src/lib.rs:45:18
|
45 | .then(|x: Result<(), ()>| ok(()))
| ^^^^ -------------------------- found signature of `fn(std::result::Result<(), ()>) -> _`
| |
| expected signature of `fn(std::result::Result<std::vec::Vec<tokio::prelude::future::Then<tokio::prelude::future::Then<impl tokio::prelude::Future, tokio::prelude::future::FutureResult<(), ()>, [closure#src/lib.rs:35:31: 41:26]>, tokio::prelude::future::FutureResult<(), _>, [closure#src/lib.rs:42:31: 42:57]>>, _>) -> _`
I've setup a Playground for this
extern crate tokio;
use tokio::prelude::future::ok;
use tokio::prelude::*;
#[allow(dead_code)]
pub fn test_future<F>(f: F) -> Result<F::Item, F::Error>
where
F: IntoFuture,
F::Future: Send + 'static,
F::Item: Send + 'static,
F::Error: Send + 'static,
{
let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
runtime.block_on(f.into_future())
}
#[allow(dead_code)]
fn fut(el: &String) -> impl Future<Item = String, Error = std::io::Error> {
ok((el.to_string() + "-ok").to_string())
}
#[test]
fn reporting_future_result_test() {
let v = vec![
vec!["a".to_string(), "b".to_string()],
vec!["a".to_string(), "b".to_string()],
];
let f = stream::iter_ok(v.iter().cloned())
.map(|el: Vec<String>| {
stream::iter_ok(el.iter().cloned())
.map(|ell: String| {
fut(&ell)
.then(|x: Result<String, std::io::Error>| {
match x {
Ok(s) => println!("{}", s),
Err(e) => println!("{:?}", e),
};
ok(())
})
.then(|x: Result<(), ()>| ok(()))
})
.collect()
.then(|x: Result<(), ()>| ok(()))
})
.collect()
.then(|x: Result<Vec<_>, std::io::Error>| ok(()));
let r = test_future(f);
match r {
Ok(x) => println!("{:?}", x),
Err(_) => println!("error"),
}
}
extern crate tokio; // 0.1.11
use tokio::prelude::*;
// a future which returns a Vec<String>
fn makes_strings() -> impl Future<Item = Vec<String>, Error = ()> {
future::ok(vec![])
}
fn make_new_string(el: String) -> impl Future<Item = String, Error = ()> {
future::ok(el + "-ok")
}
fn iterate_over() -> impl Future<Item = Vec<String>, Error = ()> {
makes_strings().and_then(|v| {
// iterate over this
let strings = v.into_iter();
// give the values to another future
let futures = strings.map(make_new_string);
// The complete thing should be a future
future::join_all(futures)
})
}
future::ok
Future::and_then
future::join_all
I have this code:
use std::fmt::Debug;
struct S<A>
where
for<'a> A: Debug + 'a,
{
f: Box<Fn(A) -> i32>,
}
impl<A> S<A>
where
for<'a> A: Debug + 'a,
{
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A>
where
for<'a> A: Debug + 'a,
{
S::<A> { f }
}
fn helper() {
let x = create::<&i32>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
let x = helper();
}
It's failed to compile:
error[E0310]: the parameter type `A` may not live long enough
In code 2, I changed Fn(A) -> i32 to Fn(&A) -> i32, the code works.
...
f: Box<Fn(&A) -> i32>,
...
Since A is argument of Fn trait, it's a type that has Higher-Rank lifetime. It shouldn't be affected by the lifetime of struct S<A> .
But why can't code 1 be compiled?
How can I workaround it for borrow or non-borrow type A?
There is no easy way to make helper work in current Rust, even if you remove all the for<'a> A: Debug + 'a, bounds (which only further restricts what types A can be, whereas you want to allow more).
This is as simple as I can make your example:
struct S<A> {
f: Box<Fn(A) -> i32>,
}
impl<A> S<A> {
fn call(&self, a: A) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A>(f: Box<Fn(A) -> i32>) -> S<A> {
S { f }
}
fn helper() {
let x = create(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
The reason it doesn't work is that A "comes from the outside", and Rust can't infer that you want for<'a> S<&'a A>, it can't even talk about such a type.
Note that if let arg = 333; is placed above let x, this example does compile (because it infers a reference to arg specifically, not a for<'a>).
The closest you can get today is with an associated type on a trait with a lifetime parameter, e.g.:
// Emulating `type Type<'a>` by moving `'a` to the trait.
trait Apply<'a> {
type Type;
}
struct Plain<T>(std::marker::PhantomData<T>);
impl<'a, T> Apply<'a> for Plain<T> {
type Type = T;
}
struct Ref<T: ?Sized>(std::marker::PhantomData<T>);
impl<'a, T: ?Sized + 'a> Apply<'a> for Ref<T> {
type Type = &'a T;
}
struct S<A: for<'a> Apply<'a>> {
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
}
impl<A: for<'a> Apply<'a>> S<A> {
fn call<'a>(&self, a: <A as Apply<'a>>::Type) {
println!("Return {:?}", (self.f)(a));
}
}
fn create<A: for<'a> Apply<'a>>(
f: Box<for<'a> Fn(<A as Apply<'a>>::Type) -> i32>,
) -> S<A> {
S { f }
}
fn helper() {
let x = create::<Ref<i32>>(Box::new(|x: &i32| *x * 2));
let arg = 333;
x.call(&arg);
}
fn main() {
helper();
}
However, it turns out that this encoding hits https://github.com/rust-lang/rust/issues/52812, so it's not actually usable at the moment (and I'm not aware of an workaround).