Is there a more efficient way of getting an owned value from a HashMap than this line?
let output_items = output_tables.get(TABLE_NAME_TLIST).unwrap().to_owned();
This screenshot expands the types:
relates to Rust - Change a reference to own value without clone
If you want to take ownership of the value, HashMap::remove() will return an Option<T> rather than the Option<&T> returned by HashMap::get(). See this playground:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, "a");
assert_eq!(map.remove(&1), Some("a"));
assert_eq!(map.remove(&1), None);
}
If you want the value to remain in the HashMap but also own the value elsewhere, you can wrap it in an Rc<T> to share ownership. If the object needs to be mutable, you can wrap it in an Rc<RefCell<T>>.
Related
Is there a more efficient way of getting an owned value from a HashMap than this line?
let output_items = output_tables.get(TABLE_NAME_TLIST).unwrap().to_owned();
This screenshot expands the types:
relates to Rust - Change a reference to own value without clone
If you want to take ownership of the value, HashMap::remove() will return an Option<T> rather than the Option<&T> returned by HashMap::get(). See this playground:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, "a");
assert_eq!(map.remove(&1), Some("a"));
assert_eq!(map.remove(&1), None);
}
If you want the value to remain in the HashMap but also own the value elsewhere, you can wrap it in an Rc<T> to share ownership. If the object needs to be mutable, you can wrap it in an Rc<RefCell<T>>.
I have a function that takes in a Vec<String> value. I want to use this function on values contained inside my_ref, so I need to extract a Vec<String> out of a Rc<RefCell<Vec<String>>>.
I thought I could do this by dereferencing a borrow of my my_ref, just like I would for a Rc<RefCell<f32>>> or Rc<RefCell<i32>>> value:
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let my_ref = Rc::from(RefCell::from(vec![
"Hello 1".to_string(),
"Hello 2".to_string(),
]));
let my_strings: Vec<String> = *my_ref.borrow();
let count = count_strings(my_strings);
}
fn count_strings(strings: Vec<String>) -> usize {
strings.len()
}
But doing so results in a dereferencing error:
error[E0507]: cannot move out of dereference of `Ref<'_, Vec<String>>`
cannot move out of dereference of `Ref<'_, Vec<String>>`
move occurs because value has type `Vec<String>`, which does not implement the `Copy` trait
So then, how do I properly extract a Vec<String> from a Rc<RefCell<Vec<String>>>?
RefCell::borrow returns a reference, not an owned value, that's why you having such an error. I can name two different solution for that problem.
Promoting Rc to exclusively-owned type
Rc::try_unwrap is able to check, whether there's other references to the data. If it's the only one, it can be safely converted to the inner type. Then, an owned RefCell can be converted into its inner via into_inner function.
let my_ref = Rc::from(RefCell::new(vec![..]));
let inner: Vec<_> = Rc::try_unwrap(my_ref).expect("I hereby claim that my_ref is exclusively owned").into_inner();
Replacing inner value
If for some reason you want to grab inner value that is already referenced, you may consider replacing it. Note, that you need to create a appropriate value for the type (i.e. with trait Default). Here's the example:
let my_ref = Rc::from(RefCell::new(vec![..]));
let inner: Vec<_> = my_ref.borrow_mut().take();
// or
let inner: Vec<_> = my_ref.borrow_mut().replace(vec![]);
Checkout the Rust code below. It compiles
fn main() {
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
Here I am declaring vec0 as immutable but fill_vec takes in a mutable vector. Depending on the function signature it seems Rust is changing the nature of the argument being passed.
My question is, this obviously seems like a "shot yourself in the foot" instant. Why does Rust allow this? Or, is this actually safe and I am missing something?
There are different things at play here that can all explain why this behavior make sense:
First of, mut doesn't really mean "mutable". There are such things as interior mutability, Cells, Mutexes, etc., which allow you to modify state without needing a single mut. Rather, mut means that you can get mutually exclusive references.
Second, mutability is a property of a binding. vec0 in main and vec in fill_vec are different bindings, so they can have different mutability.
See also:
What does 'let x = x' do in Rust?
Finally ownership: fill_vec takes full ownership of its parameter, which effectively doesn't exist anymore in main. Why should the function not be allowed to do whatever it wants with its owned parameters? Had the function taken the parameter as a mutable reference, you would have needed to declare the original binding as mut:
fn main() {
let mut vec0 = Vec::new();
// ^^^ now _needs_ a mutable binding
fill_vec(&mut vec0);
// ^^^^ needs an explicit `&mut` reference
}
fn fill_vec(vec: &mut Vec<i32>) {
// ^^^^ borrows rather than take ownership
// …
}
You're making the argument vec of fill_vec mutable. You are still passing the vec by value.
If you wanted a mutable reference you would have vec: &mut Vec<i32>.
I'm learning rust and have a simple program, shown below. Playground link.
#[derive(Debug)]
pub struct Foo {
bar: String,
}
pub fn gather_foos<'a>(data: &'a Vec<Vec<&'a Foo>>) -> Vec<Vec<&'a Foo>> {
let mut ret: Vec<Vec<&Foo>> = Vec::new();
for i in 0..data.len() {
if meets_requirements(&data[i]) {
ret.push(data[i].to_vec());
}
}
return ret
}
fn meets_requirements<'a>(_data: &'a Vec<&'a Foo>) -> bool {
true
}
fn main() {
let foo = Foo{
bar: String::from("bar"),
};
let v1 = vec![&foo, &foo, &foo];
let v2 = vec![&foo, &foo];
let data = vec![v1, v2];
println!("{:?}", gather_foos(&data));
}
The program simply loops through an array of arrays of a struct, checks if the array of structs meets some requirement and returns an array of arrays that meets said requirement.
I'm sure there's a more efficient way of doing this without the need to call to_vec(), which I had to implement in order to avoid the error cannot move out of borrowed content, but I'm not sure what that solution is.
I'm learning about Box<T> now and think it might provide a solution to my needs? Thanks for any help!!
The error is showing up because you're trying to move ownership of one of the vectors in the input vector to the output vector, which is not allowed since you've borrowed the input vector immutably. to_vec() creates a copy, which is why it works when you use it.
The solution depends on what you're trying to do. If you don't need the original input (you only want the matched ones), you can simply pass the input by value rather than by reference, which will allow you to consume the vector and move items to the output. Here's an example of this.
If you do need the original input, but you don't want to copy the vectors with to_vec(), you may want to use references in the output, as demonstrated by this example. Note that the function now returns a vector of references to vectors, rather than a vector of owned vectors.
For other cases, there are other options. If you need the data to be owned by multiple items for some reason, you could try Rc<T> or Arc<T> for reference-counted smart pointers, which can be cloned to provide immutable access to the same data by multiple owners.
I set myself a little task to acquire some basic Rust knowledge. The task was:
Read some key-value pairs from stdin and put them into a hashmap.
This, however, turned out to be a trickier challenge than expected. Mainly due to the understanding of lifetimes. The following code is what I currently have after a few experiments, but the compiler just doesn't stop yelling at me.
use std::io;
use std::collections::HashMap;
fn main() {
let mut input = io::stdin();
let mut lock = input.lock();
let mut lines_iter = lock.lines();
let mut map = HashMap::new();
for line in lines_iter {
let text = line.ok().unwrap();
let kv_pair: Vec<&str> = text.words().take(2).collect();
map.insert(kv_pair[0], kv_pair[1]);
}
println!("{}", map.len());
}
The compiler basically says:
`text` does not live long enough
As far as I understand, this is because the lifetime of 'text' is limited to the scope of the loop.
The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to a dangling pointer since 'text' will be destroyed after each iteration. (Please tell me if I'm wrong)
The big question is: How to solve this issue?
My intuition says:
Make an "owned copy" of the key value pair and "expand" it's lifetime to the outer scope .... but I have no idea how to achieve this.
The lifetime of 'text' is limited to the scope of the loop. The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to an dangling pointer since 'text' will be destroyed after each iteration.
Sounds right to me.
Make an "owned copy" of the key value pair.
An owned &str is a String:
map.insert(kv_pair[0].to_string(), kv_pair[1].to_string());
Edit
The original code is below, but I've updated the answer above to be more idiomatic
map.insert(String::from_str(kv_pair[0]), String::from_str(kv_pair[1]));
In Rust 1.1 the function words was marked as deprecated. Now you should use split_whitespace.
Here is an alternative solution which is a bit more functional and idiomatic (works with 1.3).
use std::io::{self, BufRead};
use std::collections::HashMap;
fn main() {
let stdin = io::stdin();
// iterate over all lines, "change" the lines and collect into `HashMap`
let map: HashMap<_, _> = stdin.lock().lines().filter_map(|line_res| {
// convert `Result` to `Option` and map the `Some`-value to a pair of
// `String`s
line_res.ok().map(|line| {
let kv: Vec<_> = line.split_whitespace().take(2).collect();
(kv[0].to_owned(), kv[1].to_owned())
})
}).collect();
println!("{}", map.len());
}