I am pretty new to Rust, an I'm currently having an issue with the borrow checker. This code doesn't compile:
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut out = String::new();
let mut xs = self.xs.iter();
for x in 0..11 {
out += match x {
3 | 7 => "|",
_ => &xs.next().unwrap().to_string(),
};
}
write!(f, "{}", out)
}
I get the error
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:32:23
|
30 | out += match x {
| ____________________-
31 | | 3 | 7 => "|",
32 | | _ => &xs.next().unwrap().to_string(),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary which is freed while still in use
33 | | };
| |_____________- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
However, this very similar code does compile: (of course, not with the functionality I'm looking for)
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut out = String::new();
let mut xs = self.xs.iter();
for x in 0..11 {
out += &xs.next().unwrap().to_string();
}
write!(f, "{}", out)
}
My question is, why does the first example not work if the second one does? My understanding is that the match should just be evaluated as what it's returning, so in the case where x is not 3 or 7, it should be functionally the same as the second example.
Thank you!
The arms in a match are allowed to be blocks with their own statements. The impact of that is that temporaries created in a match arm are dropped when leaving the arm. The temporary String created by xs.next().unwrap().to_string() is thus destroyed before the add-assign can execute, but the arm is attempting to return a borrow against the temporary, resulting in a dangling reference.
The second example you show doesn't have this problem because it doesn't use match (or another block construct like if) and so the temporary exists until after the add-assign is performed.
One way around this is to have both arms of the match return a String:
out += match x {
3 | 7 => "|".to_string(),
_ => xs.next().unwrap().to_string(),
};
(If you needed the match to evaluate as a &str you could borrow the result with &match x { ... } but that's not necessary here.)
Of course, this adds an extra allocation in the 3 | 7 case, but is required to make the types of the match arms consistent.
Consider instead performing the add-assign inside of the match. With this approach, the problem of making the arm types match goes away, and the temporary lifetime issue is also resolved:
match x {
3 | 7 => { out += "|"; }
_ => { out += xs.next().unwrap().to_string(); }
};
Related
I'm trying to iterate through a String via bytes iterator. My intent is to convert each byte into a single-letter &str which gets pattern matched. Based on the match arm, it will attempt to add the value to a Vector. However, I've been getting an error which is preventing the program to compile. The error message is this:
Line 15, Char 22: temporary value dropped while borrowed (solution.rs)
|
15 | let b = &[byte];
| ^^^^^^ creates a temporary which is freed while still in use
...
20 | stack.push(c);
| ------------- borrow later used here
...
39 | }
| - temporary value is freed at the end of this statement
|
= note: consider using a `let` binding to create a longer lived value
For more information about this error, try `rustc --explain E0716`.
error: could not compile `prog` due to previous error
For reference, this is the underlying piece of code:
let mut stack: Vec<&str> = Vec::new();
for byte in s.bytes() {
let b = &[byte];
let c = str::from_utf8(b).unwrap();
match c {
"a" | "b" => {
stack.push(c);
},
_ => println!("not a or b"),
}
}
The problem is that you create a temporary array, [byte], and try to push a borrow of it into the Vec. But the array lives only to the end of the scope - and the Vec lives longer!
The usual solution will be to heap-allocate the &str: stack.push(c.to_owned()). But because the memory layout of one-element array matches that of the element, you can go without allocating. First, you need to get a reference to byte with the same lifetime as the original &str instead of a by-value byte. This can be done by iterating over s.as_bytes() (that returns &[u8]) instead of over s.bytes() (that returns impl Iterator<Item = u8>).
Second, you need to convert this byte into an array with the same lifetime. There is a function in the standard library to do that: std::array::from_ref().
So:
let mut stack: Vec<&str> = Vec::new();
for byte in s.as_bytes() {
let b = std::array::from_ref(byte);
let c = str::from_utf8(b).unwrap();
match c {
"a" | "b" => {
stack.push(c);
}
_ => println!("not a or b"),
}
}
How can I return a default value from an Option<&String>?
This is my sample/minimal code:
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("").or_else(|| Some("")).unwrap(); // <== I tried lots of combinations
println!("{}", result);
}
I know I could do something like this...
let value = match result {
Some(v) => v,
None => "",
};
... but I want to know if it is possible to implement it in a one-liner with or_else or unwrap_or_else?
(It is important to make the default value lazy, so it does not get computed if it is not used)
These are some of the compiler suggestions I tried (I can put them all because SO won't allow me):
7 | let result = map.get("").or_else(|| Some("") ).unwrap();
| ^^ expected struct `String`, found `str`
.
7 | let result = map.get("").or_else(|| Some(&"".to_string()) ).unwrap();
| ^^^^^^--------------^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
.
7 | let result = map.get("").or_else(|| Some(String::new()) ).unwrap();
| ^^^^^^^^^^^^^
| |
| expected `&String`, found struct `String`
|
help: consider borrowing here: `&String::new()`
.
7 | let result = map.get("").or_else(|| Some(&String::new()) ).unwrap();
| ^^^^^^-------------^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
.
and also
6 | let result = map.get("").unwrap_or_else(|| ""); // I tried lots
| ^^ expected struct `String`, found `str`
|
= note: expected reference `&String`
found reference `&'static str`
If you really need a &String as the result, you may create a String for the default value with lifetime that's long enough.
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let default_value = "default_value".to_string();
let result = map.get("").unwrap_or(&default_value);
println!("{}", result);
}
If the default value is a compile-time fixed value, the allocation of default_value can be avoided by using &str instead.
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("")
.map(String::as_str)
.unwrap_or("default_value");
println!("{}", result);
}
How can I return a default value from an Option<&String>?
It's not trivial because as you've discovered ownership gets in the way, as you need an actual String to create an &String. The cheap and easy solution to that is to just have a static empty String really:
static DEFAULT: String = String::new();
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("").unwrap_or(&DEFAULT); // <== I tried lots of combinations
println!("{}", result);
}
String::new is const since 1.39.0, and does not allocate, so this works fine. If you want a non-empty string as default value it's not as good a solution though.
The cleaner and more regular alternative is to "downgrade" (or upgrade, depending on the POV) the &String to an &str:
let result = map.get("").map(String::as_str).unwrap_or("");
or
let result = map.get("").map(|s| &**s).unwrap_or("");
it's really not like you're losing anything here, as &String is not much more capable than &str (it does offer a few more thing e.g. String::capacity, but for the most part it exists on genericity grounds e.g. HashMap::<K, V>::get returns an &V, so if you store a String you get an &String makes sense even though it's not always quite the thing you want most).
This question already has answers here:
How to lookup from and insert into a HashMap efficiently?
(2 answers)
Closed 3 years ago.
I'm attempting to count the character frequency in a string and store the count of each character in a BTreeMap. However, I'm getting a warning and would like to get rid of it.
This is what I've tried:
use std::collections::BTreeMap;
fn letter_frequency(input: &str) -> BTreeMap<char, i32> {
let mut tree: BTreeMap<char, i32> = BTreeMap::new();
for item in &input.chars().collect::<Vec<char>>() {
match tree.get(item) {
Some(count) => tree.insert(*item, *count + 1),
None => tree.insert(*item, 1)
};
}
tree
}
This is the warning:
warning: cannot borrow `tree` as mutable because it is also borrowed as immutable
--> src/lib.rs:7:28
|
6 | match tree.get(item) {
| ---- immutable borrow occurs here
7 | Some(count) => tree.insert(*item, *count + 1),
| ^^^^ ------ immutable borrow later used here
| |
| mutable borrow occurs here
|
= note: #[warn(mutable_borrow_reservation_conflict)] on by default
= warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
= note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
How do I correctly use match with a BTreeMap to avoid the error ?
Like Svetlin mentions in a comment, the entry API is your friend. Below I've also removed one unneeded collect.
fn letter_frequency(input: &str) -> BTreeMap<char, i32> {
let mut tree = BTreeMap::new();
for item in input.chars() {
let count = tree.entry(item).or_insert(0);
*count += 1;
}
tree
}
The temporary variable count is not really needed: *tree.entry(item).or_insert(0) += 1; works just fine but might look a bit crowded at first.
I'm having an issue with a match expression in my code raising a warning when a guard is included. I believe that this warning has to do with the non-lexical lifetimes used by the borrow checker. My function either returns a mutable reference to an item from a collection, or a clone of the whole collection.
#[derive(Debug, Clone)]
enum Value {
Int(i32),
List(Vec<Value>),
}
#[derive(Debug)]
struct Error(&'static str, Value);
fn main() {
let mut value = Value::List(vec![
Value::Int(1),
Value::Int(2),
Value::Int(34),
Value::Int(12),
]);
let y = index_list(&mut value, 2);
let _ = dbg!(y);
}
fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
match *value {
Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
Value::List(_) => Err(Error("index out of range", value.clone())),
_ => Err(Error("tried to index int", value.clone())),
}
}
playground
It compiles and runs, but I get a very ominous looking warning:
warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
--> src/main.rs:25:59
|
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
| -- lifetime `'a` defined here
23 | match *value {
24 | Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
| ------------ ------------------ returning this value requires that `value.0` is borrowed for `'a`
| |
| mutable borrow occurs here
25 | Value::List(_) => Err(Error("index out of range", value.clone())),
| ^^^^^ immutable borrow occurs here
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
--> src/main.rs:26:46
|
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
| -- lifetime `'a` defined here
23 | match *value {
24 | Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
| ------------ ------------------ returning this value requires that `value.0` is borrowed for `'a`
| |
| mutable borrow occurs here
25 | Value::List(_) => Err(Error("index out of range", value.clone())),
26 | _ => Err(Error("tried to index int", value.clone())),
| ^^^^^ immutable borrow occurs here
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
I don't understand why the Err(value.clone()) line requires that the Ok(&mut ...) borrow still be active, because they are mutually exclusive and both result in a function return. This warning goes away if I remove the guard on the first match arm, but I need that guard to be there. Is this a bug in the NLL system? I never had this issue with the old borrowck. How can I make this better?
This error looks like a limitation of the borrow checker to me, similar to Double mutable borrow error in a loop happens even with NLL on. I can't see how this could lead to a soundness hole, and believe the code is safe.
When using an if statementat instead of match, the warning can be avoided, and the code becomes more readable as well:
fn bar<'a>(
map: &'a mut HashMap<String, Vec<i32>>,
name: &str,
idx: usize,
) -> Result<&'a mut i32, Vec<i32>> {
let value = map.get_mut(name).unwrap();
if idx < value.len() {
Ok(&mut value[idx])
} else {
Err(value.clone())
}
}
Editor's note: This question was asked before Rust 1.0. Since then, many functions and types have changed, as have certain language semantics. The code in the question is no longer valid, but the ideas expressed in the answers may be.
I'm trying to list the files in a directory and copy the filename to my own Vec. I've tried several solutions, but it always ends up with a problem of not being able to create long enough living variables. I don't understand my mistake.
fn getList(action_dir_path : &str) -> Vec<&str> {
let v = fs::readdir(&Path::new(action_dir_path))
.unwrap()
.iter()
.map(|&x| x.filestem_str().unwrap())
.collect();
return v;
}
Why does the compiler complain about "x" ? I don't care about x, I want the &str inside it and I thought &str were static.
I tried this way, but I got the same result with the compiler complaining about "paths" not living long enough.
fn getList2(action_dir_path : &str) -> Vec<&str> {
let paths = fs::readdir(&Path::new(action_dir_path)).unwrap();
let mut v : Vec<&str> = Vec::new();
for path in paths.iter(){
let aSlice = path.filestem_str().unwrap();
v.push(aSlice);
}
return v;
}
Here is the playground.
The most literal translation of your code that supports Rust 1.0 is this:
use std::{fs, path::Path, ffi::OsStr};
fn getList(action_dir_path: &str) -> Vec<&OsStr> {
let v = fs::read_dir(&Path::new(action_dir_path))
.unwrap()
.map(|x| x.unwrap().path().file_stem().unwrap())
.collect();
return v;
}
This produces the error messages:
Rust 2015
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| ^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 3:1...
--> src/lib.rs:3:1
|
3 | / fn getList(action_dir_path: &str) -> Vec<&OsStr> {
4 | | let v = fs::read_dir(&Path::new(action_dir_path))
5 | | .unwrap()
6 | | .map(|x| x.unwrap().path().file_stem().unwrap())
7 | | .collect();
8 | | return v;
9 | | }
| |_^
Rust 2018
error[E0515]: cannot return value referencing temporary value
--> src/lib.rs:6:18
|
6 | .map(|x| x.unwrap().path().file_stem().unwrap())
| -----------------^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| temporary value created here
The problem comes from Path::file_stem. This is the signature:
pub fn file_stem(&self) -> Option<&OsStr>
This indicates that the method will return a borrowed reference to a OsStr. The PathBuf struct is the owner of the string. When you leave the method, there's nowhere left that owns the PathBuf, so it will be dropped. This means that any references into the PathBuf will no longer be valid. This is Rust preventing you from having references to memory that is no longer allocated, yay for Rust!
The easiest thing you can do is return a Vec<String>. String owns the string inside of it, so we don't need to worry about it being freed when we leave the function:
fn get_list(action_dir_path: &str) -> Vec<String> {
fs::read_dir(action_dir_path)
.unwrap()
.map(|x| {
x.unwrap()
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string()
})
.collect()
}
I also updated the style (at no charge!) to be more Rust-like:
Use snake_case for items
No space before the colon in type definitions
There's no reason to set a variable just to return it.
Don't use explicit return statements unless you are exiting from a function early.
There's no need to wrap the path in a Path.
However, I'm not a fan of all of the unwrapping. I'd write the function like this:
use std::{ffi::OsString, fs, io, path::Path};
fn get_list(action_dir_path: impl AsRef<Path>) -> io::Result<Vec<OsString>> {
fs::read_dir(action_dir_path)?
.map(|entry| entry.map(|e| e.file_name()))
.collect()
}
fn main() {
println!("{:?}", get_list("/etc"));
}
In addition to the changes above:
I use a generic type for the input path.
I return a Result to propagate errors to the caller.
I directly ask the DirEntry for the filename.
I leave the type as an OsString.
One small related point:
I thought &str were static.
&'static strs are static, but that's only one kind of &str. It can have any kind of lifetime.