Confused of how `Box<dyn Fn(Args)>` is interpreted - rust

I don't really know how to put this question, but can somebody explain why this works:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=969cf50f66746c4aa3941200e01f1570
enum SlotFn<'a, Comp, Args = (), R = ()>
where Args: 'static,
R: Default + 'static,
{
SimpleFn(fn(Args) -> R),
MemberFn(fn(&'a Comp) -> R),
MemberFnMut(fn(&'a mut Comp, Args) -> R),
}
impl<'a, Comp, Args, R> SlotFn<'a, Comp, Args, R>
where Args: 'static,
R: Default + 'static,{
fn from_member(f: fn(&'a Comp) -> R) -> Self{
SlotFn::MemberFn(f)
}
fn from_member_mut(f: fn(&'a mut Comp, Args) -> R) -> Self {
SlotFn::MemberFnMut(f)
}
fn emit(&self, comp: &'a Comp, args: Args) -> R {
match self {
SlotFn::SimpleFn(f) => f(args),
SlotFn::MemberFn(f) => f(comp),
_ => Default::default()
}
}
fn emit_mut(&mut self, comp: &'a mut Comp, args: Args) -> R {
match self {
SlotFn::MemberFnMut(f) => f(comp, args),
_ => Default::default()
}
}
}
struct Test(u32);
impl Test {
fn reffunc(&self) {
println!("value: {}", self.0);
}
fn reffunc_mut(&mut self, val: u32) {
self.0 = val;
}
}
fn main() {
let mut test = Test(0);
let slot = SlotFn::from_member(Test::reffunc);
let mut mslot = SlotFn::from_member_mut(Test::reffunc_mut);
mslot.emit_mut(&mut test, 10);
slot.emit(&test, ());
}
but not this:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1534d9b49327272ddbbd04826efd644a
enum SlotFn<'a, Comp, Args = (), R = ()>
where Args: 'static,
R: Default + 'static,
{
SimpleFn(Box<dyn Fn(Args) -> R + Send + Sync + 'static>),
MemberFn(Box<dyn Fn(&'a Comp) -> R + Send + Sync + 'static>),
MemberFnMut(Box<dyn FnMut(&'a mut Comp, Args) -> R + Send + Sync + 'static>),
}
impl<'a, Comp, Args, R> SlotFn<'a, Comp, Args, R>
where Args: 'static,
R: Default + 'static,{
fn from_member<F>(f: F) -> Self where F: Fn(&'a Comp) -> R + Send + Sync + 'static{
SlotFn::MemberFn(Box::new(f))
}
fn from_member_mut<F>(f: F) -> Self where F: FnMut(&'a mut Comp, Args) -> R + Send + Sync + 'static{
SlotFn::MemberFnMut(Box::new(f))
}
fn emit(&self, comp: &'a Comp, args: Args) -> R {
match self {
SlotFn::SimpleFn(f) => f(args),
SlotFn::MemberFn(f) => f(comp),
_ => Default::default()
}
}
fn emit_mut(&mut self, comp: &'a mut Comp, args: Args) -> R {
match self {
SlotFn::MemberFnMut(f) => f(comp, args),
_ => Default::default()
}
}
}
struct Test(u32);
impl Test {
fn reffunc(&self) {
println!("value: {}", self.0);
}
fn reffunc_mut(&mut self, val: u32) {
self.0 = val;
}
}
fn main() {
let mut test = Test(0);
let slot = SlotFn::from_member(Test::reffunc);
let mut mslot = SlotFn::from_member_mut(Test::reffunc_mut);
mslot.emit_mut(&mut test, 10);
slot.emit(&test, ());
}
in the second case, I'm getting the following error:
error[E0502]: cannot borrow `test` as immutable because it is also borrowed as mutable
mutable borrow might be used here, when `mslot` is dropped and runs the destructor for type `SlotFn<'_, Test, u32>`

Let's call the 'a of from_member() 'a1, and the 'a of from_member_mut() 'a2. If 'a1 and 'a2 overlap, this is an error test is mutably borrowed for 'a2 while it is borrowed for 'a1. So what are 'a1 and 'a2?
The shortest they can be is from the creation of the SlotFn (for each its own SlotFn) object to its destruction, because for all that time the SlotFn can access the data of 'a.
In the first code, we can destroy the SlotFns early, because they do not implement Drop therefore they cannot perform any action while destroyed, so an early destruction cannot be observed. Therefore, in the first code, the compiler shortens 'a1 and 'a2 just until the emit_mut() and emit(), respectively, so they do not conflict.
In the second case, however, they may implement Drop - dyn Trait is always considered as implementing Drop, as the compiler cannot know what is inside - and therefore, their lifetimes must end at the end of the block, because Drop could observe 'a. So they conflict.

Related

Rust closure generics

Being an aspiring rustacean, I've been working my way through The Rust Programming Language book and being in the 13th chapter I was attempting to generalize the Cacher struct, that has as a purpose implementing lazy evaluation around a closure. While I was able to use generics to generalize the closure signature to any one parameter with any one output type, I can't figure out how to generalize this to closures with any number of params. I feel like there should be a way to do this.
struct Cacher<'a, Args, V: Clone>
{
calculation: &'a dyn Fn(Args) -> V,
value: Option<V>
}
impl<'a, Args, V: Clone> Cacher<'a, Args, V>
{
fn new(calculation: &'a dyn Fn(Args) -> V) -> Cacher<Args, V> {
Cacher {
calculation: calculation,
value: None,
}
}
fn value(&mut self, arg: Args) -> V {
// all this cloning is probably not the best way to do this
match self.value.clone() {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v.clone());
v
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut cached_func = Cacher::new(&(|asd| asd + 1));
assert_eq!(cached_func.value(1), 2);
assert_eq!(cached_func.value(4), 2);
}
#[test]
fn it_works_too() {
// compiler hates this
let mut cached_func = Cacher::new(&(|asd, qwe| asd + qwe));
assert_eq!(cached_func.value(1, 1), 2);
assert_eq!(cached_func.value(4, 1), 2);
}
}
You can do this on nightly using the fn_traits (and closely related unboxed_closures) features. This allows you to use Fn like Fn<Args, Output = V> where Args is a tuple type of all the parameters passed to the function.
#![feature(unboxed_closures)]
#![feature(fn_traits)]
struct Cacher<'a, Args, V: Clone>
{
calculation: &'a dyn Fn<Args, Output = V>,
value: Option<V>
}
impl<'a, Args, V: Clone> Cacher<'a, Args, V>
{
fn new(calculation: &'a dyn Fn<Args, Output = V>) -> Cacher<Args, V> {
Cacher {
calculation: calculation,
value: None,
}
}
fn value(&mut self, args: Args) -> V {
// all this cloning is probably not the best way to do this
match self.value.clone() {
Some(v) => v,
None => {
let v = self.calculation.call(args);
self.value = Some(v.clone());
v
}
}
}
}
This does require you to call value() with a tuple:
let mut cache1 = Cacher::new(&|a| a + 1);
let value1 = cache1.value((7,));
let mut cache2 = Cacher::new(&|a, b| a + b);
let value2 = cache2.value((7, 8));
However, you can make it nicer to use if you're willing to make the boilerplate for the numerous tuple types:
impl<'a, T, V: Clone> Cacher<'a, (T,), V>
{
fn value2(&mut self, arg1: T) -> V {
self.value((arg1, ))
}
}
impl<'a, T, U, V: Clone> Cacher<'a, (T, U), V>
{
fn value2(&mut self, arg1: T, arg2: U) -> V {
self.value((arg1, arg2))
}
}
// ...
let mut cache1 = Cacher::new(&|a: usize| a + 1);
let value1 = cache1.value2(7);
let mut cache2 = Cacher::new(&|a: usize, b: usize| a + b);
let value2 = cache2.value2(7, 8);
See it running on the playground.
This only works on nightly because its not yet been stabilized if this is how they will be supported generically in the future.
In rust, functions do not have a variable numbers of arguments, except in some cases for compatibility with C. This answer provides more background.
In your example, you could achieve some generic lazy evaluation with the lazy static crate. You don’t pass a closure to this crate, not explicitly at least. But you put the body of the closure in a variable that lazy static evaluates on first access (a bit like a closure taking () and whose result would be stored in Cacher, if you will).
It's fairly hard to understand exactly what is it that you need. So here's my guess:
struct Cacher<'a, Args, V: Copy>
{
calculation: &'a dyn Fn(Args) -> V,
value: Option<V>
}
impl<'a, Args, V: Copy> Cacher<'a, Args, V>
{
fn new(calculation: &'a dyn Fn(Args) -> V) -> Cacher<Args, V> {
Cacher {
calculation: calculation,
value: None,
}
}
fn value(&mut self, arg: Args) -> V {
// Cloning fixed
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut cached_func = Cacher::new(&(|asd| asd + 1));
assert_eq!(cached_func.value(1), 2);
assert_eq!(cached_func.value(4), 2);
}
#[test]
fn it_works_too() {
// The compiler is fine
// Although now, it's not multiple arguments but rather one arg, acting as many
let mut cached_func = Cacher::new(&(|asd: (usize, usize)| asd.0 + asd.1));
assert_eq!(cached_func.value((1, 1)), 2);
assert_eq!(cached_func.value((4, 1)), 2);
}
}
Remember that Rust's generics could be considered as Algebraic Data Types, hence, only enums, structs and functions are allowed (closures too, if you consider them different to functions). The second test works because tuples could be considered structs.
Because of this, it's impossible to have multiple arguments in one function definition.
The usual way that rust solves this issue is with macros. Although method macros don't exist in rust yet.

Why does this parameter need to have a 'static lifetime bound?

use std::future::Future;
struct A;
async fn foo<T, R>(t: T)
where
T: Fn(&mut A) -> R + Send + Sync + 'static,
R: Future + Send,
R::Output: Send
{
tokio::spawn(bar(t));
}
async fn bar<T, R>(t: T)
where
T: Fn(&mut A) -> R + Send,
R: Future + Send,
R::Output: Send
{
let mut a = A;
t(&mut a).await;
}
Playground : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e8885b0f86a5b6defcbd2f45d8e35178
My instinct is that R only need to live as long as &mut A. But this is somehow not correct.
My goal is to provide a function that accept following code
async fn handle(&mut A) {
todo!()
}
async fn main() {
foo(handle).await
}

I have a problem with a generic function and enum

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.

Resumable continuation-passing style iterator reduce in Rust

I'm trying to write a continuation-passing-style "reduce" function that can be resumed at any point. I've got a version working, but in this I need to explicitly write a new version of the function if I want it to be able to make use of a borrow of some state.
Rust Playground Link
fn reduce_async_with_store<'a, I, A, F, C>(
store: &mut Store,
mut iterator: I,
accumulator: A,
mut f: F,
continuation: C,
) where
I: Iterator + 'a,
F: FnMut(&mut Store, I::Item, A, Box<dyn FnOnce(&mut Store, A) + 'a>) + Clone + 'a,
C: FnOnce(&mut Store, A) + 'a,
{
match iterator.next() {
None => continuation(store, accumulator),
Some(item) => {
let next: Box<dyn FnOnce(&mut Store, A) + 'a> = {
let f = f.clone();
Box::new(move |store, accumulator| {
reduce_async_with_store(store, iterator, accumulator, f, continuation)
})
};
f(store, item, accumulator, next);
}
}
}
fn some_operation(state: &mut Store, continuation: Box<dyn FnOnce(&mut Store) + 'static>) {
let mut new_state = Store { foo: state.foo };
continuation(&mut new_state);
}
#[derive(Debug)]
pub struct Store {
foo: u8,
}
fn main() {
let mut some_state = Store { foo: 0 };
let arr = vec![1u8, 2u8, 3u8];
reduce_async_with_store(
&mut some_state,
arr.into_iter(),
Vec::new(),
|store, item, mut acc, continuation| {
println!("Item: {}", item);
store.foo += item;
acc.push(item);
some_operation(
store,
Box::new(move |stor| {
continuation(stor, acc);
}),
);
},
|store, acc| {
println!("Done!! {:?} {:?}", store, acc);
},
)
}
Here's the version of this function I'd like to write, where I can pass the Store in as part of the accumulator, and get it out - however, if I do this, I get cannot infer an appropriate lifetime due to conflicting requirements.
Rust Playground Link
fn reduce_async<'a, I, A, F, C>(mut iterator: I, accumulator: A, mut f: F, continuation: C)
where
I: Iterator + 'a,
F: FnMut(I::Item, A, Box<dyn FnOnce(A) + 'a>) + Clone + 'a,
C: FnOnce(A) + 'a,
{
match iterator.next() {
None => continuation(accumulator),
Some(item) => {
let next: Box<dyn FnOnce(A) + 'a> = {
let f = f.clone();
Box::new(move |accumulator| reduce_async(iterator, accumulator, f, continuation))
};
f(item, accumulator, next);
}
}
}
fn some_operation(state: &mut Store, continuation: Box<dyn FnOnce(&mut Store) + 'static>) {
let mut new_state = Store { foo: state.foo };
continuation(&mut new_state);
}
#[derive(Debug)]
pub struct Store {
foo: u8,
}
fn main() {
let mut some_state = Store { foo: 0 };
let arr = vec![1u8, 2u8, 3u8];
reduce_async(
arr.into_iter(),
(&mut some_state, Vec::new()),
|item, mut acc, continuation| {
let (store, vec) = acc;
println!("Item: {}", item);
store.foo += item;
vec.push(item);
some_operation(
store,
Box::new(move |store| {
continuation((store, vec));
}),
);
},
|(store, vec)| {
println!("Done!! {:?} {:?}", store, vec);
},
)
}
How can I write this non-specialized version of my function, and pass things like &mut Store through while respecting Rust's lifetimes?
How is it that my first example with reduce_async_with_store is permitted, even though I don't specify an explicit lifetime for &mut Store, and it could live until 'static?
some_operation takes a boxed closure because that's what the 3rd party API function I'm calling takes. I would like to eventually replace this code with async iterators, but the library I'm using doesn't have support for futures yet.
Let's start with some_operation; it's always easier to examine regular functions than closures because the compiler only checks their signatures.
Putting back the elided lifetimes, it looks like:
fn some_operation<'s>(state: &'s mut Store, continuation: Box<dyn for<'r> FnOnce(&'r mut Store) + 'static>) {
let mut new_state = Store { foo: state.foo };
continuation(&mut new_state);
}
There are two distinct lifetimes involved: 's and 'r — there is no connection between them.
Now let's look here:
Box::new(move |store| {
continuation((store, vec));
}),
The continuation type should be Box<dyn FnOnce(A) + 'a> according to reduce_async's signature. What is the type of A after monomorphising? The argument passed to the function is a tuple:
(&mut some_state, Vec::new()),
The first element has type &'state mut State for some 'state and the second has Vec<u8>. Revisiting some_operation's signature: the first argument is &'s mut State, so we have chosen 'state = 's here. Then we call the closure using an argument with type &'r mut State.
Back in the main procedure, we are trying to build the accumulator from a value of type (&'r mut State, Vec<u8>) which is not the same as (&'state mut State, Vec<u8>).
That's what the compiler is trying to explain :) Let's check this explanation by changing some_operation's signature:
fn some_operation<'s>(state: &'s mut Store, continuation: Box<dyn FnOnce(&'s mut Store) + 's>) {
continuation(state);
}
Here we explicitly mark that both lifetimes should be the same, and now the code compiles without any error.
Note that there were no problems in your first code snippet because the lifetime of store: &mut Store argument is different each time you invoke reduce_async_with_store! In the second snippet it is fixed to 'state.
In my opinion, the easiest fix would be to get rid of mutable references at all and passing Store by transferring ownership.
Rust playground link
fn reduce_async<'a, I, A, F, C>(mut iterator: I, accumulator: A, mut f: F, continuation: C)
where
I: Iterator + 'a,
F: FnMut(I::Item, A, Box<dyn FnOnce(A) + 'a>) + Clone + 'a,
C: FnOnce(A) + 'a,
{
match iterator.next() {
None => continuation(accumulator),
Some(item) => {
let next: Box<dyn FnOnce(A) + 'a> = {
let f = f.clone();
Box::new(move |accumulator| reduce_async(iterator, accumulator, f, continuation))
};
f(item, accumulator, next);
}
}
}
fn some_operation(state: Store, continuation: Box<dyn FnOnce(Store) + 'static>) {
let new_state = Store { foo: state.foo };
continuation(new_state);
}
#[derive(Debug)]
pub struct Store {
foo: u8,
}
fn main() {
let some_state = Store { foo: 0 };
let arr = vec![1u8, 2u8, 3u8];
reduce_async(
arr.into_iter(),
(some_state, Vec::new()),
|item, acc, continuation| {
let (mut store, mut vec) = acc;
println!("Item: {}", item);
store.foo += item;
vec.push(item);
some_operation(
store,
Box::new(move |store| {
continuation((store, vec));
}),
);
},
|(store, vec)| {
println!("Done!! {:?} {:?}", store, vec);
},
)
}
Keep in mind that the continuation invocations are not tail-recursive, so stack will grow on each iteration. You will probably need a trampoline here.

Strange behavior of HRTBs

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).

Resources