How can I borrow the item in an Option or create a new item when it's None? - rust

When I have an Option and want a reference to what's inside or create something if it's a None I get an error.
Example Code:
fn main() {
let my_opt: Option<String> = None;
let ref_to_thing = match my_opt {
Some(ref t) => t,
None => &"new thing created".to_owned(),
};
println!("{:?}", ref_to_thing);
}
playground
Error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:6:18
|
6 | None => &"new thing created".to_owned(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| | |
| | temporary value dropped here while still borrowed
| temporary value does not live long enough
...
10 | }
| - temporary value needs to live until here
Basically the created value doesn't live long enough. What is the best way to get a reference to the value in a Some or create a value if it's a None and use the reference?

You can also just write:
None => "new thing created"
With this adjustment your initial variant of the code will compile without the need of an extra variable binding.
An alternative could also be:
let ref_to_thing = my_opt.unwrap_or("new thing created".to_string());

The only way I've found is to create a "dummy variable" to hold the created item and give it a lifetime:
fn main() {
let my_opt: Option<String> = None;
let value_holder;
let ref_to_thing = match my_opt {
Some(ref t) => t,
None => {
value_holder = "new thing created".to_owned();
&value_holder
}
};
println!("{:?}", ref_to_thing);
}
playground

If you don't mind mutating your Option in place, you can use Option::method.get_or_insert_with:
fn main() {
let mut my_opt: Option<String> = None;
let ref_to_thing = my_opt.get_or_insert_with(|| "new thing created".to_owned());
println!("{:?}", ref_to_thing);
}

Related

Cryptic message when dealing with Rust Option type

I have a function like the following:
pub fn process_options<T>(in_refs:& mut Vec<T>,ivals:Vec<Option<T>>) -> i32 {
let mut nones = 0;
for int in 0..ivals.len() {
if let None = ivals[int] {
nones += 1;
} else {
in_refs.push(ivals[int].unwrap());
}
}
return nones;
}
What this function does is that it:
It pushes all the some(T) from one vector to another as T. And it also records the number of none values in the first vector. As written above the code fails to compile and gives me:
error[E0507]: cannot move out of index of `Vec<Option<T>>`
--> src/functions.rs:40:26
|
40 | in_refs.push(ivals[int].unwrap());
| ^^^^^^^^^^ -------- value moved due to this method call
| |
| help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
| move occurs because value has type `Option<T>`, which does not implement the `Copy` trait
|
note: this function takes ownership of the receiver `self`, which moves value
--> /Users/yunfeichen/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:772:25
|
772 | pub const fn unwrap(self) -> T {
Which I frankly do not understand can you explain this to me?
Edit:
This question has been solved:
It turns out I just need to use a different style of for loop, Not sure why this for loop works but it does:
pub fn process_options<T>(in_refs:& mut Vec<T>,ivals:Vec<Option<T>>) -> i32 {
let mut nones = 0;
for element in ivals{
if let None = element {
nones += 1;
} else {
in_refs.push(element.unwrap());
}
}
return nones;
}

Iterate through a whole file one character at a time

I'm new to Rust and I'm struggle with the concept of lifetimes. I want to make a struct that iterates through a file a character at a time, but I'm running into issues where I need lifetimes. I've tried to add them where I thought they should be but the compiler isn't happy. Here's my code:
struct Advancer<'a> {
line_iter: Lines<BufReader<File>>,
char_iter: Chars<'a>,
current: Option<char>,
peek: Option<char>,
}
impl<'a> Advancer<'a> {
pub fn new(file: BufReader<File>) -> Result<Self, Error> {
let mut line_iter = file.lines();
if let Some(Ok(line)) = line_iter.next() {
let char_iter = line.chars();
let mut advancer = Advancer {
line_iter,
char_iter,
current: None,
peek: None,
};
// Prime the pump. Populate peek so the next call to advance returns the first char
let _ = advancer.next();
Ok(advancer)
} else {
Err(anyhow!("Failed reading an empty file."))
}
}
pub fn next(&mut self) -> Option<char> {
self.current = self.peek;
if let Some(char) = self.char_iter.next() {
self.peek = Some(char);
} else {
if let Some(Ok(line)) = self.line_iter.next() {
self.char_iter = line.chars();
self.peek = Some('\n');
} else {
self.peek = None;
}
}
self.current
}
pub fn current(&self) -> Option<char> {
self.current
}
pub fn peek(&self) -> Option<char> {
self.peek
}
}
fn main() -> Result<(), Error> {
let file = File::open("input_file.txt")?;
let file_buf = BufReader::new(file);
let mut advancer = Advancer::new(file_buf)?;
while let Some(char) = advancer.next() {
print!("{}", char);
}
Ok(())
}
And here's what the compiler is telling me:
error[E0515]: cannot return value referencing local variable `line`
--> src/main.rs:37:13
|
25 | let char_iter = line.chars();
| ---- `line` is borrowed here
...
37 | Ok(advancer)
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0597]: `line` does not live long enough
--> src/main.rs:49:34
|
21 | impl<'a> Advancer<'a> {
| -- lifetime `'a` defined here
...
49 | self.char_iter = line.chars();
| -----------------^^^^--------
| | |
| | borrowed value does not live long enough
| assignment requires that `line` is borrowed for `'a`
50 | self.peek = Some('\n');
51 | } else {
| - `line` dropped here while still borrowed
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0515, E0597.
For more information about an error, try `rustc --explain E0515`.
error: could not compile `advancer`.
Some notes:
The Chars iterator borrows from the String it was created from. So you can't drop the String while the iterator is alive. But that's what happens in your new() method, the line variable owning the String disappears while the iterator referencing it is stored in the struct.
You could also try storing the current line in the struct, then it would live long enough, but that's not an option – a struct cannot hold a reference to itself.
Can you make a char iterator on a String that doesn't store a reference into the String? Yes, probably, for instance by storing the current position in the string as an integer – it shouldn't be the index of the char, because chars can be more than one byte long, so you'd need to deal with the underlying bytes yourself (using e.g. is_char_boundary() to take the next bunch of bytes starting from your current index that form a char).
Is there an easier way? Yes, if performance is not of highest importance, one solution is to make use of Vec's IntoIterator instance (which uses unsafe magic to create an object that hands out parts of itself) :
let char_iter = file_buf.lines().flat_map(|line_res| {
let line = line_res.unwrap_or(String::new());
line.chars().collect::<Vec<_>>()
});
Note that just returning line.chars() would have the same problem as the first point.
You might think that String should have a similar IntoIterator instance, and I wouldn't disagree.

How to process every value in a HashMap and optionally reject some?

I would like to process the values from a HashMap one by one, while maybe removing some of them.
For example, I would like to do an equivalent of:
use std::collections::HashMap;
fn example() {
let mut to_process = HashMap::new();
to_process.insert(1, true);
loop {
// get an arbitrary element
let ans = to_process.iter().next().clone(); // get an item from the hash
match ans {
Some((k, v)) => {
if condition(&k,&v) {
to_process.remove(&k);
}
}
None => break, // work finished
}
}
}
But this fails to compile:
error[E0502]: cannot borrow `to_process` as mutable because it is also borrowed as immutable
--> src/lib.rs:12:17
|
9 | let ans = to_process.iter().next().clone();
| ---------- immutable borrow occurs here
...
12 | to_process.remove(&k);
| ^^^^^^^^^^^------^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
I know I really would need https://github.com/rust-lang/rust/issues/27804 (which is for HashSet but for HashMap would be the same)
and I cannot implement the provided solutions without having a non-mut and mutable reference still or using unsafe.
Is there a simple way I am missing?
Note If you need to alter keys or add kvps to the HashMap during processing, see #edwardw's answer. Otherwise ...
Use HashMap::retain. You can change your process function to return a bool indicating whether to keep that key value pair. For example
let mut to_process: HashMap<u32, String> = HashMap::new();
to_process.insert(1, "ok".to_string());
to_process.insert(2, "bad".to_string());
to_process.retain(process);
fn process(k: &u32, v: &mut String) -> bool {
// do stuff with k and v
v == "ok"
}
This looks like an awfully good fit for Iterator::filter_map:
The closure must return an Option<T>. filter_map creates an iterator which calls this closure on each element. If the closure returns Some(element), then that element is returned. If the closure returns None, it will try again, and call the closure on the next element, seeing if it will return Some.
The following process_and_maybe_add is very simple, but you get the idea:
use std::collections::HashMap;
fn main() {
let mut data = HashMap::new();
data.insert(1, "a");
data.insert(2, "b");
data.insert(3, "c");
let processed = data
.into_iter()
.filter_map(process_and_maybe_add)
.collect::<HashMap<_, _>>();
dbg!(processed);
}
fn process_and_maybe_add((k, v): (u32, &str)) -> Option<(u32, String)> {
if k % 2 != 0 {
Some((k + 100, v.to_owned() + v))
} else {
None
}
}

Mutably borrowing in match statement and result

I'm trying to determine if a container has an object and return the found object if it does, or add it if it doesn't.
I've found Rust borrow mutable self inside match expression
which has an answer which says what I am trying to do can't (couldn't?) be done.
In my situation, I've got some objects that have vectors of children. I don't want to expose the internals of my object, because I may want to change the representation underneath.
How can you resolve the need to mutably borrow in different match arms in Rust? seems to suggest I may be able to do what I want if I get the lifetimes correct, but I haven't been able to figure out how.
Here's a representation of the issue I'm having:
fn find_val<'a>(container: &'a mut Vec<i32>, to_find: i32) -> Option<&'a mut i32> {
for item in container.iter_mut() {
if *item == to_find {
return Some(item);
}
}
None
}
fn main() {
let mut container = Vec::<i32>::new();
container.push(1);
container.push(2);
container.push(3);
let to_find = 4;
match find_val(&mut container, to_find) {
Some(x) => {
println!("Found {}", x);
}
_ => {
container.push(to_find);
println!("Added {}", to_find);
}
}
}
playground
The error I get is:
error[E0499]: cannot borrow `container` as mutable more than once at a time
--> src/main.rs:24:13
|
19 | match find_val(&mut container, to_find) {
| --------- first mutable borrow occurs here
...
24 | container.push(to_find);
| ^^^^^^^^^ second mutable borrow occurs here
...
27 | }
| - first borrow ends here
Put the change in a function, and use early return instead of an else branch:
fn find_val_or_insert(container: &mut Vec<i32>, to_find: i32) {
if let Some(x) = find_val(&container, to_find) {
println!("Found {}", x);
return; // <- return here instead of an else branch
}
container.push(to_find);
println!("Added {}", to_find);
}
See also Mutable borrow more than once and How to update-or-insert on a Vec?

Swapping values between two hashmaps

Edit Note: This code now compile see What are non-lexical lifetimes?.
I have two HashMaps and want to swap a value between them under certain conditions. If the key does not exist in the second HashMap, it should be inserted. I do not want to clone the value, since that is too expensive.
The (simplified) critical code that is not working is as follows:
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::mem;
#[derive(Debug)]
struct ExpensiveStruct {
replace: bool,
// imagine a lot of heap data here
}
fn main() {
let mut hm : HashMap<usize, ExpensiveStruct> = HashMap::new();
let mut hm1 : HashMap<usize, ExpensiveStruct> = HashMap::new();
let dummy = ExpensiveStruct { replace: false };
hm.insert(1, ExpensiveStruct { replace: true});
hm1.insert(1, ExpensiveStruct { replace: true});
match hm1.get_mut(&1) {
Some(ref mut x) =>
match hm.entry(1) {
Entry::Occupied(mut y) => { if y.get().replace {
mem::swap(x, &mut y.get_mut());
}
},
Entry::Vacant(y) => { y.insert(mem::replace(x, dummy)); }
},
None => {}
}
println!("{:?}", hm);
}
(On the Rust Playground)
I get the error:
error[E0597]: `y` does not live long enough
--> src/main.rs:28:9
|
23 | mem::swap(x, &mut y.get_mut());
| - borrow occurs here
...
28 | },
| ^ `y` dropped here while still borrowed
29 | None => {}
30 | }
| - borrowed value needs to live until here
I am really confused about this borrow problem and I do not see a way to fix it. If I replace the Entry by a match hm.get_mut(1), I cannot insert in the None case, since the matching mutably borrows the HashMap.
You're giving references to references where you should be giving references.
&mut y.get_mut()
for instance is
&mut &mut ExpensiveStruct
and you're having a simular issue with
match hm1.get_mut(&1) {
Some(ref mut x) =>
Your code works as expected when the types are trimmed down to &mut ExpensiveStruct:
match hm1.get_mut(&1) {
Some(x) => match hm.entry(1) {
Entry::Occupied(mut y) => if y.get().replace {
mem::swap(x, y.get_mut());
(On the Playground)
Note that the ref mut is removed, because hm1.get_mut(&1) already returns an Option for a mutable reference, and the &mut is removed because y.get_mut() already returns a reference.

Resources