Second mutable borrow occurs here error in rust - rust

I want to make a function that compose given function twice, i.e., f(f(x)) in Rust. Here is my code:
pub fn compose<T, F: FnMut(T) -> T>(mut f: F) -> impl FnMut(T) -> T {
move |x| f(f(x))
}
But I got this error message:
error[E0499]: cannot borrow `f` as mutable more than once at a time
--> src/lib.rs:2:16
|
2 | move |x| f(f(x))
| - ^ second mutable borrow occurs here
| |
| first mutable borrow occurs here
| first borrow later used by call
For more information about this error, try `rustc --explain E0499`.
How do I fix this problem?

You could extract the intermediate step into a variable that no longer requires the borrow of f. Something like
pub fn compose<T, F: FnMut(T) -> T>(mut f: F) -> impl FnMut(T) -> T {
move |x| {
let tmp = f(x);
f(tmp)
}
}

Related

Why can I not call FnMut twice in a line?

Taking example snippets from here: the following doesn't compile
fn foobar<F>(mut f: F)
where F: FnMut(i32) -> i32
{
println!("{}", f(f(2)));
// error: cannot borrow `f` as mutable more than once at a time
}
fn main() {
foobar(|x| x * 2);
}
but this does
fn foobar<F>(mut f: F)
where F: FnMut(i32) -> i32
{
let tmp = f(2);
println!("{}", f(tmp));
}
fn main() {
foobar(|x| x * 2);
}
I don't understand why the first snippet is illegal: it's effectively the same as the second one, just written more concisely. More specifically, why must f(f(2)) mutably borrow f twice? It can simply borrow the inner f to compute the value of f(2), and then borrow the outer f and apply it to the value.
More specifically, why must f(f(2)) mutably borrow f twice?
The borrows here happen in the order of expression evaluation, and expression evaluation is always left-to-right, even when the expressions in question are trivial variable accesses. The expressions to be evaluated in this code are:
f(f(2)) is made up of two subexpressions: f and f(2).
Evaluate the function value, f (and borrow it as &mut because we're calling a FnMut).
Evaluate the argument, f(2).
Evaluate the function value, f; error because it's already borrowed.
Evaluate the argument, 2.
Call the borrow of f with the argument, 2. This is the result of f(2).
Call the borrow of f with the argument, the result of evaluating f(2). This is the result of f(f(2))
The borrow checker could soundly accept this case, but it would require the idea of recognizing that the first borrow hasn't been used yet, which isn't currently a thing in the borrow checker.

How lifetime parameters and borrowing interacts in function signatures

Lets say I have a function with the following signature in Rust:
fn f<'a>(x: &'a i32) -> &'a i32;
Lets say I do the following then:
let x = 0;
let y = f(&x);
In that case, the Rust borrow checker considers y to borrow x. Why? What is the reason on a deeper level than "because you used the same lifetime parameter in the parameter type and the return type".
The function signature
fn f<'a>(x: &'a i32) -> &'a i32;
means that the value returned by f is a reference to what x parameter refers to, hence it can't outlive it. For example, this won't work:
// Compile error
let y = {
let x = 0;
f(&x)
// x is dropped here
};
// Here y still "exists", but x doesn't (y outlives x)
To your specific question:
Lets say I do the following then:
let x = 0;
let y = f(&x);
In that case, the Rust borrow checker considers y to borrow x. Why?
The answer is because the function signature of f tells it so. To give you an example, suppose that we change the signature to this:
fn f<'a, 'b>(x: &'a i32, z: &'b i32) -> &'a i32;
Then we call f like this:
let x = 0;
let z = 1;
let y = f(&x, &z);
In the code above, y borrows x, but not z. It's because the return value of f has 'a lifetime that is the same as x's lifetime.
Using the syntax from the Rustonomicon, we can elaborate the second snippet
let x: i32 = 0;
'a: { // definition of a lifetime (not real syntax)
let y: &'a i32 = f::<'a>(&'a x) // &'a x is also not real, though interestingly rustc recognizes it
}
The lifetime 'a is introduced by the compiler because you wrote a & and it needs to know how long that borrow will last (note that you cannot manually specify lifetimes for borrows). The type of the function also means that y has 'a in its type. The compiler needs to figure out where 'a starts and ends. The rules are that 'a must start right before you perform the borrow, that x cannot be moved during 'a (since it's borrowed), and that y cannot be used after 'a (because its type says so). If the compiler can pick start and end points for 'a so that the rules hold, then it compiles. If it can't, there's an error. There is no direct relationship between x and y; it goes through the lifetime 'a that they both interact with. E.g. you can't move x and then read *y because 'a must end before you move x and after you read *y, but such a time does not exist. TL;DR think about borrow checking as lifetime inference.

Why does this Rust code compile with a lifetime bound on the struct, but give a lifetime error if the bound is only on the impl?

Recently, I tried to write a piece of code similar to the following:
pub struct Foo<'a, F> /* where F: Fn(&u32) -> bool */ {
u: &'a u32,
f: F
}
impl<'a, F> Foo<'a, F>
where F: Fn(&u32) -> bool
{
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
where G: Fn(&u32) -> bool
{
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
}
}
Here, an instance of Foo represents a condition on a piece of data (the u32), where a more restrictive Foo can be built from a less restrictive one via new_foo, without consuming the old. However, the above code does not compile as written, but gives the rather cryptic error message:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
error: higher-ranked subtype error
--> src/lib.rs:9:5
|
9 | / pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
10 | | where G: Fn(&u32) -> bool
11 | | {
12 | | Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
13 | | }
| |_____^
error: aborting due to 2 previous errors
After much experimentation, I did find a way to make the code compile, and I believe it then functions as intended. I am used to the convention of placing bounds on impls rather than declarations when the declaration can be written without relying on those bounds, but for some reason uncommenting the where clause above, that is, copying the bound F: Fn(&u32) -> bool from the impl to the declaration of Foo itself resolved the problem. However, I don't have a clue why this makes a difference (nor do I really understand the error message in the first place). Does anyone have an explanation of what's going on here?
The only subtypes that exist in Rust are lifetimes, so your errors (cryptically) hint that there's some sort of lifetime problem at play. Furthermore, the error clearly points at the signature of your closure, which involves two lifetimes:
the lifetime of the closure itself, which you have explicitly stated outlives the anonymous lifetime '_; and
the lifetime of its argument &u32, which you have not explicitly stated, so a higher-ranked lifetime is inferred as if you had stated the following:
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
where G: Fn(&u32) -> bool
Using the more explicit signature above gives a (very) slightly more helpful error:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&'b u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
At least we can now see that "one type is more general than the other": we expected a closure that can take an argument with any lifetime but for some reason Rust thinks that what we have instead is a closure that takes an argument that may have some more restricted range of lifetimes.
What's happened? Well, the function's return value is the following expression:
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
This is of course an instance of struct Foo<'a, F>, where this F bears no relation to that declared on the impl block (with its trait bound). Indeed, since there's no explicit bound on F in the struct definition, the compiler must fully infer this type F from the expression itself. By giving the struct definition a trait bound, you are telling the compiler that instances of Foo, including the above expression, have an F that implements for<'b> Fn(&'b u32) -> bool: i.e. the range of lifetimes for the &u32 argument are unbounded.
Okay, so the compiler needs to infer F instead, and indeed it does infer that it implements Fn(&u32) -> bool. However, it's just not quite smart enough to determine to what range of lifetimes that &u32 argument might be restricted. Adding an explicit type annotation, as suggested in #rodrigo's comment above, states that the argument can indeed have any lifetime.
If there are in fact some restrictions on the possible lifetimes of the closure's argument, you would need to indicate that more explicitly by changing the definition of 'b from a higher-ranked trait bound (i.e. for<'b> in the return type above) to whatever is appropriate for your situation.
Hopefully once chalk is fully integrated into the compiler it will be able to perform this inference in both the unrestricted and restricted cases. In the meantime, the compiler is erring on the side of caution and not making potentially erroneous assumptions. The errors could definitely have been a bit more helpful, though!

How do I create a vector of functions with generic type parameters?

I have a few functions with the same generic type signature:
pub fn selection_sort<T: Ord + Debug>(v: &mut [T]) {}
pub fn insertion_sort<T: Ord + Debug>(v: &mut [T]) {}
I want to put them into a Vec or array so that I can iterate over them and run the same tests on them, as follows:
let mut sorters: Vec<fn<T: Ord + Debug>(v: &mut [T])> = Vec::new();
However I get this compiler error:
error: expected `(`, found `<`
--> src/main.rs:8:28
|
8 | let mut sorters: Vec<fn<T: Ord + Debug>(v: &mut [T])> = Vec::new();
| ----------- ^
| |
| while parsing the type for `mut sorters`
How can I put these functions into a vector? Is there a better approach to re-use tests against functions that satisfy this signature?
I'm using Rust 1.24.0.
You cannot have a function pointer with a generic type. You will need to pick a specific type for T:
use std::fmt::Debug;
pub fn selection_sort<T: Ord + Debug>(v: &mut [T]) {}
pub fn insertion_sort<T: Ord + Debug>(v: &mut [T]) {}
fn main() {
let sorters_u8: &[fn(&mut [u8])] = &[selection_sort, insertion_sort];
let sorters_bool: &[fn(&mut [bool])] = &[selection_sort, insertion_sort];
}
The intuition here is that a function pointer has to point to something. Until a specific type has been provided to the function, there isn't any actual code generated — the compiler cannot create an infinite number of functions just in case one of them is chosen later.
See also:
Vector of Generic Structs in Rust
Function pointers in Rust using constrained generics

How to apply trait inside a closure which is used as a function result

I can't implement this LISP construction
(defun foo (n)
(lambda (i) (incf n i)))
in Rust.
I have tried this:
use std::ops::Add;
fn f<T: Add>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
fn main() {
let adder = f(2);
assert_eq!(4, adder(2));
}
But it causes an error:
error: mismatched types:
expected `T`,
found `<T as core::ops::Add>::Output`
(expected type parameter,
found associated type) [E0308]
Box::new(move |i: T| n + i)
^~~~~
Seems like the trait Add defined for the outer function was not transferred into the inner closure.
Is it possible to implement such a construction?
It is possible to implement this function with a concrete type instead of a generic:
fn f(n: i32) -> Box<Fn(i32) -> i32> {
Box::new(move |i| n + i)
}
There are several problems with the generic version.
First, the error you have provided occurs because just T: Add is not enough to specify output type: you need to put a constraint onto the associated type <T as Add>::Output as well (see Add docs):
fn f<T: Add<Output=T>>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
Alternatively, you can make the closure return the output type of <T as Add>:
fn f<T: Add>(n: T) -> Box<Fn(T) -> T::Output> {
Box::new(move |i: T| n + i)
}
However, now you'll get the following error:
<anon>:4:10: 4:37 error: the parameter type `T` may not live long enough [E0310]
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:4:10: 4:37 help: see the detailed explanation for E0310
<anon>:4:10: 4:37 help: consider adding an explicit lifetime bound `T: 'static`...
<anon>:4:10: 4:37 note: ...so that the type `[closure#<anon>:4:19: 4:36 n:T]` will meet its required lifetime bounds
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~
The problem here is that T may contain references inside it (afterwards, it is a generic type - it can contain anything); however, Box<Fn(T) -> T> implicitly means that anything inside this trait object must be 'static, i.e. the compiler automatically adds 'static constraint: Box<Fn(T) -> T + 'static>. However, your closure captures T which can contain any references, not only 'static.
The most general way to fix it would be to add an explicit lifetime constraint of T and Box<Fn(T) -> T>:
fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
Alternatively, you can specify that T is 'static, though this unnecessarily limits the genericity of your code:
fn f<T: Add<Output=T> + 'static>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}
However, this still does not compile:
<anon>:4:31: 4:32 error: cannot move out of captured outer variable in an `Fn` closure
<anon>:4 Box::new(move |i: T| n + i)
^
This error happens because addition in Rust (i.e. Add trait) works by value - it consumes both arguments. For Copy types, like numbers, it is fine - they are always copied. However, the compiler can't assume that generic type parameters also designate Copy types because there is no respective bound, therefore it assumes that values of type T can only be moved around. However, you're specifying that the returned closure is Fn, so it takes its environment by reference. You can't move out of a reference, and this is what this error is about.
There are several ways to fix this error, the simplest one being to add Copy bound:
fn f<'a, T: Add<Output=T> + Copy + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
And now it compiles.
One possible alternative would be to return FnOnce closure which takes its environment by value:
fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<FnOnce(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}
There are two problems with it, however. First, as its name implies, FnOnce can only be called once, because upon its first invocation its environment is consumed, and there is nothing to call it on the next time. This may be overly limiting. Second, unfortunately, Rust cannot call Box<FnOnce()> closures at all. This is an implementation problem and it should be solved in the future; for now there is an unstable FnBox trait to work around this.
Even another alternative would be to use references instead of values:
fn f<'a, T: 'a>(n: T) -> Box<Fn(T) -> T + 'a> where for<'b> &'b T: Add<T, Output=T> {
Box::new(move |i: T| &n + i)
}
Now we specify that instead of T, &'b T for any lifetime 'b must be summable with T. Here we use the fact that Add trait is overloaded for references to primitive types as well. This is probably the most generic version of this function.
If you are OK with moving away from function calling, you could wrap the x in your own type:
use std::ops::Add;
struct Adder<X> {
x: X
}
impl<X: Copy> Adder<X> {
fn add<Y: Add<X>>(&self, y: Y) -> <Y as Add<X>>::Output {
y + self.x
}
}
fn main() {
let x = Adder { x: 1usize };
x.add(2); // as opposed to x(2)
}
This means you can get rid of Box and don't need to allocate anything. Implementing Fn(..) is not possible in stable Rust, and the unstable version may break in future Rust versions. Look at std::ops::Fn for further information.

Resources