Get HashMap entry or add it if there isn't one - rust

I want to do something like this:
fn some_fn() {
let mut my_map = HashMap::from([
(1, "1.0".to_string()),
(2, "2.0".to_string()),
]);
let key = 3;
let res = match my_map.get(&key) {
Some(child) => child,
None => {
let value = "3.0".to_string();
my_map.insert(key, value);
&value // HERE IT FAILS
}
};
println!("{}", res);
}
but it compiles with errors:
error[E0597]: `value` does not live long enough
--> src/lib.rs:16:13
|
16 | &value // HERE IT FAILS
| ^^^^^^
| |
| borrowed value does not live long enough
| borrow later used here
17 | }
| - `value` dropped here while still borrowed
error[E0382]: borrow of moved value: `value`
--> src/lib.rs:16:13
|
14 | let value = "3.0".to_string();
| ----- move occurs because `value` has type `String`, which does not implement the `Copy` trait
15 | my_map.insert(key, value);
| ----- value moved here
16 | &value // HERE IT FAILS
| ^^^^^^ value borrowed here after move
Playground
How can I fix it elegantly? Make a copy of string seems to me non-optimal.

The issue here is that you're inserting the value string then trying to return a reference to it, despite it already being moved into the HashMap. What you really want is to insert the value then get a reference to the inserted value, which could look something like this:
let res = match my_map.get(&key) {
Some(child) => child,
None => {
let value = "3.0".to_string();
my_map.insert(key, value);
my_map.get(&key).unwrap() // unwrap is guaranteed to work
}
};
BUT DON'T DO THIS. It's ugly and slow, as it has to look up the key in the map twice. Rust has a convenient Entry type that lets you get an entry of a HashMap then perform operations on that:
// or_insert returns the value if it exists, otherwise it inserts a default and returns it
let res = my_map.entry(key).or_insert("3.0".to_string());
Alternatively, if the operation to generate the default value is expensive, you can use or_insert_with to pass a closure:
// this won't call "3.0".to_string() unless it needs to
let res = my_map.entry(key).or_insert_with(|| "3.0".to_string());

Related

Rust loop - borrowed value does not live long enough

In a rebroadcaster (redis to zmq) implementation i am unable to figure out how to resolve the lifetime issue of temporary values (derived from consumed message from pubsub in a loop).
I pretty much understand why, but i am so far failing to produce Vec<&String> that i need to pass into zmq publisher. I tried with different types, cloning etc. but i'm just getting same or similar errors related to ownership and lifetime.
The problematic code:
async fn ps_rebroadcaster() -> Result<(), ApiError>{
let mut inproc_publisher: async_zmq::Publish<std::vec::IntoIter<&std::string::String>, &std::string::String> = async_zmq::publish("ipc://pricing")?.bind()?;
let con_str = &APP_CONFIG.REDIS_URL;
let conn = create_client(con_str.to_string())
.await
.expect("Can't connect to redis");
let mut pubsub = conn.get_async_connection().await?.into_pubsub();
match pubsub.psubscribe("T1050_*").await {
Ok(_) => {}
Err(_) => {}
};
let mut msg_stream = pubsub.into_on_message();
loop {
let result = msg_stream.next().await;
match result {
Some(message) => {
let channel_name = message.get_channel_name().to_string();
let payload_value: redis::Value = message.get_payload().expect("Can't get payload of message");
let payload_string: String = FromRedisValue::from_redis_value(&payload_value).expect("Can't convert from Redis value");
let item: Vec<&String> = vec![&channel_name , &payload_string ];
// problem here ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
inproc_publisher.send(item.into()).await?;
}
None => {
println!("None received");
continue;
}
}
}
}
And the error:
error[E0597]: `channel_name` does not live long enough
--> src/server.rs:248:47
|
248 | let item: Vec<&String> = vec![&channel_name , &payload_string ];
| ^^^^^^^^^^^^^ borrowed value does not live long enough
249 |
250 | inproc_publisher.send(item.into()).await?;
| ---------------------------------- borrow later used here
251 | }
| - `channel_name` dropped here while still borrowed
error[E0597]: `payload_string` does not live long enough
--> src/server.rs:248:63
|
248 | let item: Vec<&String> = vec![&channel_name , &payload_string ];
| ^^^^^^^^^^^^^^^ borrowed value does not live long enough
249 |
250 | inproc_publisher.send(item.into()).await?;
| ---------------------------------- borrow later used here
251 | }
| - `payload_string` dropped here while still borrowed
This fails because the sink wants an Into<Message> but doesn't apparently perform the conversion immediately, so the reference needs to continue to exist for some unspecified amount of time, and the values they refer to are destroyed too soon. Basically, you need to give the sink an owned value instead of a borrowed one.
You can do this by converting the Strings into Message values before giving them to the Publish sink, or by giving it an owned value that can be converted to Message. There isn't a conversion from String to Message, but there is one from Vec<u8>, and you can convert a String to a Vec<u8> without any copying by using the String::into_bytes() method.

How to get value from HashMap or create new one in Rust

I am wondering what would be an elegant way to either get an existing value from a HashMap or create a new one and assign it to a variable.
I was thinking about something like this ...
struct Test {
name: String,
valid: bool
}
let tests: HashMap<String, Test> = HashMap::new();
let key = String::from("a");
let test = match tests.get(&key) {
Some(t) => t,
None => {
let t = Test {
name: key,
valid: false,
};
&t
}
};
This results to the expected error
error[E0597]: `t` does not live long enough
--> src/main.rs:33:12
|
33 | &t
| ^^
| |
| borrowed value does not live long enough
| borrow later used here
34 | }
| - `t` dropped here while still borrowed
A kind of workaround is to do this ...
let t;
let test = match tests.get(&key) {
Some(node) => node,
None => {
t = Test {
name: key,
valid: false,
};
&t
}
};
It seems to me a little bit unhandy just to introduce the extra variable t in advance to be able to shift out the reference of the allocated structure for the case the instance of the structure is not found in the HashMap.
Is there a better way to do this?

How to return reference to value inside option while conditionaly modifying outer option

I have token inside Option. Before each operation, I need to check if token is valid or not. it might be expired. if it is expired, I want to set outer option to None and return Error so next time I will just get "No Token" Error.
#[derive(Debug)]
struct Token {
exp : u64
//other cool stuff
}
fn is_valid<'a>(v : &'a mut Option<Token> ) -> Result<&'a mut Token, String> {
match v {
None => Err("No Token".into()),
Some(v_) => {
if v_.exp > 10 {
*v = None;
Err("Token Expired".into())
}
else {
Ok(v_)
// Err("erlgnerlg".into()) //commenting this out and comenting upper line also work.
}
}
}
}
fn main() {
let mut v = Some(Token { exp : 69 });
//expecting "Token Expired" error
println!("{:?} ", is_valid(&mut v));
//expecting "No Token" error
println!("{:?} ", is_valid(&mut v));
}
but it fails to compile.
error[E0506]: cannot assign to `*v` because it is borrowed
--> src/main.rs:13:17
|
8 | fn is_valid<'a>(v : &'a mut Option<Token> ) -> Result<&'a mut Token, String> {
| -- lifetime `'a` defined here
...
11 | Some(v_) => {
| -- borrow of `*v` occurs here
12 | if v_.exp > 10 {
13 | *v = None;
| ^^^^^^^^^ assignment to borrowed `*v` occurs here
...
17 | Ok(v_)
| ------ returning this value requires that `v.0` is borrowed for `'a
if I dont assing None to outer option, my code compile. also in else block, if I return Err instead of Ok, it compiles fine. why does this happen and how can I solve this problem ?
Firstly, we can get another compiler error if we replace *v = None with let _ = v.take() (which does exactly the same thing). This yields the classic "second mutable borrow occurs here":
10 | Some(v_) => {
| -- first mutable borrow occurs here
11 | if v_.exp > 10 {
12 | let _ = v.take();
| ^ second mutable borrow occurs here
...
16 | Ok(v_)
| ------ returning this value requires that `v.0` is borrowed for `'a`
And it makes sense: First, we destructured the borrowed Option to take a look inside, but then we want to change it while still having the reference to the inside of the Option lying around.
We can however capture all of your conditional logic with one match statement, using match guards:
match v {
None => Err("No Token".into()),
Some(v_) if v_.exp > 10 => {
dbg!(v_.exp); // use v_ in _some_ way
let _ = v.take(); // same as *v = None
Err("Token Expired".into())
}
Some(v_) => Ok(v_)
}
I am not entirely sure, why the compiler gets stuck on your version, but here is what I think happened: Your match arm returns a reference to the inner Token value in some way, thus the compiler deduces that this reference must be kept alive for the entire scope of the arm.
With the match guards, it can deduce that we never touch the contained value again and shorten the lifetime of the inner borrow (the one we implicitly did when matching against Some(v_) if ...). Note that the lifetime of this borrow ends precisely when we drop the previously contained value, as we can still use it in the line dbg!(v_.exp).
Once this inner borrow is over, the outer borrow v is the only reference left and it is fine to touch the outer value in the match arm.
The match mutably borrows v (for v_), so you're not allowed to use it anymore. You need to use v_ instead - at least until it's dropped.
I also changed the names to v_opt and v for readability:
match v_opt {
None => Err("No Token".into()),
Some(v) if v.exp > 10 => {
drop(v);
*v_opt = None;
Err("Token Expired".into())
}
Some(v_) => Ok(v_)
}

How can I iterate on a Bevy Query and keep a reference to the iterated value so that I can use it later?

I have a borrow in the empty variable and I want to extend its life. In the commented code-block, I attempt to address it, but the reference is no longer available. I have to loop through the loop again to find the match in order to act on it.
How can I loop through a query looking for a best-match and then act on it once I know it's the best match, without having to loop through to find it again?
use bevy::prelude::*;
struct Person;
struct Name(String);
fn main() {
App::build()
.add_default_plugins()
.add_startup_system(startup.system())
.add_system(boot.system())
.run();
}
fn boot(mut query: Query<(&Person, &mut Name)>) {
let mut temp_str = String::new();
let mut empty: Option<&mut Name> = None;
for (_p, mut n_val) in &mut query.iter() {
if n_val.0.to_lowercase() > temp_str.to_lowercase() {
temp_str = n_val.0.clone();
empty = Some(&mut n_val);
}
}
println!("{}", temp_str);
if let Some(n) = empty {
// ...
}
// for (_p, mut n_val) in &mut query.iter() {
// if n_val.0 == temp_str {
// n_val.0 = "a".to_string();
// }
// }
}
fn startup(mut commands: Commands) {
commands
.spawn((Person, Name("Gene".to_string())))
.spawn((Person, Name("Candace".to_string())))
.spawn((Person, Name("Zany".to_string())))
.spawn((Person, Name("Sarah".to_string())))
.spawn((Person, Name("Carl".to_string())))
.spawn((Person, Name("Robert".to_string())));
}
Cargo.toml:
[package]
name = "sample"
version = "0.1.0"
authors = [""]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy = "0.1.3"
Specific error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:17:33
|
17 | for (_p, mut n_val) in &mut query.iter() {
| ^^^^^^^^^^^-
| | |
| | temporary value is freed at the end of this statement
| creates a temporary which is freed while still in use
...
24 | if let Some(n) = empty {
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0597]: `n_val` does not live long enough
--> src/main.rs:20:26
|
20 | empty = Some(&mut n_val);
| ^^^^^^^^^^ borrowed value does not live long enough
21 | }
22 | }
| - `n_val` dropped here while still borrowed
23 | println!("{}", temp_str);
24 | if let Some(n) = empty {
| ----- borrow later used here
You cannot extend the lifetime of a reference, but that is not your issue here, the error says temporary value dropped while borrowed, so you must extend the lifetime of your temporary.
If you wonder what temporary, the compiler also points to that (literally) in the error message: query.iter(). This is a function call, and the returned value is not bound to anything, so the compiler creates a temporary value for that. Then you iterate using a reference to that temporary. When the for loop ends, the temporary is dropped and any reference lifetime to it expires.
The solution is to bind the temporary to a local variable. This way you extend the lifetime of the object to the scope of the variable:
let mut iter = query.iter();
for (_p, n_val) in &mut iter {
if n_val.0.to_lowercase() > temp_str.to_lowercase() {
temp_str = n_val.0.clone();
empty = Some(n_val);
}
}
PS: I find quite bizarre the pattern of iterating over &mut iter. I would expect the return of iter() to implement Iterator or IntoIterator directly, but it looks like this is not the case.

Cannot borrow as mutable in a loop when calling a closure that borrows as immutable?

Here is the code:
fn test(){
let mut numbers = vec![2];
let f = || {
for _ in numbers.iter(){
}
false
};
while false {
let res = f();
if res {
numbers.push(10);
}
}
}
The error is:
|
15 | let f = || {
| -- immutable borrow occurs here
16 | for _ in numbers.iter(){
| ------- first borrow occurs due to use of `numbers` in closure
...
22 | let res = f();
| - immutable borrow later used here
23 | if res {
24 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
But if I change the while keyword to if, it can be compiled. How to fix this? I want to call the anonymous function in a loop.
We can simplify your example even more by replacing the closure by a simple immutable reference
let mut numbers = vec![2];
let r = &numbers;
while false {
println!("{:?}", r);
numbers.push(10);
}
Here we get this error:
error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
--> src/lib.rs:7:5
|
3 | let r = &numbers;
| -------- immutable borrow occurs here
...
6 | println!("{:?}", r); // use reference
| - immutable borrow later used here
7 | numbers.push(10);
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
And like in your example, replacing the while with if makes the error go away. Why?
You probably know about the important Rust rule: Aliasing nand mutability. It states that, at any given time, a value can either be borrowed immutably arbitrarily many times or mutably exactly once.
The statement numbers.push(10) borrows numbers mutably temporarily (just for the statement). But we also have r which is an immutable reference. In order for numbers.push(10) to work, the compiler has to make sure that no other borrow exists at that time. But there exists the reference r! This reference cannot exist at the same time as numbers.push(10) exists.
Let's see for the if case first:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
if false { // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
}
While the lexical scope means the variable r is only dropped at the end of the function, due to non-lexical lifetimes, the compiler can see that the last use of r is in the println line. Then the compiler can mark r as "dead" after this line. And this in turn means, that there is no other borrow in the line numbers.push(10) and everything works out fine.
And for the loop case? Let's imagine the loop iterating three times:
let mut numbers = vec![2];
let r = &numbers; // <------+ (creation of r)
// |
// First iteration // |
println!("{:?}", r); // |
numbers.push(10); // | <== oh oh!
// |
// Second iteration // |
println!("{:?}", r); // |
numbers.push(10); // |
// |
// Third iteration // |
println!("{:?}", r); // <------+ (last use of r)
numbers.push(10);
As can be seen here, the time in which r is active overlaps numbers.push(10) (except the last one). And as a result, the compiler will produce an error because this code violates the central Rust rule.
And the explanation is the same for your closure case: the closure borrows numbers immutably and f(); uses that closure. In the loop case, the compiler is not able to shrink the "alive time" of the closure enough to make sure it doesn't overlap the mutable borrow for push.
How to fix?
Well, you could pass numbers into the closure each time:
let mut numbers = vec![2];
let f = |numbers: &[i32]| {
for _ in numbers.iter(){
}
false
};
while false {
let res = f(&numbers);
if res {
numbers.push(10);
}
}
This works because now, numbers is borrowed immutably also just temporarily for the f(&numbers); statement.
You can also use a RefCell as the other answer suggested, but that should be a last resort.
It's not exactly sure what you're trying to accomplish, but one way to solve this, without changing your code too drastically, would be to use std::cell::RefCell (described in the std and in the book):
use std::cell::RefCell;
fn test(){
let numbers = RefCell::new(vec![2]);
let f = || {
for _ in numbers.borrow().iter(){
}
false
};
while false {
let res = f();
if res {
numbers.borrow_mut().push(10);
}
}
}
Here's a little bit tweaked demo, which actually does something:
use std::cell::RefCell;
fn main() {
test();
}
fn test() {
let numbers = RefCell::new(vec![0]);
let f = || {
for n in numbers.borrow().iter() {
println!("In closure: {}", n);
}
println!();
true
};
let mut i = 1;
while i <= 3 {
let res = f();
if res {
numbers.borrow_mut().push(i);
}
i += 1;
}
println!("End of test(): {:?}", numbers.borrow());
}
Output:
In closure: 0
In closure: 0
In closure: 1
In closure: 0
In closure: 1
In closure: 2
End of test(): [0, 1, 2, 3]
Rust Playground demo

Resources