my code:
fn add(n:i32, f: impl Fn(i32)) {
if n < 1 {
f(0)
} else {
add(n-1, |d:i32| {
f(n + d);
});
}
}
but when i try to build, i got a error:
reached the recursion limit while instantiating
my code:
fn add(n:i32, f: impl Fn(i32)) {
if n < 1 {
f(0)
} else {
add(n-1, |d:i32| {
f(n + d);
});
}
}
but when i try to build, i got a error:
reached the recursion limit while instantiating
The problem is that every closure has its own unique type. Thus when you call add(…, |d| another_closure)
, you instantiate add()
with a new type (not with the type of f
), which in turn instantiates it with yet another new type, and so on.
In general, if you need value-level recursion, you can't do that using type-level recursion only. You'll need to introduce a function pointer at some point, e.g.:
fn add(n: i32, f: &mut dyn FnMut(i32)) {
if n < 1 {
f(0)
} else {
add(n - 1, &mut |d: i32| {
f(n + d);
});
}
}
sorry, i don't understand.
i know the
|d:i32| {
f(n + d);
}
is a new type fn, but it's also impl Fn(i32). So i don't know why it will be expand forever
Generic expansions happen fully at compile time, but your function recursively uses an infinite list of types, which the compiler is unable to handle.
Here's the type-based recursion spelled out a bit more.
And another sketching how erasing the base type with dyn Fn(i32)
avoids the infinite recursion.
If every closure is a new type, then every generic instantiation will need to happen with that new type, hence the infinite recursion.
I suppose the confusion is because you think that impl Fn(i32)
is a single type. It's not. It means any type which implement Fn(i32)
. For the intents of this question, your signature is
fn add<F>(n:i32, f: F) where F: Fn(i32);
Since all closures (even syntactically identical ones) have different types, it should be clear that your recursively defined closure has a recursive type, which can't terminate based only on compile-time information.
The way to avoid type recursion is to explicitly erase the generic parameter to a single type. This means using dynamic dispatch via trait objects. This means using instead of impl Fn(i32)
some sort of pointer: &dyn Fn(i32)
, Box<dyn Fn(i32)>
, Rc<dyn Fn(i32)>
etc.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.