This question already has answers here:
Expected closure, found a different closure
(2 answers)
What is the inferred type of a vector of closures?
(1 answer)
Closed 4 years ago.
I'm trying to build a simple RPN calculator, and I've got the basics working. I'd like to make a dispatch table to implement the various calculator functions. If I were doing this in Perl, I would write something like:
my %ops = (
'+' => sub { +shift + +shift; },
'-' => sub { +shift - +shift; },
'*' => sub { +shift * +shift; },
'/' => sub { +shift / +shift; }
);
or in JavaScript:
let ops = {
"+": (a, b) => a + b,
"-": (a, b) => a - b,
"*": (a, b) => a * b,
"/": (a, b) => a / b
};
This is what I've tried so far in Rust:
use std::collections::HashMap;
fn main() {
println!("Going to call +");
let dispatch = HashMap::new();
dispatch.insert(String::from("+"), |a, b| a + b);
dispatch.insert(String::from("-"), |a, b| a - b);
let plus = dispatch.get(&String::from("+"));
println!("2 + 3 = {}", plus(2, 3));
let minus = dispatch.get(&String::from("-"));
println!("2 - 3 = {}", minus(2, 3));
}
When I try compiling, I get these errors:
error[E0308]: mismatched types
--> src/main.rs:9:40
|
9 | dispatch.insert(String::from("-"), |a, b| a - b);
| ^^^^^^^^^^^^ expected closure, found a different closure
|
= note: expected type `[closure#src/main.rs:8:40: 8:52]`
found type `[closure#src/main.rs:9:40: 9: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
error[E0618]: expected function, found enum variant `plus`
--> src/main.rs:12:28
|
11 | let plus = dispatch.get(&String::from("+"));
| ---- `plus` defined here
12 | println!("2 + 3 = {}", plus(2, 3));
| ^^^^^^^^^^ not a function
help: `plus` is a unit variant, you need to write it without the parenthesis
|
12 | println!("2 + 3 = {}", plus);
| ^^^^
error[E0618]: expected function, found enum variant `minus`
--> src/main.rs:15:28
|
14 | let minus = dispatch.get(&String::from("-"));
| ----- `minus` defined here
15 | println!("2 - 3 = {}", minus(2, 3));
| ^^^^^^^^^^^ not a function
help: `minus` is a unit variant, you need to write it without the parenthesis
|
15 | println!("2 - 3 = {}", minus);
| ^^^^^
What does "no two closures, even if identical, have the same type" mean? How can I make a HashMap hold a closure, and then call it?
It sounds like using Box would fix it... Like I said, I'm pretty new, and I haven't used Box. How do I get what's in the box out of the box?
There are a few orthogonal issues here. First and foremost, your hashmap is immutable. You use let instead of let mut, which is good practice, but in order to be able to insert into it, we need it to (at least initially) be let mut. If you're planning to modify the hashmap after the initial construction, you may want to let mut the dispatch variable as well.
let dispatch = {
let mut temp = HashMap::new();
temp.insert(String::from("+"), |a, b| a + b);
temp.insert(String::from("-"), |a, b| a - b);
temp
};
Now you need an explicit type for your hashmap. The two closures you've defined, as far as the compiler is concerned, are of entirely distinct types. However, they are both compatible with fn(i32, i32) -> i32, the type of binary functions on i32 (you can replace i32 with a different numerical type if you wish), so let's make the type explicit.
let dispatch = {
let mut temp: HashMap<String, fn(i32, i32) -> i32> = HashMap::new();
temp.insert(String::from("+"), |a, b| a + b);
temp.insert(String::from("-"), |a, b| a - b);
temp
};
Finally, HashMap.get returns an std::option::Option, not a direct value, so we need to unwrap it. get returns None if the key isn't found. If this was a large project, we'd handle that error appropriately, perhaps by logging it or telling the user, but for something simple like this, we simply need to use expect, which essentially tells the compiler "Yes, I know this could go horribly wrong. I'm willfully ignoring that fact." which is perfectly fine for our simple example.
let plus = dispatch.get(&String::from("+")).expect("Couldn't find +");
let minus = dispatch.get(&String::from("-")).expect("Couldn't find -");
Complete example
use std::collections::HashMap;
fn main() {
let dispatch = {
let mut temp: HashMap<String, fn(i32, i32) -> i32> = HashMap::new();
temp.insert("+".into(), |a, b| a + b);
temp.insert("-".into(), |a, b| a - b);
temp
};
let plus = dispatch["+"];
println!("2 + 3 = {}", plus(2, 3));
let minus = dispatch["-"];
println!("2 - 3 = {}", minus(2, 3));
}
Note that you can replace String with &'static str:
let dispatch = {
let mut temp: HashMap<_, fn(i32, i32) -> i32> = HashMap::new();
temp.insert("+", |a, b| a + b);
temp.insert("-", |a, b| a - b);
temp
};
Related
So, I am trying to build a vector of vectors of a custom type.I am trying to implement a default for it and was successful with a for loop.
let mut data: Vec<Vec<Cell>> = Vec::new();
for _i in 0..63 {
let mut row: Vec<Cell> = Vec::with_capacity(64);
for _j in 0..63 {
row.push(Cell::default())
}
data.push(row);
}
I felt this code could do with some functional style and interators, so I decided to do something like so:
let data: Vec<Vec<Cell>> = Vec::with_capacity(64)
.iter_mut()
.map(|mut x: &mut Vec<Cell>| {
x = Vec::with_capacity(64)
.iter_mut()
.map(|mut y: Cell| y = Cell::default())
.collect()
})
.collect();
With this, I get an error like so:
error[E0631]: type mismatch in closure arguments
--> src/types.rs:124:26
|
124 | .map(|mut y: Cell| y = Cell::default())
| ^^^ ------------- found signature defined here
| |
| expected due to this
|
= note: expected closure signature `fn(&mut _) -> _`
found closure signature `fn(types::cell::Cell) -> _`
note: required by a bound in `map`
--> /home/naitik/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:779:12
|
779 | F: FnMut(Self::Item) -> B,
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
I do not understand the problem here. What exactly is it? What can I do to fix it?
You're misunderstanding how Vec::with_capacity works. It doesn't actually put anything into a vec that you can iterate over and assign to. You want to map Cell::default over something and then collect that into a vec. That something doesn't matter, only that there's 64 of them, so a range will do:
let data: Vec<Vec<Cell>> = (0..64)
.map(|_| (0..64).map(|_| Cell::default()).collect())
.collect();
collect should hopefully figure out the needed capacities from the ranges' TrustedLen impl on its own to avoid unnecessary re-allocations.
I would, however, doubt that this is better than the procedural approach. It seems less clear and harder to modify to me. Just stick to good ol' for loops is my two cents.
If Cell is Clone or Copy you could even just do:
let data: Vec<Vec<Cell>> = vec![vec![Cell::default(); 64]; 64];
for maximal minimality.
I am trying to write quick sort in Rust, I got it working when I used the specific type [i32] but I get an error when I try to use [T].
fn main() {
let mut arr = vec![4,3,2,1];
quick_sort(&mut arr);
println!("{:?}", arr);
}
fn partition<T: Ord>(slice: &mut [T]) -> usize {
let end = slice.len() - 1;
let pivot = slice[end];
let mut j = 0;
for i in 0..end {
if slice[j] <= pivot {
slice.swap(i, j);
j += 1;
}
}
slice.swap(end, j);
j
}
fn quick_sort<T: Ord>(slice: &mut [T]) {
if !slice.is_empty() {
let j = partition(slice);
let len = slice.len();
quick_sort(&mut slice[0..j]);
quick_sort(&mut slice[j+1..len]);
}
}
I get the following error:
error[E0508]: cannot move out of type `[T]`, a non-copy slice
--> src/main.rs:9:17
|
9| let pivot = slice[end];
| ^^^^^^^^^^ cannot move out of here
| move occurs because `slice[_]` has type `T`,
| which does not implement the `Copy` trait
| help: consider borrowing here: `&slice[end]`
When let pivot = &slice[end]; , I get a different error:
error[E0308]: mismatched types
--> src/main.rs:12:22
|
7| fn partition<T: Ord>(slice: &mut [T]) -> usize {
| - this type parameter
...
12| if slice[j] <= pivot {
| ^^^^^ expected type parameter `T`, found `&T`
= note: expected type parameter `T`
found reference `&T`
I cannot get this to work with [T].
We can fix the "expected type parameter T, found &T" error by changing the if to:
if &slice[j] <= pivot {
but that runs into another error:
error[E0502]: cannot borrow `*slice` as mutable because it is also borrowed as immutable
--> src/main.rs:13:13
|
9 | let pivot = &slice[end];
| ----------- immutable borrow occurs here
...
12 | if &slice[j] <= pivot {
| ----- immutable borrow later used here
13 | slice.swap(i, j);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
This is because we are taking a reference to a value from the slice and while that reference is alive, we are calling a method that requires mutable access to the slice.
To fix this, we can inline the referencing into the if statement itself:
fn partition<T: Ord>(slice: &mut [T]) -> usize {
let end = slice.len() - 1;
let mut j = 0;
for i in 0..end {
if slice[j] <= slice[end] {
slice.swap(i, j);
j += 1;
}
}
slice.swap(end, j);
j
}
Output:
[1, 2, 3, 4]
Playground
The reason this worked for [i32] is because calling slice[end] implicitly created a copy of the value because i32 implements the Copy trait. If a type does not implement Copy, you need to either take a reference using &slice[index] or if it implements Clone, call slice[index].clone(). In this code you have a generic T which does not implement either of those.
I am trying to create a HashMap using functional programming and utilizing parallelization by rayon.
If I try this without rayon, it works:
use std::collections::HashMap;
fn main() {
let nums = [1, 2, 1, 2, 1, 2];
let result: HashMap<i32, i32> =
nums.iter()
.filter(|x| *x % 2 == 0)
.fold(HashMap::new(), |mut acc, x| {
*acc.entry(*x).or_insert(0) += 1;
acc
});
println!("{:?}", result);
}
If I try to use multiple cores by switching from iter() to par_iter(), I get an error:
use rayon::prelude::*; // 1.5.1
use std::collections::HashMap;
fn main() {
let nums = [1, 2, 1, 2, 1, 2];
let result: HashMap<i32, i32> =
nums.par_iter()
.filter(|x| *x % 2 == 0)
.fold(HashMap::new(), |mut acc, x| {
*acc.entry(*x).or_insert(0) += 1;
acc
});
println!("{:?}", result);
}
error[E0277]: expected a `Fn<()>` closure, found `HashMap<_, _>`
--> src/main.rs:9:19
|
9 | .fold(HashMap::new(), |mut acc, x| {
| ^^^^^^^^^^^^^^ expected an `Fn<()>` closure, found `HashMap<_, _>`
|
= help: the trait `Fn<()>` is not implemented for `HashMap<_, _>`
= note: wrap the `HashMap<_, _>` in a closure with no arguments: `|| { /* code */ }`
error[E0308]: mismatched types
--> src/main.rs:7:9
|
6 | let result: HashMap<i32, i32> =
| ----------------- expected due to this
7 | / nums.par_iter()
8 | | .filter(|x| *x % 2 == 0)
9 | | .fold(HashMap::new(), |mut acc, x| {
10 | | *acc.entry(*x).or_insert(0) += 1;
11 | | acc
12 | | });
| |______________^ expected struct `HashMap`, found struct `Fold`
|
= note: expected struct `HashMap<i32, i32>`
found struct `Fold<rayon::iter::Filter<rayon::slice::Iter<'_, {integer}>, [closure#src/main.rs:8:21: 8:36]>, HashMap<_, _>, _>`
Obviously, Rust tries to stop me from doing something stupid involving race conditions, but how would I build a HashMap inside a par_iter()?
Rayon's fold creates intermediate items (cannot be known how many). From the documentation (emphasis mine):
Parallel fold is similar to sequential fold except that the
sequence of items may be subdivided before it is
folded. Consider a list of numbers like 22 3 77 89 46. If
you used sequential fold to add them (fold(0, |a,b| a+b),
you would wind up first adding 0 + 22, then 22 + 3, then 25 +
77, and so forth. The parallel fold works similarly except
that it first breaks up your list into sublists, and hence
instead of yielding up a single sum at the end, it yields up
multiple sums. The number of results is nondeterministic, as
is the point where the breaks occur.
You need to reduce those intermediate items into the final one:
use rayon::prelude::*; // 1.5.1
use std::collections::HashMap;
fn main() {
let nums = [1, 2, 1, 2, 1, 2];
let result: HashMap<i32, i32> = nums
.par_iter()
.filter(|x| *x % 2 == 0)
.fold(HashMap::new, |mut acc, x| {
*acc.entry(*x).or_insert(0) += 1;
acc
})
.reduce_with(|mut m1, m2| {
for (k, v) in m2 {
*m1.entry(k).or_default() += v;
}
m1
})
.unwrap();
println!("{:?}", result);
}
Playground
Also note that the first parameter of Rayon's fold is a function that creates the empty HashMap, not an empty HashMap like the standard library's fold.
How can I get a mutable reference to an item found in a vector?
I've tried the following which works if I don't make the iterator mutable using .iter():
fn main() {
let mut vec = vec![1, 2, 3, 4];
let mut wrong = -1;
let working = match vec.iter().find(|&c| *c == 2) {
Some(c) => c,
None => &wrong
};
println!("Result: {}", working);
}
But when I try to get a mutable reference using a mutable iterator .iter_mut(),
fn main() {
let mut vec = vec![1, 2, 3, 4];
let mut wrong = -1;
let mut error = match vec.iter_mut().find(|&c| *c == 2) {
Some(c) => c,
None => &mut wrong
};
println!("Result: {}", error);
}
I get the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:5:48
|
5 | let mut error = match vec.iter_mut().find(|&c| *c == 2) {
| ^-
| ||
| |hint: to prevent move, use `ref c` or `ref mut c`
| cannot move out of borrowed content
I also tried to make the type in the closure mutable with |&mut c| but that gives the following error:
error[E0308]: mismatched types
--> src/main.rs:5:48
|
5 | let mut error = match vec.iter_mut().find(|&mut c| *c == 2) {
| ^^^^^^ types differ in mutability
|
= note: expected type `&&mut {integer}`
found type `&mut _`
= help: did you mean `mut c: &&&mut {integer}`?
Rust's .find passes the callback the type &Self::Item, and since you are using .iter_mut(), you've created an iterator where each item is &mut T. That means the type passed to your find callback is &&mut T. To get that to typecheck, you can do either
vec.iter_mut().find(|&&mut c| c == 2)
or
vec.iter_mut().find(|c| **c == 2)
with the second one being preferable.
The error you are getting is because the middle-ground you've chosen by using &c would set c to a value of &mut T, and one of Rust's big rules is that multiple things can't own a mutable reference to an item at the same time. Your non-mutable case works because you are allowed to have multiple immutable references to an item.
I am attempting to generate a Vec<(Point, f64)>:
let grid_size = 5;
let points_in_grid = (0..grid_size).flat_map(|x| {
(0..grid_size)
.map(|y| Point::new(f64::from(x), f64::from(y)))
.collect::<Vec<Point>>()
});
let origin = Point::origin();
let points_and_distances = points_in_grid
.map(|point| (point, point.distance_to(&origin)))
.collect::<Vec<(Point, f64)>>();
I get the following error:
use of moved value: point
I understand that I cannot use point in both elements of the tuple, but when I attempt to store a reference, I get an error regarding lifetime.
I am presuming your Point struct looks like the following:
#[derive(Debug)]
struct Point(f64, f64);
impl Point {
fn new(x: f64, y: f64) -> Self { Point(x, y) }
fn origin() -> Self { Point(0.,0.) }
fn distance_to(&self, other: &Point) -> f64 {
((other.0 - self.0).powi(2) + (other.1 - self.1).powi(2)).sqrt()
}
}
Now let's look at an even simpler example that will not compile:
let x = Point::new(2.5, 1.0);
let y = x;
let d = x.distance_to(&y);
Which gives the error:
error[E0382]: use of moved value: `x`
--> <anon>:15:13
|
14 | let y = x;
| - value moved here
15 | let d = x.distance_to(&y);
| ^ value used here after move
|
= note: move occurs because `x` has type `Point`, which does not implement the `Copy` trait
Because x has been moved into y, it now can't have a reference taken in order to call the distance_to function.
The important thing to note here is that order matters - if we swap the lines over we can call distance_to by borrowing x, the borrow will end and then x can be moved into y.
let x = Point(0., 0.);
let d = x.distance_to(&y);
let y = x; // compiles
In your case, a very similar thing is happening when constructing the tuple. point gets moved into the tuple, and then tries to borrow it to form the second element. The simplest solution is to do the same thing as here: swap the order of the elements of the tuple.
let points_and_distances = points_in_grid
.map(|point| (point.distance_to(&origin), point))
.collect::<Vec<(f64, Point)>>(); // compiles
Playground link
N.B. if you want to retain the order:
.map(|(a, b)| (b, a))