I am new to Rust and I have this error at compilation time but I don't get it
error[E0614]: type `Option<u32>` cannot be dereferenced
--> src/main.rs:9:5
|
9 | *mymap.insert("hello world", 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here is my code simplified to reproduce the issue:
use std::collections::HashMap;
fn main() {
let mymap: HashMap<&str, u32> = HashMap::new();
f(&mymap)
}
fn f(mymap: &HashMap<&str, u32>) {
*mymap.insert("hello world", 0);
}
Also the following does not work either
*mymap.insert("hello world", &0);
I don't find the root cause of my problem by googling it, I think I don't have the words. It looks like some borrowing issue.
You're actually not dereferencing mymap, you're actually dereferencing the result of insert(), because dereferencing (i.e. *) have a weaker precedence than a method call.
So it complains about dereferencing an Option<u32> because that's what insert() returns. If you actually wanted to dereference mymap you'd have to write (*mymap).insert("hello world", 0);. However, that is not needed in Rust.
If you remove the * then you will get a second problem, which is that you're attempting to mutate mymap, which is an immutable reference. So to be able to insert, i.e. mutate mymap in f, you need to pass it a mutable reference, i.e. mymap: &mut HashMap<&str, u32>.
use std::collections::HashMap;
fn main() {
let mut mymap: HashMap<&str, u32> = HashMap::new();
f(&mut mymap)
}
fn f(mymap: &mut HashMap<&str, u32>) {
mymap.insert("hello world", 0);
}
You don't need to dereference references on method calls, Rust will automatically do that for you. Also, you need to pass a mutable reference of mymap so you can actually perform the insert. Fixed example:
use std::collections::HashMap;
fn main() {
let mut mymap: HashMap<&str, u32> = HashMap::new();
f(&mut mymap)
}
fn f(mymap: &mut HashMap<&str, u32>) {
mymap.insert("hello world", 0);
}
playground
Related
I've instantiated a struct result and pass it as mutable ref to another function, that fills this struct with data.
AFTER that, I pass this struct as immutable ref to other functions, to insert the data into a database.
let mut result = indexer::IndexRefreshResultHolder {
putlist: Vec::new(),
dellist: Vec::new(),
};
indexer::refresh_indeces(&mut new_idx_set, old_idx_set_opt, &mut result);
pg::delete_index_rows(&mut tx, &result.dellist).await?;
pg::insert_index_rows(&mut tx, &result.putlist).await?;
Signature of refresh_indeces is like below:
pub fn refresh_indeces<'a>(
new_idx: &'a mut IndexSet,
old_idx_opt: Option<&'a mut IndexSet>,
result: &'a mut IndexRefreshResultHolder<'a>,
) -> Result<(), AppError>
The function takes data from new_idx and old_idx and merges it into result.
Compiler error:
cannot borrow 'result.dellist' as immutable because it is also borrowed as mutable. Same for 'result.putlist'.
I understand that since the struct was mutable borrowed to refresh_indeces it can not be made sure, that data changes afterwards.
My question now is: "How can I make the compiler understand, that result is not changed after refresh_indeces was called" or in other words: "How can result passed as immutable ref again after it was passed once as mutable ref?
Thanks for you help.
Playground
Just use a new scope so the &mut is dropped before the other refs need to be used:
let mut result = indexer::IndexRefreshResultHolder {
putlist: Vec::new(),
dellist: Vec::new(),
};
{
indexer::refresh_indeces(&mut new_idx_set, old_idx_set_opt, &mut result);
}
pg::delete_index_rows(&mut tx, &result.dellist).await?;
pg::insert_index_rows(&mut tx, &result.putlist).await?;
EDIT: Actually your lifetimes are wrong, since they are enforcing to extend a lifetime for the &mut. You can separate them:
fn refresh_indeces<'a, 'b, 'c>(
new_idx: &'a mut IndexSet,
old_idx_opt: Option<&'a mut IndexSet>,
result: &'b mut IndexRefreshResultHolder<'a>,
) -> Result<(), std::io::Error>
Playground
Also, you are creating some temporary references in your playground that will need fixing. And after that another bunch of fixes about ownership will come too
Resolved it by replacing slicing:
result.dellist.extend(old_idx.rows[i_old..].to_vec().iter());
with an iterator:
for row in old_idx.rows.iter().skip(i_old) {
result.dellist.push(row);
}
I have a code looks like this:
use std::collections::HashMap;
fn main() {
let x = get_hash_map();
println!("{:?}", x);
}
fn get_hash_map() -> Option<&'static Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.get("1");
}
But I got this error:
error[E0515]: cannot return value referencing local variable `hm`
--> src/main.rs:13:12
|
13 | return hm.get("1");
| --^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `hm` is borrowed here
Here is the rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7605176aee2dd3ff77a0cfd04a89db55
Can anyone suggest the alternatives to fix this problem minimally? Thanks!
fn get_hash_map() -> Option<&'static Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.get("1");
}
This is invalid, because you have declared you are going to return an Option<&'static Vec<i32>>, but you are returning an Option<&'a Vec<i32>> where 'a is the current function lifetime. The HashMap will stop existing as soon as the function returns, freeing the vector, and the reference will then become dangling. This is the exact kind of situation the borrow checker is designed to avoid.
Just return the vector by value:
fn get_hash_map() -> Option<Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.remove("1");
}
remove moves the value out of the map, returning it as an Option<V>.
If you then need an Option<&Vec<i32>>, you can just use as_ref() on your Option<Vec<i32>> to get one. Always remember it will become invalid as soon as its value goes out of scope.
The HashMap hm is local to the scope of the get_hash_map() function and is dropped as soon as get_hash_map() returns. The value returned by hm.get("1") contains a reference to this HashMap, thus its lifetime is also tied to the scope of get_hash_map() which unfortunately is shorter than the ascribed 'static lifetime.
If you remove the 'static lifetime and replaced it by some 'a annotation on the function, you would get a similar error, since again there is no (sound) way to return borrowed data from a function that creates the owner of that data.
You can however create the map in the surrounding scope and pass it via a mutable reference to get_hash_map
use std::collections::HashMap;
fn main() {
let mut hm = HashMap::new();
let x = get_hash_map(&mut hm);
println!("{:?}", x);
}
// note that both `hm` and the reference in the return type have the same 'a lifetime.
fn get_hash_map<'a>(hm: &'a mut HashMap<String, Vec<i32>>) -> Option<&'a Vec<i32>> {
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
hm.get("1");
}
I have a function f that accepts two references, one mut and one not mut. I have values for f inside a HashMap:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
let mut map = HashMap::new();
map.insert("1", 1);
map.insert("2", 2);
{
let a: &i32 = map.get("1").unwrap();
println!("a: {}", a);
let b: &mut i32 = map.get_mut("2").unwrap();
println!("b: {}", b);
*b = 5;
}
println!("Results: {:?}", map)
}
This doesn't work because HashMap::get and HashMap::get_mut attempt to mutably borrow and immutably borrow at the same time:
error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
--> src/main.rs:15:27
|
12 | let a: &i32 = map.get("1").unwrap();
| --- immutable borrow occurs here
...
15 | let b: &mut i32 = map.get_mut("2").unwrap();
| ^^^ mutable borrow occurs here
...
18 | }
| - immutable borrow ends here
In my real code I'm using a large, complex structure instead of a i32 so it is not a good idea to clone it.
In fact, I'm borrowing two different things mutably/immutably, like:
struct HashMap {
a: i32,
b: i32,
}
let mut map = HashMap { a: 1, b: 2 };
let a = &map.a;
let b = &mut map.b;
Is there any way to explain to the compiler that this is actually safe code?
I see how it possible to solve in the concrete case with iter_mut:
{
let mut a: &i32 = unsafe { mem::uninitialized() };
let mut b: &mut i32 = unsafe { mem::uninitialized() };
for (k, mut v) in &mut map {
match *k {
"1" => {
a = v;
}
"2" => {
b = v;
}
_ => {}
}
}
f(a, b);
}
But this is slow in comparison with HashMap::get/get_mut
TL;DR: You will need to change the type of HashMap
When using a method, the compiler does not inspect the interior of a method, or perform any runtime simulation: it only bases its ownership/borrow-checking analysis on the signature of the method.
In your case, this means that:
using get will borrow the entire HashMap for as long as the reference lives,
using get_mut will mutably borrow the entire HashMap for as long as the reference lives.
And therefore, it is not possible with a HashMap<K, V> to obtain both a &V and &mut V at the same time.
The work-around, therefore, is to avoid the need for a &mut V entirely.
This can be accomplished by using Cell or RefCell:
Turn your HashMap into HashMap<K, RefCell<V>>,
Use get in both cases,
Use borrow() to get a reference and borrow_mut() to get a mutable reference.
use std::{cell::RefCell, collections::HashMap};
fn main() {
let mut map = HashMap::new();
map.insert("1", RefCell::new(1));
map.insert("2", RefCell::new(2));
{
let a = map.get("1").unwrap();
println!("a: {}", a.borrow());
let b = map.get("2").unwrap();
println!("b: {}", b.borrow());
*b.borrow_mut() = 5;
}
println!("Results: {:?}", map);
}
This will add a runtime check each time you call borrow() or borrow_mut(), and will panic if you ever attempt to use them incorrectly (if the two keys are equal, unlike your expectations).
As for using fields: this works because the compiler can reason about borrowing status on a per-field basis.
Something appears to have changed since the question was asked. In Rust 1.38.0 (possibly earlier), the following compiles and works:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
let mut map = HashMap::new();
map.insert("1", 1);
map.insert("2", 2);
let a: &i32 = map.get("1").unwrap();
println!("a: {}", a);
let b: &mut i32 = map.get_mut("2").unwrap();
println!("b: {}", b);
*b = 5;
println!("Results: {:?}", map)
}
playground
There is no need for RefCell, nor is there even a need for the inner scope.
I am trying to convert a vector of &str pairs into a HashMap with the following code snippet:
use std::collections::HashMap;
fn main() {
let pairs = vec!(("foo", "bar"), ("toto", "tata"));
let map: HashMap<&str, &str> = pairs.iter().collect();
println!("{:?}", map);
}
However the compilation fails with this error:
<anon>:5:47: 5:56 error: the trait `core::iter::FromIterator<&(&str, &str)>` is not implemented for the type `std::collections::hash::map::HashMap<&str, &str>` [E0277]
<anon>:5 let map: HashMap<&str, &str> = pairs.iter().collect();
However if I add .cloned() before calling collect() everything works fine:
...
let map: HashMap<&str, &str> = pairs.iter().cloned().collect();
...
Even if I understand the error message (there is no implementation of the trait FromIterator<&(&str, &str)> for the type HashMap<&str, &str>) I do not understand where the type &(&str, &str) comes from (according to the method signature in the Rust documentation) and why calling cloned() fixes that problem.
The type &(&str, &str) comes from what iter() on a Vec returns:
fn iter(&self) -> Iter<T>
where Iter<T> implements Iterator<Item=&T>:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T
...
}
In other words, iter() on a vector returns an iterator yielding references into the vector.
cloned() solves the problem because it is an iterator adapter which converts Iterator<Item=&T> to Iterator<Item=T> if T is cloneable. You can think of it as a shorthand for map(|v| v.clone()):
let v1: Vec<i32> = vec![1, 2, 3, 4];
let v2: Vec<_> = v1.iter().cloned().collect();
let v3: Vec<_> = v1.iter().map(|v| v.clone()).collect();
assert_eq!(v2, v3);
It happens that (&str, &str) is cloneable because each tuple component is also cloneable (all references are), so cloned() would return an object which implements Iterator<Item=(&str, &str)> - exactly what collect() needs to create a HashMap.
Alternatively, you can use into_iter() to get Iterator<Item=T> from Vec<T>, but then the original vector will be consumed:
use std::collections::HashMap;
fn main() {
let pairs = vec!(("foo", "bar"), ("toto", "tata"));
let map: HashMap<&str, &str> = pairs.into_iter().collect();
println!("{:?}", map);
}
The problem is that while the references may be copied, the tuples cannot.
However, if you don't need the pairs anymore, you can iterate by values:
use std::collections::HashMap;
fn main() {
let pairs = vec!(("foo", "bar"), ("toto", "tata"));
let map: HashMap<&'static str, &'static str> = pairs.into_iter().collect();
println!("{:?}", map);
}
Vec<T> has two methods:
fn push(&mut self, value: T)
fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])
They both take a mutable reference to the vector. But the scope of the borrow seems to be different, e.g:
fn works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
nums.push(5);
println!("{}", nums.len());
}
fn doesnt_work() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let (l,r) = nums.split_at_mut(2);
println!("{}", nums.len());
}
fn also_works() {
let mut nums: Vec<i64> = vec![1,2,3,4];
let _ = nums.split_at_mut(2);
println!("{}", nums.len());
}
The doesnt_work function doesn't compile, saying there is already a mutable borrow on nums and that it ends and the end of the function. The problem goes away if I ignore the values returned from split_at_mut.
The borrowing of nums in doesnt_work will last as long as the variables l and r exist because the values in the vector (and the vector itself) have literally been borrowed and are now accessible only through l and r.
You can see this effect by putting the let for l and r in a scope which ends so the borrow also ends. For example this code works fine but if you try to move the println! inside the scope (inside the curly brackets) then it will fail:
fn works() {
let mut nums = vec![1,2,3,4];
{
let (l, r) = nums.split_at_mut(2);
//println!("{}", nums.len()); //println! will fail here
}
println!("{}", nums.len());
}
In your also_works example you don't do anything with the result so the borrow is lost immediately. Basically the compiler can see that there is no way you can access the vector through the result of the method so you're free to access them through the original vector.
Let me answer my own question, since what I was really missing were lifetimes. This code compiles:
fn maybe_use<'a, 'b>(v1: &'a mut Vec<i64>, v2: &'b mut Vec<i64>) -> &'a mut Vec<i64> {
v1
}
fn main() {
let mut nums1: Vec<i64> = vec![1,2,3,4];
let mut nums2: Vec<i64> = vec![1,2,3,4];
let ret = maybe_use(&mut nums1, &mut nums2);
println!("{}", nums2.len());
}
Because the return type of maybe_use makes it clear the reference comes from the first argument. If we change v2 to use 'a lifetime, main stops compiling because both vectors passed to maybe_use are considered borrowed. If we omit the lifetime altogether, compiler emits this error:
this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from v1 or v2
So what surprised me initially (how does the compiler know split_at_mut returns pointers to the vector?) boils down to the references having the same lifetime.