I'm trying to return a Vector from a function. This happens in a loop and I need the values to exist outside of the loop. Since I perform the return multiple times and I only need the unique values, I thought I use a HashSet for this, in which I insert and then try to get a reference to the value in the next line.
I need a reference to the value in multiple other datastructures and don't want to duplicate the actual values. The values don't need to be mutable.
What I tried
use std::collections::HashSet;
fn main() {
let mut vec: Vec<&str> = Vec::new();
let mut books = HashSet::new();
for i in 0..5 {
// This could be a function call, which returns a vector of objects, which should all be
// stored centrally and uniquely in a HashSet
books.insert("A Dance With Dragons".to_string());
let mut reference: &str = books.get("A Dance With Dragons").unwrap();
// This would be done for multiple "refering" datastructures
vec.push(reference);
}
}
What I was expecting
Getting a pointer to the String in the HashSet for future use.
What actually happens
error[E0502]: cannot borrow `books` as mutable because it is also borrowed as immutable
--> src/main.rs:10:9
|
10 | books.insert("A Dance With Dragons".to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
11 |
12 | let mut reference: &str = books.get("A Dance With Dragons").unwrap();
| --------------------------------- immutable borrow occurs here
13 | // This would be done for multiple "refering" datastructures
14 | vec.push(reference);
| ------------------- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
warning: `set_test` (bin "set_test") generated 2 warnings
error: could not compile `set_test` due to previous error; 2 warnings emitted
I think I'm missing a very obvious solution to this...
Thanks in advance for helping.
CodePudding user response:
You can't do this
use std::collections::HashSet;
fn main() {
let mut v: Vec<&str> = Vec::new();
let mut books = HashSet::new();
for i in 0..5 {
// this needs to borrow mutably
books.insert("A Dance With Dragons".to_string());
// this reference has to live as long as v
// so on the second iteration books is still borrowed
// which means you can't borrow it mutably any more.
let reference: &str = books.get("A Dance With Dragons").unwrap();
v.push(reference);
}
// v only goes out of scope here
}
You might find success in separating mutation and referencing like this:
fn main() {
let mut books = HashSet::new();
for i in 0..5 {
books.insert("A Dance With Dragons".to_string());
}
let v: Vec<&str> = books.iter().collect();
}
Or by using a Rc
like pigeonhgands suggests.
fn main() {
let mut v: Vec<Rc<str>> = Vec::new();
let mut books = HashSet::new();
for i in 0..5 {
books.insert(Rc::new("A Dance With Dragons".to_string()));
// note: this clone is cheap cause it only clones the `Rc` not the whole String.
let reference = books.get("A Dance With Dragons").unwrap().clone();
v.push(reference);
}
}
CodePudding user response:
The issue is that's the value in "book" could be removed and you try to save reference in a vector. It is a risk of null pointer.
You need to build book in imutable way, like this
use std::collections::HashSet;
fn main() {
let mut vec: Vec<&str> = Vec::new();
let books: HashSet<String> = vec!(
"A Dance With Dragons".to_string(),
"A Dance With Dragons".to_string()).into_iter().collect();
let reference: &str = books.get("A Dance With Dragons").unwrap();
vec.push(reference);
}