(rust noob here; I'm trying to understand what can/cannot/should/shouldn't be passed by reference in a higher order function situation)
let a = [1, 2, 3];
This invocation works:
let sum = a.iter().fold(0, |acc: i32, x: &i32| acc + x);
These do not:
let sum = a.iter().fold(0, |acc: i32, x: i32| acc + x);
let sum = a.iter().fold(0, |acc: &i32, x: i32| acc + x);
The error message is
error[E0631]: type mismatch in closure arguments
--> main.rs:8:22
|
8 | let sum = a.iter().fold(0, |acc: &i32, x: i32| acc + x);
| ^^^^ --------------------------- found signature of `for<'r> fn(&'r i32, i32) -> _`
| |
| expected signature of `fn({integer}, &{integer}) -> _`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0631`.
The explanation does not offer anything of interest. It says the arguments of the closure do not match those of the arguments of fold. I however cannot see how it follows from the declaration of fold:
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B
Why is the second argument supposed to be &{integer}, and the first one {integer}?
The items in the iterator are borrowed from the array, so are &i32 not i32. This form works because the accumulator is owned, while the items are borrowed:
let sum = a.iter().fold(0, |acc: i32, x: &i32| acc + x);
You can convert the iterator so its items are copied instead of referenced, and then the first form will work:
let sum = a.iter().copied().fold(0, |acc: i32, x: i32| acc + x);
The third form can never work. The closure needs to be able to return a new value with which to update the accumulator. The type of the accumulator is i32. It can't be a reference because you can't return a reference from a closure (the original value would be dropped and Rust won't let you return a dangling pointer).
Looking at the declaration of fold()
fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B
you can see that the function has two generic type parameters – the type of the accumulator B and the type of the closure F. The closure type has the trait bound FnMut(B, Self::Item) -> B, meaning that the type of the first argument is identical to the type of the accumulator, while the type of the second argument is the item type of the iterator.
In the invocation
let sum = a.iter().fold(0, |acc, x| acc + x);
we use an iterator with the item type &i32, and your accumulator is initialised to 0, so it's type is inferred to be i32 (which is the default integer type in the absence of any further qualification).
Related
What is the type for
let plus:Plus = |x| move |y| x + y;
Obviously,
type Plus = fn(isize) -> fn(isize) -> isize;
won't work since the last part is not a function pointer but a closure.
The type is impl Fn(isize) -> (impl Fn(isize) -> isize). It can be coerced to fn(isize) -> (impl Fn(isize) -> isize), because the outer closure does not capture.
However, you cannot express this type in today's Rust. With the nightly feature type_alias_impl_trait, you can break it into two types:
#![feature(type_alias_impl_trait)]
type Adder = impl Fn(isize) -> isize;
type Plus = fn(isize) -> Adder;
let plus: Plus = |x| move |y| x + y;
Playground.
Or you can box it:
type Plus = fn(isize) -> Box<dyn Fn(isize) -> isize>;
let plus: Plus = |x| Box::new(move |y| x + y);
Playground.
I'm new to Rust lang and wonder what difference between two blocks in fn main(), can anyone explain in words of lifetime.
Are .as_str() calls change the lifetime of x and y?
Whose lifetime does -> &a' str refer to? a,b,c at lifetime() returning position or z to receive the result?
I consider the variables x and y have the same lifetime scopes in both blocks. And that of z covers the x's and y's.
If (x: &'a str, y: &'a str, z: &'a str) demands same lifetime scope of x,y,z, both blocks should fail.
fn main()
{
let mut z = "123abc";
{//// CAN ONLY SUCCEED WHEN REMOVE THE BRACES OF THIS BLOCK
let x = String::from("ajoisd");
let y = String::from("aso");
z = lifetime(x.as_str(), y.as_str(), z);
}
{//// GOES WELL WITH/WITHOUT BRACES
let x = "ajoisd";
let y = "aso";
z = lifetime(x, y, z);
}
println!("{}", z);
}
fn lifetime<'a>(a: &'a str, b: &'a str, c: &'a str) -> &'a str
{
if a.len() > b.len() {a}
else if a.len() < b.len() {b}
else {c}
}
Strings "ajoisd" and "aso" have the 'static lifetime. They outlive any variable in the program. Variables x and y in the first block live only in this block which is less than the lifetime of z.
This question is more complex than Closure as function parameter “cannot infer an appropriate lifetime due to conflicting requirements”.
There's a recursive closure which move environmental variable into it.
The code below works, tool is a grab-bag of useful functions for functional programming includes making recursive closure:
extern crate tool;
use tool::prelude::*;
use std::cell::Cell;
fn main() {
let a = Cell::new(false);
let fib = fix(move |f, x| {
a.set(true);
if x == 0 || x == 1 {
x
} else {
// `f` is `fib`
f(x - 1) + f(x - 2)
}
});
println!("{}", fib(10));
}
I want to know is it possible to pass that closure to a function, then call that function in that closure, the code below throws an error.
extern crate tool;
use tool::prelude::*;
use std::cell::RefCell;
fn main() {
let a = RefCell::new(false);
let fib = fix(move |f, x| {
*a.borrow_mut() = true;
if x == 0 || x == 1 {
x
} else {
// `f` is `fib`
b(Box::new(f), x - 1) + f(x - 2)
}
});
fn b (c: Box<Fn(u64) -> u64>, n: u64) -> u64 {
c(n)
}
println!("{}", b(Box::new(fib), 10));
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:14:24
|
14 | b(Box::new(f), x - 1) + f(x - 2)
| ^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 8:19...
--> src/main.rs:8:19
|
8 | let fib = fix(move |f, x| {
| ___________________^
9 | | *a.borrow_mut() = true;
10 | | if x == 0 || x == 1 {
11 | | x
... |
15 | | }
16 | | });
| |_____^
= note: ...so that the expression is assignable:
expected &dyn std::ops::Fn(u64) -> u64
found &dyn std::ops::Fn(u64) -> u64
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<(dyn std::ops::Fn(u64) -> u64 + 'static)>
found std::boxed::Box<dyn std::ops::Fn(u64) -> u64>
It looks like you are mixing several concepts here. First of all you must understand the difference between these:
fn(A) -> B
impl Fn(A) -> B or T: Fn(A) -> B
&dyn Fn(A) -> B
Box<dyn Fn(A) -> B>
Number 1 is the type of a pointer to a function, just like in C.
Number 2 is a generic type that implements the function trait Fn, that is a type that is callable.
Number 3 is a dynamic reference to a callable object (the dyn keyword is optional).
Number 4 is a trait object, that is a boxed callable object with the real type erased.
Now look at the definition of tool::fix:
pub fn fix<A, B, F>(f: F) -> impl Fn(A) -> B
where
F: Fn(&Fn(A) -> B, A) -> B,
From that you see that fix uses number 2 for the f parameter, but number 3 for the A parameter of f. Also, it returns number 2.
The tricky part here is that f is a function that takes a function as argument. The f itself can be any of any kind that implements Fn, but the first argument of that function must be of &dyn Fn kind.
Your original error comes from trying to box a &dyn Fn(A) -> B, but you cannot do that generically, because such a value may contain references, and Box requires a 'static type.
But with all that in mind you can carefully write your function without using Box, so your problem just disappears, and the result is nicer (playground):
fn main() {
fn wrap (wrap_fn: impl Fn(&dyn Fn(u64) -> u64, u64) -> u64) -> impl Fn(u64) -> u64 {
let a = RefCell::new(false);
let fib = fix(move |f, x| {
*a.borrow_mut() = true;
if x == 0 || x == 1 {
x
} else {
// `f` is `fib`
wrap_fn(f, x - 1) + wrap_fn(f, x - 2)
}
});
fib
}
fn b (c: &dyn Fn(u64) -> u64, n: u64) -> u64 {
c(n)
}
println!("{}", (wrap(b))(10));
}
I'm trying to write a function that composes two functions. The initial design is pretty simple: a function that takes two functions and returns a composed function which I can then compose with other functions, since Rust doesn't have rest parameters. I've run into a wall built with frustrating non-helpful compiler errors.
My compose function:
fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
where
F: 'a + Fn(A) -> B + Sized,
G: 'a + Fn(B) -> C + Sized,
{
Box::new(move |x| g(f(x)))
}
How I would like to use it:
fn main() {
let addAndMultiply = compose(|x| x * 2, |x| x + 2);
let divideAndSubtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(*addAndMultiply, *divideAndSubtract);
println!("Result is {}", finally(10));
}
The compiler doesn't like that, no matter what I try, the trait bounds are never satisfied. The error is:
error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time
--> src/main.rs:13:19
|
13 | let finally = compose(*addAndMultiply, *divideAndSubtract);
| ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _`
= 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 by `compose`
--> src/main.rs:1:1
|
1 | / fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
2 | | where
3 | | F: 'a + Fn(A) -> B + Sized,
4 | | G: 'a + Fn(B) -> C + Sized,
5 | | {
6 | | Box::new(move |x| g(f(x)))
7 | | }
| |_^
As #ljedrz points out, to make it work you only need to reference the composed functions again:
let finally = compose(&*multiply_and_add, &*divide_and_subtract);
(Note that in Rust, convention dictates that variable names should be in snake_case)
However, we can make this better!
Since Rust 1.26, we can use abstract return types (previously featured gated as #![feature(conservative_impl_trait)]). This can help you simplify your example greatly, as it allows you to skip the lifetimes, references, Sized constraints and Boxes:
fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
fn main() {
let multiply_and_add = compose(|x| x * 2, |x| x + 2);
let divide_and_subtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(multiply_and_add, divide_and_subtract);
println!("Result is {}", finally(10));
}
Finally, since you mention rest parameters, I suspect that what you actually want is to have a way to chain-compose as many functions as you want in a flexible manner. I wrote this macro for this purpose:
macro_rules! compose {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
compose_two($head, compose!($($tail),+))
};
}
fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
fn main() {
let add = |x| x + 2;
let multiply = |x| x * 2;
let divide = |x| x / 2;
let intermediate = compose!(add, multiply, divide);
let subtract = |x| x - 2;
let finally = compose!(intermediate, subtract);
println!("Result is {}", finally(10));
}
Just add references in finally and it will work:
fn main() {
let addAndMultiply = compose(|x| x * 2, |x| x + 2);
let divideAndSubtract = compose(|x| x / 2, |x| x - 2);
let finally = compose(&*addAndMultiply, &*divideAndSubtract);
println!("Result is {}", finally(10));
}
Dereferencing addAndMultiply or divideAndSubtract uncovers a trait object which is not Sized; it needs to either be wrapped in a Box or referenced in order for it to be passed to a function with a Sized constraint.
macro_rules! comp {
($f: expr) => {
move |g: fn(_) -> _| move |x: _| $f(g(x))
};
}
fn main() {
let add1 = |x| x + 1;
let add2 = |x| x + 2;
let add3 = comp!(add1)(add2);
println!("{}", add3(3));
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1c6915d94f7e1e35cf93fb21daceb9ef
Previously a question was asked about creating an array of functions where the functions returned integers from a range. The final solution was to do a map/collect into a Vec<_>.
I have a similar yet different situation where I have closures with the same signature but different implementations. I tried this:
let xs: Vec<_> = vec![
move |(x, y)| (y, x),
move |(x, y)| (1 - y, 1 - x),
];
The error I get back:
error[E0308]: mismatched types
--> src/main.rs:4:9
|
4 | move |(x, y)| (1 - y, 1 - x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected type `[closure#src/main.rs:3:9: 3:29]`
found type `[closure#src/main.rs:4:9: 4:37]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
I tried boxing:
let xs: Vec<_> = vec![
Box::new(move |x: u8, y: u8| (y, x)),
Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
];
I get back the same error:
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected type `[closure#src/main.rs:3:18: 3:44]`
found type `[closure#src/main.rs:4:18: 4:52]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
What is the right way to box closures so that they can be put into a vector (or an array)?
The problem is that type inference has kicked in before you wanted it to. Conceptually, it's like this:
let mut xs: Vec<_> = Vec::new();
xs.push(Type1);
xs.push(Type2);
When the first value is seen, the type of the Vec's elements is inferred to be of that type. The second element then causes a mismatch.
Even when you Box the values, you have the same problem:
let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1));
xs.push(Box::new(Type2));
Looking at it another way, you never actually created a trait object. You have a Box<ConcreteType>, not a Box<dyn Trait>.
The solution is to cast the boxed concrete types to the boxed trait object:
let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1) as Box<dyn Trait>);
xs.push(Box::new(Type2) as Box<dyn Trait>);
The second push can have the type coerced automatically, so you can choose to leave the as bit off of that line.
Rolling this back up to the original problem:
let xs: Vec<_> = vec![
Box::new(move |(x, y)| (y, x)) as Box<dyn Fn((i32, i32)) -> (i32, i32)>,
Box::new(move |(x, y)| (1 - y, 1 - x)),
];
Or you can avoid the inference at all by specifying the type on the variable, my preferred style for this:
let xs: Vec<Box<dyn Fn((i32, i32)) -> (i32, i32)>> = vec![
Box::new(move |(x, y)| (y, x)),
Box::new(move |(x, y)| (1 - y, 1 - x)),
];
You should read the suggestion in the error message as "consider boxing your closure and using it as a trait object, or using it just as a trait object".
Using trait object references without boxing them will not work here because nothing owns the closures. The references in the vector would outlive the closures:
// This fails
let xs: Vec<&Fn((i32, i32)) -> (i32, i32)> = vec![
&move |(x, y)| (y, x),
&move |(x, y)| (1 - y, 1 - x),
];
The vector needs to take ownership of the closures, which is where boxing the trait objects comes into play:
let xs: Vec<Box<Fn((i32, i32)) -> (i32, i32)>> = vec![
Box::new(move |(x, y)| (y, x)),
Box::new(move |(x, y)| (1 - y, 1 - x)),
];
This explicitly tells the compiler that the vector can contain boxes of any closure with the same interface.