Rust code:
let item_discount_price = item_discount_price_element.text().collect::<String>().trim();
give error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:64:35
|
64 | let item_discount_price = item_discount_price_element.text().collect::<String>().trim();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
65 | let item_discount_price = item_discount_price.trim();
| -------------------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived valu
I have solved it with following code:
let item_discount_price = item_discount_price_element.text().collect::<String>();
let item_discount_price = item_discount_price.trim();
For background I am doing some web-scraping, item_discount_price_element is ElementRef from scraper. https://docs.rs/scraper/latest/scraper/element_ref/struct.ElementRef.html
Question is why first code is not working ?
If you look at the documentation of trim() you'll notice that it's not a member of String but of str:
pub fn trim(&self) -> &str
That is, it takes a &str (string slice) and returns another &str, a subslice, with the same lifetime.
Now, your code is equivalent of doing something like:
let s: &str = String::from(" hello ").trim();
That would create a temporary String, borrow it as a &str, and compute its trim slice. Then the temporary is dropped and the trimmed slice reference is invalidated. Fortunately Rust lifetime rules prevents you from trying to use that invalid value.
If you save a temporary value in a variable you avoid dropping it, as you noticed in your code.
let s: String = String::from(" hello ");
let s: &str = s.trim();
And now the code does what you want.
If you do not need to ever use the temporary it is idiomatic in Rust to use the same name for both values, to illustrate that point, and to avoid having to think of two names (naming things is hard). Note that the first s still exists, it is not destroyed by having another variable with the same name, it is just shadowed, and it will be dropped normally at the end of its scope.
Related
I just started to learn Rust and I'm trying to compare two versions of type String. As the Version::from function only accepts &str, I'm trying to convert, but then I get the error that tag does not live long enough. I understand that it goes out of scope, but how to fix it?
use version_compare::Version;
fn main() {
let tags: Vec<String> = vec!["1.2.1".to_string(),"1.2.2".to_string()];
let mut max_ver = Version::from("0.0.0").unwrap();
for tag in tags {
let v_tag = Version::from(&tag.as_str()).unwrap();
if v_tag > max_ver {
max_ver = v_tag;
}
}
println!("max_ver: {max_ver}");
}
Error:
Compiling rust-test2 v0.1.0 (/Users/scenox/rust-test2)
error[E0597]: `tag` does not live long enough
--> src/main.rs:8:36
|
8 | let v_tag = Version::from(&tag.as_str()).unwrap();
| ^^^^^^^^^^^^ borrowed value does not live long enough
9 | if v_tag > max_ver {
| ------- borrow later used here
...
12 | }
| - `tag` dropped here while still borrowed
The problem here is that the for loop consumes tags, meaning that each String is assigned to tag one-by-one, and at the end of each iteration that String is dropped. At the end of the loop, the Vec itself is dropped. However, v_tag might reference one of these String values, which means you could have a Version that borrows from a String that no longer exists. This is a "use after free" bug that Rust has discovered in your code.
To fix this, iterate over tags without consuming it. You can do this by iterating over a reference to the Vec:
for tag in &tags {
Or by using the iter() method:
for tag in tags.iter() {
In both of these cases, tag has type &String. In your code, tag has type String. This is an important distinction!
I'm still new to rust so there are some stuff about it I still don't know
I'm trying to split a String value and pass it to a variable like so:
let mut splitted_line = String::from("something=random").split("=");
let key = splitted_line.nth(0).expect("incorrect line format");
let value = splitted_line.nth(1).expect("incorrect line format");
The code above raise the following error:
error[E0716]: temporary value dropped while borrowed
--> src\main.rs:49:37
|
49 | let mut splitted_line = String::from("something=random").split("=");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
...
55 | let key = splitted_line.clone().nth(0).expect("incorrect line format");
| --------------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
Yet (from some reason I still don't know), when I run the code below it works completely fine:
let line = String::from("something=random");
let mut splitted_line = line.split("=");
let key = splitted_line.nth(0).expect("incorrect line format");
let value = splitted_line.nth(1).expect("incorrect line format");
As far as I read from the Error code description. This shouldn't have happened cause I'm not borrowing any value from a variable.
Can someone explain to me why the first code raises the temporary value error and why the second one doesn't?
split() is borrowing. It takes &self and returns an iterator that yields &strs referencing self.
The difference between the codes is when the splitted string is freed: in the first snippet it is a temporary because we invoke a method on it immediately and it is freed at the end of the statement (let mut splitted_line = String::from("something=random").split("=");). However, since we use the strings split() returned after, and they're borrowing from this freed string, this is an error.
On the second case, the string is not temporary as it is bound to a variable, and it is freed at the end of the scope. Thus, it is still alive when we use split() returned strings.
I want to return String from &Option<String> which is returned from config map from configparser::ini::Ini
use configparser::ini::Ini;
fn main() {
let filename : String = "p2pvpn.conf".to_owned();
let mut config = Ini::new();
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap(); //returns hashmap
/*
* 1st unwrap returns &Option<String>
* 2nd unwrap should return String
*/
let tunopt : String = tunc.get("ip").unwrap().unwrap(); //here is the problem
println!("{tunopt}");
}
But I am getting this error:
--> src/main.rs:9:28
|
9 | let tunopt : String = tunc.get("ip").unwrap().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
|
help: consider borrowing the `Option`'s content
|
9 | let tunopt : String = tunc.get("ip").unwrap().unwrap().as_ref();
| +++++++++
I tried that option with as_ref, but it did not helped (expected struct String, found reference), and to_string did not helped too.
I know that first unwrap after get returns &Option<String> tunc.get("ip").unwrap().unwrap().
I tried this:
let tunopt : Option<String> = *(tunc.get("ip").unwrap()); //move this, deference using asterisk
I thought it will move ownership but still not working
13 | let tunopt : Option<String> = *(tunc.get("ip").unwrap()); //move this, deference using asterisk
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
|
help: consider borrowing the `Option`'s content
My questions are:
How to properly get value String from option reference &Option<String>?
Why I can't deference &Option<String> and move it's ownership to variable of type Option<String>?
Why is it complaining about this: not implement the 'Copy' trait', I am tring to move and not to copy.
You can't directly move a value from a HashMap using get. Moving generally imply that you are taking something from somewhere.
In this case you are trying to simply "get" the value, so you only receive the reverence to it. To move it, you can remove the element from the HashMap, so you receive the value itself.
In practice you can do is:
Copy a value from the hashMap:
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap();
let tunopt: &Option<String> = tunc.get("ip").unwrap();
let tunopt: &String = tunopt.as_ref().unwrap();
let tunopt: String = tunopt.to_owned();
Or take the value, AKA remove from HashMap to local var.
let mut map = config.load(filename).unwrap();
let tunc = map.get_mut("tun").unwrap();
let tunopt: Option<String> = tunc.remove("ip").unwrap();
let tunopt: String = tunopt.unwrap();
You cannot move a String out of a &Option<String>, full stop. In fact, you can't move anything out of an object behind an immutable reference (&), unless the object has some form of interior mutability (RefCell, Mutex, etc). That is the point of immutable borrows.
To move the String out of the config, the config would have to update itself to reflect that it no longer owns the string and that it should no longer attempt to access it. Otherwise, it may very well attempt to access the string after you have freed it, causing memory unsafety.
configparser::ini::Ini does have a method to remove the string value and transfer its ownership: remove_key. This will allow you to move the string out of the Ini, but as the name suggests, it does this by removing it from the Ini object.
Or you could just clone the string. Config parsing is unlikely to be a hotspot in your program, and such strings aren't usually that big.
You can't move the String out of the shared reference, but You can borrow the Option's contents by using the reference on that String:
use configparser::ini::Ini;
fn main() {
let filename: String = "p2pvpn.conf".to_owned();
let mut config = Ini::new();
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap(); //returns hashmap
/*
* 1st unwrap returns &Option<String>
* 2nd unwrap returns &String
*/
let tunopt: &String = tunc.get("ip").unwrap().as_ref().unwrap(); //problem solved
println!("{tunopt}");
}
Please help me to compile my code attached bellow. The compiler says that following 2 patterns depending on which lines I comment out.
The program reads a &str which is a simple "svg path command" like code then parses it. The pasted code has been simplified for simplicity. It uses Regex to split the input string into lines then study each line in the main for loop. Each loop pushes the parse result onto a vector. Finally the function returns the vector.
Basically the compiler says returning the vector is not allowed because it refers local variable. Though I don't have any workaround.
error[E0597]: `cmd` does not live long enough
--> src/main.rs:24:25
|
24 | codeV = re.captures(cmd.as_str());
| ----- ^^^ borrowed value does not live long enough
| |
| borrow might be used here, when `codeV` is dropped and runs the destructor for type `Option<regex::Captures<'_>>`
...
30 | }
| - `cmd` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are defined
error[E0515]: cannot return value referencing local variable `cmd`
--> src/main.rs:31:1
|
24 | codeV = re.captures(cmd.as_str());
| --- `cmd` is borrowed here
...
31 | V //Error
| ^ returns a value referencing data owned by the current function
Playground
use regex::Regex;
pub fn parse(path:&str) {//->Vec<Option<regex::Captures<>>> //Error
let reg_n=Regex::new(r"\n").unwrap();
let path=reg_n.replace_all("\n"," ");
let reg_cmd=Regex::new(r"(?P<cmd>[mlhv])").unwrap();
let path=reg_cmd.replace_all(&path,"\n${cmd}");
let cmdV=reg_n.split(&path);
//let cmdV:Vec<&str> = reg.split(path).map(|x|x).collect();
let mut V:Vec<Option<regex::Captures<>>>=vec![];
let mut codeV:Option<regex::Captures<>>=None;
let mut count=0;
for cmd_f in cmdV{//This loop block has been simplified.
count+=1;
if count==1{continue;}
let mut cmd="".to_string();
cmd=cmd_f.to_string();
cmd=cmd.replace(" ","");
let re = Regex::new(r"\{(?P<code>[^\{^\}]{0,})\}").unwrap();
codeV = re.captures(cmd.as_str());
//cmd= re.replace_all(cmd.as_str(),"").to_string();
let cmd_0=cmd.chars().nth(0).unwrap();
//cmd.remove(0);
//V.push(codeV); //Compile error
V.push(None); //OK
}
//V
}
fn main() {
parse("m {abcd} l {efgh}");
}
Though I don't have any workaround.
regex's captures refer to the string they matched for efficiency. This means they can't outlive that string, as the match groups are essentially just offsets into that string.
Since the strings you match are created in the loop body, this means captures can't escape the loop body.
Aside from not creating strings in the loop body (or even the function), the solution / workaround is to convert your capture groups to owned data and store that: instead of trying to return a vector of captures, extract from the capture the data you actually want, convert it to an owned String (or tuple thereof, or whatever), and push that onto your vector.
e.g. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0107333e30f831a418d75b280e9e2f31
you can use cmd.clone().as_str() if you not sure the value has borrowed or no
I am banging my head trying to figure out Rust's borrowing/lifetime/ownership properties. Namely, when using a buffered reader, and attempting to split a line. The code
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let f = File::open("foo.txt").expect("file not found");
let f = BufReader::new(f);
for line in f.lines() {
let split: Vec<&str> = {
let ln: String = line.unwrap();
ln.split(' ').collect()
};
}
}
or any variation of (with or without specifying variable type, futile attempts to make it mutable, etc) results in:
'ln' does not live long enough; borrowed value must only be valid for the static lifetime...
yet trying to maybe fake an extended lifetime and grab some data out of the line via a slice
let nm = line;
name = &line[..];
or even just trying to operate the split() on the unmodified line variable results in:
cannot index into a value of type 'std::result::Result<std::string::String, std::io::Error>'
"borrowed value does not live long enough" seems to blame the wrong thing suggests that the lifetime lasts long enough to put each word into its own string, but modifying my original code on the Playground to include that nested for loop still results in a
error[E0597]: borrowed value does not live long enough
--> src/main.rs:11:18
|
11 | for w in line.unwrap().split_whitespace() {
| ^^^^^^^^^^^^^ temporary value does not live long enough
...
14 | }
| - temporary value dropped here while still borrowed
15 | }
| - temporary value needs to live until here
|
= note: consider using a `let` binding to increase its lifetime
in reference to the line.unwrap()
Ultimately, what am I misunderstanding here about Rust's lifetime or borrowing properties?
The error your original code gives when compiled is:
error[E0597]: `ln` does not live long enough
--> src/main.rs:11:13
|
11 | ln.split(' ').collect()
| ^^ borrowed value does not live long enough
12 | };
| - `ln` dropped here while still borrowed
13 | }
| - borrowed value needs to live until here
error: aborting due to previous error
As per #shepmasters comments it is a good idea to provide the full error when posting a question.
Anyway, it highlights the problem:
let split: Vec<&str> = {
let ln: String = line.unwrap();
ln.split(' ').collect()
};
You are creating a Vec containing references to str slices; slices don't own the data that they are sliced from, they are effectively pointers into data which has to be owned by another variable. Therefore the variable that they are sliced from have to outlive the slices.
Inside the expression you are using to initialise the Vec, you create a String containing the line of text that you are processing. The scope of this string is the variable ln is the initialisation expression - it will be dropped as soon as you leave that scope.
Then you split the string, which returns an iterator to string slices, one per substring. Remember though, the iterator is returning slices, which are pointers to the substrings in the String ln. Those slices are not allowed to outlive ln itself.
Hopefully you can see the problem now. As soon as you exit the initialisation expression, ln is dropped, but the Vec would still contain the str slices. What are they pointing to?
The fix is very simple. Why declare ln inside that block? In fact why have a block there at all? This works:
for line in f.lines() {
let ln: String = line.unwrap();
let split: Vec<&str> = ln.split(' ').collect();
// Now do something with split
}