Consider the following (dumb) program:
fn main() {
let mut array = &mut [1u8, 2u8, 3u8];
for &mut value in array {
}
}
It compiles and runs okay (though warns about unused variables/unnecessary mutability, as expected). But what does &mut do in the for statement?
It doesn't seem to give you a mutable reference into the array, since trying to assign value = 0; results in the error:
error[E0384]: re-assignment of immutable variable `value`
Is &mut here a no-op then?
So there's a few different things that's going on here. First, here's the answer:
fn main() {
let mut array = [1u8, 2u8, 3u8];
for value in &mut array {
*value = 0;
}
}
So. Where did you go wrong? Let's look at what value is, like this:
for &mut value in array {
let () = value;
}
This gives this error:
= note: expected type `u8`
= note: found type `()`
So here, value is a u8. But why? Well, let's try this one:
for value in array {
let () = value;
}
This gives:
= note: expected type `&mut u8`
= note: found type `()`
So, value here is an &mut u8, a reference into the array. So by saying for &mut value, we're saying "hey, this is going to be a mutable pointer to a u8. We'd like value to be the u8 value that's pointed at. This is because &mut value is a pattern, which binds against a &mut T and binds value to the T.
So, we remove the &mut, since we don't want a copy of the value, we want to use it to modify what's pointed to. So that looks like this:
fn main() {
let mut array = &mut [1u8, 2u8, 3u8];
for value in array {
*value = 0;
}
}
This... compiles! Are we done? Well, let's try to print out array, just to be sure:
fn main() {
let mut array = &mut [1u8, 2u8, 3u8];
for value in array {
*value = 0;
}
println!("{:?}", array);
}
This fails to compile:
error[E0382]: use of moved value: `array`
--> <anon>:7:22
|
3 | for value in array {
| ----- value moved here
We've destroyed array by iterating. Why's that? Well, when you loop over an array like this, you're saying you want to loop by owner. But that's not actually what we want; we want to loop by mutable reference.
Arrays have a method to help with this:
fn main() {
let mut array = &mut [1u8, 2u8, 3u8];
for value in array.iter_mut() {
*value = 0;
}
println!("{:?}", array);
}
iter_mut will iterate by &mut T rather than by T. So this works!
One last thing, though:
let mut array = &mut [1u8, 2u8, 3u8];
This says that array is a &mut [u8; 3], that is, a mutable reference to an array, not an array itself. This probably isn't what you actually want, and it's not neccesary with our code. So we can remove the &mut bit:
let mut array = [1u8, 2u8, 3u8];
And now you're at our first code sample.
Hope this helps!
Is &mut here a no-op then?
No, it's part of a pattern. Print the type of value:
fn main() {
let mut array = &mut [1u8, 2u8, 3u8];
for &mut value in array {
let () = value;
// expected type `u8`
}
for value in array {
let () = value;
// expected type `&mut u8`
}
}
Related
This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 1 year ago.
I have read those two similar questions here and here. But still can't figure out how to achieve what I want: initializing a String field in a struct with a String that belongs to a HashMap in the same structure. Here is my initial code:
use std::collections::HashMap;
struct S<'a> {
a: HashMap<String,Vec<String>>,
b: &'a String,
}
impl <'a> S<'a> {
fn new() -> S<'a> {
let mut v:Vec<String> = Vec::new();
v.push("a".to_string());
v.push("b".to_string());
let mut h = HashMap::new();
h.insert("toto".to_string(),v);
let b = &h.get("toto").unwrap()[0];
S {
a: h, // error[E0505]: cannot move out of `h` because it is borrowed
b: b // error[E0515]: cannot return value referencing local variable `h`
}
}
}
fn main() {
let mut s = S::new();
println!("s.b = {}", s.b);
}
I would like b to be initilaized with a String value that belong to the HashMap a. Since the above did not work, I tried to initialize b with an empty String, and immediatly change b's value after creating s:
use std::collections::HashMap;
struct S {
a: HashMap<String,Vec<String>>,
b: String,
}
impl S {
fn new() -> S {
let mut v:Vec<String> = Vec::new();
v.push("a".to_string());
v.push("b".to_string());
let mut h = HashMap::new();
h.insert("toto".to_string(),v);
let b = "".to_string();
S {
a: h,
b: b
}
}
}
fn main() {
let mut s = S::new();
s.b = s.a.get("toto").unwrap()[0]; //error[E0507]: cannot move out of index of `Vec<String>`
println!("s.b = {}", s.b);
}
Since I cannot copy the value into s.b:
error[E0507]: cannot move out of index of `Vec<String>`
--> src/main.rs:24:15
|
24 | s.b = s.a.get("toto").unwrap()[0]; //error[E0507]: cannot move out of index of `Vec<String>`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
I thought that a reference could work:
use std::collections::HashMap;
struct S<'a> {
a: HashMap<String,Vec<String>>,
b: &'a String,
}
impl <'a> S<'a> {
fn new() -> S<'a> {
let mut v:Vec<String> = Vec::new();
v.push("a".to_string());
v.push("b".to_string());
let mut h = HashMap::new();
h.insert("toto".to_string(),v);
let b = &"".to_string();
S {
a: h,
b: b // error[E0515]: cannot return value referencing temporary value
}
}
}
fn main() {
let mut s = S::new();
s.b = &s.a.get("toto").unwrap()[0];
println!("s.b = {}", s.b);
}
But of course, I run into the same issue of referencing a value that does not have a lifetime longer than the function. No clue how/if I can tell the compiler that my empty String should live as long as S.
There might be an obvious solution, but if so, I can't see it :-(
There might be an obvious solution, but if so, I can't see it :-(
If your goal is to share the value between the map and the field, then no. Rust does not like self-referential data structures.
And a string is affine, you can't have both the structure and the nested map own the "same" string unless:
you relax the ownership using Rc or Arc
you clone() the string and both just have a copy of the same thing
There are also crates for string caching / interning, but I've no idea how convenient & efficient they are.
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 am reading the official Rust Book and looking at listing 4-8 in Section 4.3.
The code looks like this:
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
}
This line:
let word = first_word(&s);
seems to borrow an immutable reference to s. (This is where I guess I'm wrong; I just don't know why.)
In the next line, we mutate s by calling the clear() method.
I was expecting the compiler to throw:
cannot borrow `s` as mutable because it is also borrowed as immutable
Why does this compile?
The string s is immutably borrowed for the duration of the call to first_word. As soon as the control is returned to main after first_word, the string is not considered borrowed anymore and can be mutated as you have observed.
If first_word were to return a &String which you extended the lifetime of by assigning it to a variable, then you would see the error you expected. E.g.
fn first_word(s: &String) -> &String {
&s
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
}
cannot borrow s as mutable because it is also borrowed as immutable
https://rust.godbolt.org/z/cMVdVf
In that case, adding an extra scope would fix that:
fn main() {
let mut s = String::from("hello world");
{
let word = first_word(&s);
}
s.clear();
}
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 thought that it should be something like this, but I cannot iterate over an borrowed array.
fn print_me<'a, I>(iter: &'a I) where I: Iterator<Item = i32> {
for i in *iter {
println!("{}", i);
}
}
fn main() {
let numbers = vec![1, 2, 3];
//let numbers = &numbers;
print_me(numbers.iter());
}
But Rust complains:
<anon>:15:12: 15:26 error: mismatched types:
expected `&_`,
found `core::slice::Iter<'_, _>`
(expected &-ptr,
found struct `core::slice::Iter`) [E0308]
<anon>:15 print_me(numbers.iter());
^~~~~~~~~~~~~~
An iterator is a regular object; you work with an iterator directly, not typically through references—and certainly not typically through immutable references, for taking the next value of an iterator takes &mut self. And a reference to an iterator is quite different from an iterator over references.
Fixing this part, you then have this:
fn print_me<I: Iterator<Item = i32>>(iter: I) {
for i in iter {
println!("{}", i);
}
}
This doesn’t fix everything, however, because [T].iter() produces a type implementing Iterator<Item = &T>—it iterates over references to each item. The most common fix for that is cloning each value with the handy .cloned() method, which is equivalent to .map(|x| x.clone()).
Here, then, is the rest of what you end up with:
fn main() {
let numbers = vec![1, 2, 3];
print_me(numbers.iter().cloned());
}