Lifetimes when wrapping async function in struct - rust

I'm trying to wrap an async function in a struct. For example:
use std::future::Future;
struct X;
struct Y;
async fn f(x: &X) -> Y {
Y
}
struct MyStruct<F, Fut>(F)
where
F: Fn(&X) -> Fut,
Fut: Future<Output = Y>;
fn main() {
MyStruct(f);
}
The compiler complains about this with the following (unhelpful) error:
error[E0308]: mismatched types
--> src/main.rs:16:5
|
16 | MyStruct(f);
| ^^^^^^^^ one type is more general than the other
|
= note: expected associated type `<for<'_> fn(&X) -> impl Future {f} as FnOnce<(&X,)>>::Output`
found associated type `<for<'_> fn(&X) -> impl Future {f} as FnOnce<(&X,)>>::Output`
Is something like this actually possible? As I understand it, f desugars to something like:
fn f<'a>(x: &'a X) -> impl Future<Output = Y> + 'a {
Y
}
so I'd need to somehow express in MyStruct that Fut has the same lifetime as x.

I actually don't know much about async.
However, when it comes to trait-bounds, typically, fewer trait-bound are better. In other words, only declare those trait-bounds that you really need.
In the case of a struct, as long as you don't need an associated type within your struct, you are mostly good without any bounds. This is pretty much to what #George Glavan wrote in his answer.
When you add methods to your struct, you are more likely to use traits and thus require trait-bounds. Sometimes it is useful to combine the trait-bound of multiple functions by declaring it on the impl-block itself. Tho, this has some restrictions. You should also consider whether each function really needs all these constraints.
For instance, consider the following code:
struct X;
struct Y;
struct MyStruct<F>(F);
impl<F> MyStruct<F> {
pub fn new(f: F) -> Self {
MyStruct(f)
}
pub fn invoke<'a, Fut>(&self) -> Fut
where
F: Fn(&'a X) -> Fut,
{
(self.0)(&X)
}
}
I added a new and a invoke function. The former doesn't require any traits, thus it doesn't have trait-bounds. The latter only calls the function, so it bounds F by Fn. And this is good enough, because in the end, the caller must already know what the return type is, i.e. whether it is some Future or not.
However, there are a few cases where one really needs additional trait-bounds, which involves additional generics such as for a function return type. In such a case, you can declare additional (phantom) generics on a struct, e.g.:
use std::future::Future;
use std::marker::PhantomData;
struct X;
struct Y;
struct MyStruct<F, Fut> {
func: F,
_fut: PhantomData<Fut>,
}
impl<'a, F, Fut> MyStruct<F, Fut>
where
F: Fn(&'a X) -> Fut,
Fut: Future<Output = Y> + Send + Sync + 'a,
{
pub fn new(f: F) -> Self {
MyStruct {
func: f,
_fut: PhantomData,
}
}
pub fn invoke(&self) {
(self.func)(&X);
}
}
Notice, that in this example the trait-bounds apply to both function new and invoke, and both are over-restricted. Still, you don't need to over-restrict the struct itself.

Related

Covariance of Box<dyn FnOnce(T)> in rust

I have a function that expects a short lived object. I would expect that I would be able to always pass it a long lived object. But I am getting a strange error when I try to encode that:
type F<'arg> = Box<dyn FnOnce(&'arg ())>;
fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
f
}
playground
Particularly:
error: lifetime may not live long enough
--> src/lib.rs:3:5
|
2 | fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
| ------ ------ lifetime `'large` defined here
| |
| lifetime `'small` defined here
3 | f
| ^ function was supposed to return data with lifetime `'large` but it is returning data with lifetime `'small`
|
= help: consider adding the following bound: `'small: 'large`
It seems like F is invariant for its argument but I would have guessed that it's contravariant. Am I missing something? Is there a way to make F<'arg> really contravariant for 'arg?
Edit: it looks like the "problem" is that rust wants to treat all generic traits the same (including Fn/FnMut/FnOnce). My opinion is that those 3 are and should be treated special especially given that they are the only way to refer to closures. For that reason I opened an issue
The Rust Reference's page on Subtyping and Variance documents that, as of Rust 1.63.0, fn(T) -> () is contravariant over T and that dyn Trait<T> + 'a is invariant over T.
FnOnce, FnMut and Fn are traits, so that means dyn FnOnce(&'a ()) is unfortunately invariant over &'a ().
// Compiles
pub fn contravariant<'a, 'b: 'a>(x: fn(&'a ())) -> fn(&'b ()) { x }
// Doesn't compile
pub fn contravariant2<'a, 'b: 'a>(x: Box<dyn FnOnce(&'a ())>) -> Box<dyn FnOnce(&'b ())> { x }
Is there a way to wrap FnOnce somehow to convince the compiler of the correct variance?
Here's what I could come up with using unsafe code. Note that I'm not making any guarantees as to whether this is sound or not. I don't know of any way to do this without unsafe code.
use std::marker::PhantomData;
trait Erased {}
impl<T> Erased for T {}
pub struct VariantBoxedFnOnce<Arg, Output> {
boxed_real_fn: Box<dyn Erased + 'static>,
_phantom_fn: PhantomData<fn(Arg) -> Output>,
}
impl<Arg, Output> VariantBoxedFnOnce<Arg, Output> {
pub fn new(real_fn: Box<dyn FnOnce(Arg) -> Output>) -> Self {
let boxed_real_fn: Box<dyn Erased + '_> = Box::new(real_fn);
let boxed_real_fn: Box<dyn Erased + 'static> = unsafe {
// Step through *const T because *mut T is invariant over T
Box::from_raw(Box::into_raw(boxed_real_fn) as *const (dyn Erased + '_) as *mut (dyn Erased + 'static))
};
Self {
boxed_real_fn,
_phantom_fn: PhantomData,
}
}
pub fn call_once(self, arg: Arg) -> Output {
let boxed_real_fn: Box<Box<dyn FnOnce(Arg) -> Output>> = unsafe {
// Based on Box<dyn Any>::downcast()
Box::from_raw(Box::into_raw(self.boxed_real_fn) as *mut Box<dyn FnOnce(Arg) -> Output>)
};
boxed_real_fn(arg)
}
}
pub fn contravariant<'a, 'b: 'a>(x: VariantBoxedFnOnce<&'a (), ()>) -> VariantBoxedFnOnce<&'b (), ()> { x }
#[cfg(test)]
mod tests {
use super::*;
fn foo(_x: &()) {}
#[test]
pub fn check_fn_does_not_require_static() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let x = ();
f.call_once(&x);
}
#[test]
pub fn check_fn_arg_is_contravariant() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let g = contravariant(f);
let x = ();
g.call_once(&x);
}
}
Here, VariantBoxedFnOnce is limited to functions taking one argument.
The trick is to store a type-erased version of the Box<dyn FnOnce(Arg) -> Output> such that Arg disappears, because we don't want the variance of VariantBoxedFnOnce<Arg, Output> to depend on Box<dyn FnOnce(Arg) -> Output> (which is invariant over Arg). However, there's also a PhantomData<fn(Arg) -> Output> field to provide the proper contravariance over Arg (and covariance over Output).
We can't use Any as our erased type, because only 'static types implement Any, and we have a step in VariantBoxedFnOnce::new() where we have a Box<dyn Erased + '_> where '_ is not guaranteed to be 'static. We then immediately "transmute" it into 'static, to avoid having a redundant lifetime parameter on VariantBoxedFnOnce, but that 'static is a lie (hence the unsafe code). call_once "downcasts" the erased type to the "original" Box<dyn FnOnce(Arg) -> Output>, except that Arg and Output may be different from the original due to variance.

Can't solve lifetime problem in simple method

I'm brand new at Rust, coming from Java and I experience some difficulties with owenership and lifetimes. I'd like to do some formal calculus but obviously I'm not doing things the right way... Can you please show me why?
In my code I define those:
pub trait Function {
fn differentiate(&self) -> Box<dyn Function>;
}
pub struct Add<'a>(&'a Box<dyn Function>, &'a Box<dyn Function>);
impl<'a> Function for Add<'a> {
fn differentiate(&self) -> Box<dyn Function> {
let x = self.0.differentiate();
let y = self.1.differentiate();
let add = Add(&x, &y);
Box::new(add)
}
Compiler tells me I have a borrowing problem with x and y, I understand why but can't figure out how to solve it; I tried to set let x: Box<dyn Function + 'a> = ... but then I got lifetime problems on defining add and on the last line:
expected `Box<(dyn Function + 'static)>`
found `Box<dyn Function>`
You cannot return an object that references local variables.
This is nothing special to Rust, it is like that in every language that has references (Java doesn't, in Java everything is a reference counting smart pointer). Writing this in C/C++ would be undefined behaviour. The borrow checker is here to prevent undefined behaviour, so it rightfully complains.
Here is a wild guess of what you might have wanted to do.
I'm unsure why you use references here, so I removed them. Your code looks like Add should own its members.
pub trait Function {
fn differentiate(&self) -> Box<dyn Function>;
}
pub struct Add(Box<dyn Function>, Box<dyn Function>);
impl Function for Add {
fn differentiate(&self) -> Box<dyn Function> {
let x = self.0.differentiate();
let y = self.1.differentiate();
let add = Add(x, y);
Box::new(add)
}
}
An alternative design would be to require differentiable functions to be clonable (because you'll probably want to be able to use them in different places), and avoid dynamic dispatch (and the indirection required by trait objects) altogether. Here is the implementation of two simple operations as an example.
trait Differentiable: Clone {
type Output;
fn differentiate(&self) -> Self::Output;
}
#[derive(Clone)]
struct Add<L, R>(L, R);
impl<L: Differentiable, R: Differentiable> Differentiable for Add<L, R> {
type Output = Add<L::Output, R::Output>;
fn differentiate(&self) -> Self::Output {
Add(self.0.differentiate(), self.1.differentiate())
}
}
#[derive(Clone)]
struct Mul<L, R>(L, R);
impl<L: Differentiable, R: Differentiable> Differentiable for Mul<L, R> {
type Output = Add<Mul<L::Output, R>, Mul<L, R::Output>>;
fn differentiate(&self) -> Self::Output {
Add(Mul(self.0.differentiate(), self.1.clone()), Mul(self.0.clone(), self.1.differentiate()))
}
}
Note that this easily allows adding useful constraints, such as making them callable (if you actually want to be able to evaluate them) or stuff like that. These, alongside with the identify function and the constant function should probably be enough for you to "create" polynomial calculus.
The simplest is to remove the references, because your Box is a smart pointer. So:
pub trait Function {
fn differentiate(&self) -> Box<dyn Function>;
}
pub struct Add(Box<dyn Function>, Box<dyn Function>);
impl Function for Add {
fn differentiate(&self) -> Box<dyn Function> {
let x = self.0.differentiate();
let y = self.1.differentiate();
Box::new(Add(x, y))
}
}
I think what you are trying to return a type after adding two types that implement differentiation.
There are a few different ways to think about this... Here's one:
pub trait Differentiable {
type Result;
fn differentiate(self) -> Self::Result;
}
pub struct Add<OP1, OP2>(OP1, OP2);
impl<OP1, OP2> Differentiable for Add<OP1, OP2>
where
OP1: Differentiable,
OP2: Differentiable,
{
type Result = Add<OP1::Result, OP2::Result>;
fn differentiate(self) -> Self::Result {
let x = self.0.differentiate();
let y = self.1.differentiate();
Add(x, y)
}
}

Type matching instead of `impl Trait` in trait definition

These days, I am playing with Rust's async ecosystem, and Streams in particular, and I want to implement an equivalent of map() method (from StreamExt) with some specificities that are not interesting here. And because I am not allowed to write impl Stream as the return type of the extension trait I want to create, I feel quite limited in the choice of APIs I can define.
My code looks like that (I simplified the fn_to_future function):
use futures::{future::Ready, stream::Then, Stream, StreamExt};
pub fn fn_to_future<F, I, T>(f: F) -> impl Fn(I) -> Ready<T>
where
F: Fn(I) -> T,
{
// this is a reduced example
move |input| {
let out = f(input);
futures::future::ready(out)
}
}
// This is fine: this is a function, so we can use `impl`
// Also, it hides the actual type of the returned stream and
// allows for a change in the implementation without breaking the API.
pub fn my_map<S, F, T>(stream: S, f: F) -> impl Stream<Item = T>
where
S: Stream,
F: Fn(S::Item) -> T,
{
stream.then(fn_to_future(f))
}
// This is the API I would like to offer to users, as it is
// easier to compose with existing functions
pub trait MyMapStreamExt: Stream + Sized {
fn my_map_in_trait<F, T, G>(self, f: F) -> Then<Self, Ready<T>, G>
// Cannot use 'impl Stream' here as we are in a trait
// So, we try to be explicit about the return type
where
F: Fn(Self::Item) -> T,
G: Fn(Self::Item) -> Ready<T>,
{
self.then(fn_to_future(f)) // the compiler complains here
// as the type parameter 'G' cannot match the opaque type
// returned by 'fn_to_future'
}
}
I know that there are a few (possible) solutions to my particular problem :
Define a MyMap type that implements Stream, just as the Then type implements Stream.
Wrap the output of fn_to_future in a generic type parametrised by F and which implements Fn(Self::Item) -> Ready<T>.
Try using a Box as the output of the trait function (i.e. circumvent the impl Trait limitations).
Solution 1 will work for sure, but I feel like this is writing a lot of unnecessary code (cf. the amount of code used for the my_map function), and would mostly duplicate the Then implementation.
Solution 2 should work, but would require using unstable, which I would like to avoid, as there is no clear date of when the Fn traits will become stable (cf. the tracking issue).
I haven't really looked into Solution 3, but I find it very inelegant (and might cause many problems in my case).
So, my question is two-folds:
why does the type matching fail here (I don't quite understand if this is a weakness of the compiler or a soundness issue), and can we fix it?
is there any other elegant and efficient solution (as explained, I have either inelegant or inefficient solutions at the moment)?
Thanks!
The problem is by declaring the function like this:
fn my_map_in_trait<F, T, G>(self, f: F) -> Then<Self, Ready<T>, G>
Means that G is defined by the caller. This is definitely not the intention. Since you don't want to define a new Stream type, you need a named type instead of G. Here are some options for you:
1. Box the Fn
Implementation looks like this:
fn my_map_in_trait<'a, F, T>(self, f: F) -> Then<Self, Ready<T>, Box<dyn Fn(Self::Item) -> Ready<T> + 'a>>
where
F: Fn(Self::Item) -> T,
F: 'a,
T: 'a,
Self::Item: 'a,
{
self.then(Box::new(fn_to_future(f)))
}
This should be better than Boxing the Stream implementation because less functions will need to be generated in the vtable.
2. existential types / TAIT (type alias impl trait)
An nightly only solution but doesn't have any overhead: playground
#![feature(type_alias_impl_trait)]
use futures::{future::Ready, stream::Then, Stream, StreamExt};
pub fn fn_to_future<S: Stream, F, T>(f: F) -> Fun<S, F, T>
where
F: Fn(S::Item) -> T,
{
move |input| {
let out = f(input);
futures::future::ready(out)
}
}
type Fun<S: Stream, F, Output> = impl Fn(S::Item) -> Ready<Output>;
pub trait MyMapStreamExt: Stream + Sized {
fn my_map_in_trait<'a, F, T>(self, f: F) -> Then<Self, Ready<T>, Fun<Self, F, T>>
where
F: Fn(Self::Item) -> T,
{
self.then(fn_to_future:<Self, _, _>(f))
}
}
3. Unboxed closures
Another nightly only solution. This solution does not use impl Trait so no opaque types: playground
#![feature(fn_traits)]
#![feature(unboxed_closures)]
use futures::{future::Ready, stream::Then, Stream, StreamExt};
pub struct ToFuture<F>(F);
impl<F, I, O> FnOnce<(I,)> for ToFuture<F>
where
F: FnOnce(I) -> O,
{
type Output = Ready<O>;
extern "rust-call" fn call_once(self, (i,): (I,)) -> Ready<O> {
futures::future::ready((self.0)(i))
}
}
impl<F, I, O> FnMut<(I,)> for ToFuture<F>
where
F: FnMut(I) -> O,
{
extern "rust-call" fn call_mut(&mut self, (i,): (I,)) -> Ready<O> {
futures::future::ready((self.0)(i))
}
}
pub fn fn_to_future<F>(f: F) -> ToFuture<F> {
ToFuture(f)
}
pub trait MyMapStreamExt: Stream + Sized {
fn my_map_in_trait<'a, F, T>(self, f: F) -> Then<Self, Ready<T>, ToFuture<F>>
where
F: Fn(Self::Item) -> T,
{
self.then(fn_to_future(f))
}
}

Using higher-ranked trait bounds with generics

I've stumbled upon an interesting edge case: using higher-ranked lifetime bounds to accept closures that return generic parameters, such as for<'a> FnOnce(&'a T) -> R: MyTrait. There's no way to specify that R lives for at most 'a. Perhaps it's best to explain with an example.
Let's define a simple reference-like type wrapping a value:
struct Source;
struct Ref<'a> {
source: &'a Source,
value: i32,
}
For convenience, let's add a helper constructor. Here I will use explicit lifetimes to make the borrowing self-explanatory:
impl Source {
fn new_ref<'a>(&'a self, value: i32) -> Ref<'a> {
Ref { source: self, value }
}
}
This is an extremely fancy implementation of a integer copying routine using HRTBs with a closure over our Ref:
fn call_1<F>(callback: F) -> i32
where
for<'a> F: FnOnce(&'a Source) -> Ref<'a>,
{
let source = Source;
callback(&source).value
}
fn fancy_copy_1(value: i32) -> i32 {
call_1(|s| s.new_ref(value))
}
This is fine and is working as expected. We know that Ref does not outlive the Source and the compiler is also able to pick it up. Now let's create a simple trait and implement it for our reference:
trait MyTrait {
fn value(&self) -> i32;
}
impl<'a> MyTrait for Ref<'a> {
fn value(&self) -> i32 {
self.value
}
}
And modify our integer copying routine to return a generic type implementing that trait instead of just returning Ref:
fn call_2<R, F>(callback: F) -> i32
where
for<'a> F: FnOnce(&'a Source) -> R,
R: MyTrait,
{
let source = Source;
callback(&source).value()
}
fn fancy_copy_2(value: i32) -> i32 {
call_2(|s| s.new_ref(value))
}
Suddenly I get an error: cannot infer an appropriate lifetime for autoref due to conflicting requirements. Rust playground link for convenience. That actually makes sense from some perspective: unlike with Ref<'a> in the first example I never said that R has to live for at most 'a. could very well live longer and thus have access to freed memory. So I need to annotate it with it's own lifetime. But there's no place to do it! The first instinct is to put the lifetime in the bounds:
where
for<'a> F: FnOnce(&'a Source) -> R,
R: MyTrait + 'a,
which is of course incorrect, as 'a is only defined for the first bound.
This is where I got confused, started searching and never found anything about combining HRTBs and generic types in one bound. Perhaps more experienced people in Rust have any suggestions?
Upd 1.
While I was thinking about the problem some more, I remembered I could use the impl Trait syntax. This looks like a solution to my problem:
fn call_3<F>(callback: F) -> i32
where
F: for<'a> FnOnce(&'a Source) -> (impl MyTrait + 'a),
{
let source = Source;
callback(&source).value()
}
fn fancy_copy_3(value: i32) -> i32 {
call_3(|s| Box::new(s.new_ref(value)))
}
That, however does not work because impl MyTrait is not allowed in this place for some reason (probably temporarily). But that made me think of dyn Trait syntax and that indeed does work!
fn call_4<F>(callback: F) -> i32
where
F: for<'a> FnOnce(&'a Source) -> Box<dyn MyTrait + 'a>,
{
let source = Source;
let value = callback(&source); // note how a temporary is required
value.value()
}
fn fancy_copy_4(value: i32) -> i32 {
call_4(|s| Box::new(s.new_ref(value)))
}
Here's my solution: with dyn Trait syntax there is a place to put the + 'a! Unfortunately this solution still doesn't quite work for me too well, as it requires object-safety on the trait plus adds an overhead of allocating a boxed value. But at least it's something.

Rust function which takes function with arg a function

I want to write a generic function count_calls which calls a function f which takes a function pointer (lambda) where count_calls counts how often function f called the given lambda function.
I struggle with the approach (Playground).
fn count_calls<S, F>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(Fn() -> S) -> (),
{
let mut counter: u32 = 0;
f(|| {
counter += 1;
s.clone()
});
counter
}
#[cfg(test)]
mod stackoverflow {
use super::*;
fn f(p: fn() -> i32) {
p();
p();
}
#[test]
fn test() {
let counts = count_calls(3, f);
assert_eq!(counts, 2);
}
}
Here I get the error:
error[E0277]: the size for values of type `(dyn std::ops::Fn() -> S + 'static)` cannot be known at compilation time
--> src/lib.rs:1:1
|
1 | / fn count_calls<S, F>(s: S, f: F) -> u32
2 | | where
3 | | S: Clone,
4 | | F: Sized + FnMut(Fn() -> S) -> (),
... |
12 | | counter
13 | | }
| |_^ doesn't have a size known at compile-time
|
= help: within `((dyn std::ops::Fn() -> S + 'static),)`, the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() -> S + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because it appears within the type `((dyn std::ops::Fn() -> S + 'static),)`
= note: required by `std::ops::FnMut`
Does someone know how to fix this?
[Edit]
I think using Box<Fn()->S> might be a solution. But I would prefer a stack only solution, if possible.
The error "the size for values of type (dyn std::ops::Fn() -> S + 'static) cannot be known at compilation time" is caused by your trait bound for F:
F: Sized + FnMut(Fn() -> S) -> ()
This is equivalent to F: Sized + FnMut(dyn Fn() -> S). This means that the closure F would take a trait object (dyn Fn() -> S) by value. But trait objects are unsized and cannot be passed by value (yet).
One solution would be to pass the trait object by reference or in a Box. The answer by rodrigo explains and discusses these solutions.
Can we avoid trait objects and dynamic dispatch?
Not properly, I think.
Non solutions
One idea would be to add another type parameter to count_calls:
fn count_calls<S, F, G>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(G),
G: Fn() -> S,
However, this doesn't work:
error[E0308]: mismatched types
--> src/lib.rs:9:7
|
9 | f(|| {
| _______^
10 | | counter += 1;
11 | | s.clone()
12 | | });
| |_____^ expected type parameter, found closure
|
= note: expected type `G`
found type `[closure#src/lib.rs:9:7: 12:6 counter:_, s:_]`
The problem here is that type arguments of count_calls are chosen by the caller of count_calls. But we actually want G to always be the type of our own closure. So that doesn't work.
What we want is a generic closure (where we can choose it's type parameters). Something similar is possible, but restricted to lifetime parameters. It's called HRTBs and looks like F: for<'a> Fn(&'a u32). But it doesn't help here because we need a type parameter and for<T> doesn't exist (yet?).
Sub-optimal, nightly solution
One solution would be to not use a closure, but a type with a known name which implements FnMut. Sadly, you can't implement the Fn* traits for your own type on stable yet. On nightly, this works.
struct CallCounter<S> {
counter: u32,
s: S,
}
impl<S: Clone> FnOnce<()> for CallCounter<S> {
type Output = S;
extern "rust-call" fn call_once(self, _: ()) -> Self::Output {
// No point in incrementing the counter here
self.s
}
}
impl<S: Clone> FnMut<()> for CallCounter<S> {
extern "rust-call" fn call_mut(&mut self, _: ()) -> Self::Output {
self.counter += 1;
self.s.clone()
}
}
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: Sized + FnMut(&mut CallCounter<S>), // <----
{
let mut counter = CallCounter {
counter: 0,
s,
};
f(&mut counter); // <-------
counter.counter
}
Unfortunately, now you have this strange type in your public interface (which should be implementation detail).
Apart from that, I can't think of any real solution (only other super verbose solutions with plenty of disadvantages). The developments in the type system corner (in particular in regards to GATs and HKTs) could solve this properly in the future. However, I think there are still a few different features lacking; in particular, I don't think that GATs as proposed would already solve this.
So if this is a real life problem which needs to be solved right now, I would:
step back and rethink the problem in a bigger scope to maybe avoid this Rust limitation, or
just use dynamic dispatch.
This is the simplest code that I managed to get working (playground):
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: FnMut(&mut dyn FnMut() -> S) -> (),
{
let mut counter: u32 = 0;
f(&mut || {
counter += 1;
s.clone()
});
counter
}
#[cfg(test)]
mod stackoverflow {
use super::*;
fn f(p: &mut dyn FnMut() -> i32) {
p();
p();
}
#[test]
fn test() {
let counts = count_calls(3, f);
assert_eq!(counts, 2);
}
}
The key change is that the function argument for F is changed from Fn() -> S into &mut dyn FnMut() -> S. You need a reference because you are using dynamic dispatching. Also you need FnMut because you are capturing counter and changing it inside, and a Fn will not allow it.
Note that you cannot use Box<FnMut() -> S. It will not allow capturing a reference to counter because boxed functions must be 'static.
If you find that changing your Fn to FnMut is undesirable (because you are changing your public API) you could go back to F: FnMut(&mut dyn Fn() -> S) -> () by defining counter as a Cell<u32>:
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: FnMut(&dyn Fn() -> S) -> (),
{
let counter: Cell<u32> = Cell::new(0);
f(&|| {
counter.set(counter.get() + 1);
s.clone()
});
counter.into_inner()
}

Resources