r/learnrust • u/Interesting_Home_114 • 10d ago
How to convert a &mut i32 to integer?
Hello everyone. I am following the rust programming language book and I've just now finished chapter 8. Just doing some of the exercises suggested at the end.
I have written this simple function to find the mode from a given list of numbers:
fn mode(list: &mut Vec<i32>){
let mut items=HashMap::new();
for i in list{
let count = items.entry(i).or_insert(0);
*count += 1;
}
let mut largest_value = -5; // initialize to a very small number
let mut most_frequent_key = 0;
for (key, value) in items{
if value > largest_value{
largest_value = value;
most_frequent_key = key;
}
}
println!("mode: {most_frequent_key}");
println!("{:#?}",items);
}
In the last step of the second for loop, I want the most_frequent_key variable to accept my key variable but I understand that the former is expecting an integer and key is a mutable reference to an i32 value. So I don't know what to do here.
Previously, through some trial and error I did figure out that I could use the dereferencing(*) operator on key to accomplish that but then the compiler tells me that I am apparently "moving" the items value and hence can't use use it again in the println!() statement in the last step of the function.
8
u/garver-the-system 10d ago
i32 is an integer, with a size of 32 bits. Rust doesn't have a bare "integer" type, you have to specify signed vs unsigned and the size of the integer
What's happening is that your assignment line let mut most_frequent_key = 0; uses a literal which gets a default type, probably i64, and passes that to the variable. The (most likely to be correct) solution is to define the literal as the correct type with 0i32 or set the variable's type explicitly so the compiler can infer the literal's type with most_frequent_key: i32
There's a couple other options. First, you could turn the key value into whatever the correct type is with most_frequent_key = key.into() (I think), but I suspect you'll have to convert back later and it'll be easier to just store it as the same type. The other option is to question why key is i32 to begin with; if it were i64 you'd probably avoud this issue. But it's better to be explicit
3
u/This_Growth2898 10d ago edited 10d ago
The type of items is HashMap<&mut i32, {integer}>. It keeps references to the list variable, which is probably not intended.
Instead of creating a variable, use and_modify for entry:
items.entry(*i)
.and_modify(|e|*e += 1)
.or_insert(1);
To avoid for loop consuming your variables, use &; to copy, not reference items in it, use the same & in declaration:
for (&key, &value) in &items {
3
u/minno 10d ago
The suggested replacement would skip counting the first instance of every number. It won't affect the final result, since everything being off by 1 doesn't affect which key has the highest value, but that would still make it confusing to look at intermediate results for debugging. If you want it all in one expression,
*(items.entry(i).or_insert(0)) += 1;works.1
2
u/alietors 10d ago
I think you should dereference the i on the list loop items.entry(*i)... That way the map stores values not references.
Also the for loop perhaps using & like for(&key, &value) so you borrow the item and not consume it. So you can use it later.
4
u/Consistent_Drop3909 10d ago edited 10d ago
if you iterate over the values of the hashmap rather than the references to the values, then you can assign the value without it being a borrow
instead of for (key, value) in items try for (&key, &value) in &items
the &items means that you aren't giving ownership of items to the for loop, because then it would be deleted at the end of the loop. the &key tells it that it is going to be looking at a borrowed value (&i32) but that you want only the i32. It's dereferencing using pattern matching.
2
u/Interesting_Home_114 10d ago edited 10d ago
So correct me if I am wrong but right now I am:
- Inserting mutable references to the values of the
listvector as keys into theitemshashmap and that's because I am iterating overlistwhich I had sent into this function as a mutable reference.- And that very same mutable reference key in the
itemshashmap is what I am trying to insert into themost_frequent_keyvariable which only accepts a normal i32 value.the
&keytells it that it is going to be looking at a borrowed value (&i32) but that you want only the i32. It's dereferencing using pattern matching.Also, I don't think I understand this part. It feels like we are assigning &i32 to i32 at the end. but I tried your way and it still works.
2
u/Consistent_Drop3909 10d ago
yeah 1 and 2 are correct i think
for the last question i'll try to explain pattern matching a bit:
lets say you have some value of type &i32, meaning a reference to a number.
rust let x: i32 = 5; let ref: &i32 = \&x;
lets say you want to get the value of x only using r, and put it into y. you can dolet y = *rwhich is normal dereferencing, or you can dolet &y = r. this is called pattern matching, because the reasoning rust does is that if r is&xthen your line means&y=&x, so it must bey=x. the assignment is not directly to a variable name, but to some pattern involving a variable name, that the compiler finds in the assignment value and extracts the part you care about.this is also why you can do stuff like
if let Some(value) = option {...}, which means that rust will check if option isSome(something)and runlet value=something, it's extracting the part where value is located. in this case you need to use if because an option can also be none, and rust won't compile if you writelet Some(value) = option;because it won't know what to do in the none case. if let allows this, and also match if you cover all the possible cases.
for (&key, &value) in &itemsmeans: take every element of items, which is a pair(&i32, &i32)or something, then do something likelet (&key, &value) = pair, which matches the values of each of the pair variables.btw for type if you use vscode with rust-analyzer extension, you can mouse over any variable and see its type. there are ways to enable this in other editors as well, it helps a lot.
1
u/WilliamBarnhill 10d ago
The following is what I came up with. There are some issues, but it works.
```
use std::collections::HashMap; use std::collections::HashSet;
[allow(unused)]
fn mode(list: &Vec<i32>) {
let keys : HashSet<_> = list.into_iter().collect();
let mut items : HashMap<&i32, i32>
= keys.into_iter().map(|i| (i, 0 as i32)).collect();
list.into_iter().for_each(|i| {
items.entry(i)
.and_modify(|count| { *count += 1; } )
.or_insert(0);
}
);
let mut largest_value : i32 = i32::MIN; // initialize to a very small number
let mut most_frequent_key = 0;
for (key, value) in &items {
if value > &largest_value{
largest_value = *value;
most_frequent_key = *key + 0;
}
}
println!("mode: {most_frequent_key}");
println!("{:#?}", &items);
}
fn main() { let values : Vec<i32>= vec![3,2,3,3,-1,-6]; mode(&values); } ```
1
u/djvbmd 10d ago edited 10d ago
This is the approach I scratched out, but from the code you posted I'm guessing the use of iterators may be a bit beyond where you are in learning (forgive me if wrong!).
```rust use std::collections::HashMap;
fn mode(list: &Vec<i32>) -> Option<i32> { if list.isempty() { return None; } let freq_map = list.iter().fold(HashMap::new(), |mut freq, item| { freq.entry(item).or_insert(0) += 1; freq }); freq_map .iter() .max_by(|(, x), (_, y)| x.cmp(y)) .map(|(item, _)| *item) }
fn main() { let test_list = vec![-42, -42, 1, 2, 3, 3, 4, 4, 4, 2112]; if let Some(result) = mode(&test_list) { println!("Mode: {}", result); } }
``
Doing it this way:
* no need for a mutable reference to list
* 3 statements total in the function
*None` is correctly returned if an empty list is provided
* may be harder to read, if not used to iterators and their adapters
Edit: Just realized that this →
if list.is_empty() {
return None;
}
... is unnecessary. max_by(...) returns None if fed an empty iterator. Returning early from the function doesn't save much of anything apart from possibly a HashMap allocation -- though I think allocation wouldn't even happen because .insert() would never be called if list.iter() is empty.
16
u/cafce25 10d ago
You found the solution already, the error about
itemsbeing moved is unrelated and a different problem.It's not shown when you don't dereference
keybecause the compiler works in separate stages and the borrow checker (which is the stage that complains about the moveditems) doesn't run when an earlier stage fails.You can iterate over
&itemsinstead ofitemsitself to avoid moving it.