Rust - How does String::from() actually work? - rust

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.

Related

trim() on .collect::<String>() E0716 in Rust

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.

Deleting duplicates from a single linked list

pub fn delete_duplicates(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
let mut head = head;
let mut cur = head.as_mut();
while cur.is_some() && cur.as_ref().unwrap().next.is_some() {
if cur.as_ref().unwrap().val == cur.as_ref().unwrap().next.as_ref().unwrap().val {
//let next = cur.unwrap().next.as_mut().unwrap().next.take(); (1) Error!
let next = cur.as_mut().unwrap().next.as_mut().unwrap().next.take(); // (2) Ok
cur.as_mut().unwrap().next = next;
}
else {
//cur = cur.as_ref().unwrap().next.as_mut(); (3) Error!
cur = cur.unwrap().next.as_mut(); // (4) Ok
}
}
head
}
The code above deletes duplicates inside a custom single linked list. (e.g. [1,1,2] -> [1,2])
Using (2) and (4) compiles and does what the program is supposed to do.
(1) and (3) generate compiler error and I just can't grasp what is happening here.
(1) Why do we need as_mut()? I believe cur is already mutable.
(3) Why can't I use as_ref() when assigning a value to lvalue?
(4) Isn't this consuming the Options as we proceed?
I have created a reproducible playground. Other folks, please use it to help and correct me if my answer is wrong.
For (1), when you call cur.unwrap(), the value (Option<&mut Box<ListNode>>) got moved from cur. but when you call cur.as_mut(), it gave you a Option<&mut &mut Box<ListNode>>, and unwrapping it doesn't move cur's value. I created a small playground code snippet to illustrate this.
For (3), it has two errors.
when you call cur.as_ref(), the value is behind a &, so you cannot access its value but then borrowed it as mutable again when you call as_mut()
if you replace the as_ref() call in (3) with a as_mut() call, you will see only the other error.
Line 28, Char 17: cannot assign to `cur` because it is borrowed (solution.rs)
|
28 | cur = cur.as_mut().unwrap().next.as_mut();
| ^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | borrow of `cur` occurs here
| assignment to borrowed `cur` occurs here
| borrow later used here
error: aborting due to previous error
You cannot first borrow cur's value, reassign cur with a new value, then use the borrowed value. Playground to illustrate this.
For (4), yes, it consumes cur's value, which is a Option<&mut Box<ListNode>>, not Option<Box<ListNode>>, so it is OK.

Programming in Rust , how to fix error[E0515] "cannot return value referencing local variable"?

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

Understanding a lifetime issue

I'm hitting a lifetime error when compiling a change I made for Firecracker (on aarch64, but I doubt the issue is architecture-dependent):
error[E0716]: temporary value dropped while borrowed
--> src/vmm/src/device_manager/mmio.rs:174:24
|
174 | let int_evt = &serial
| ________________________^
175 | | .lock()
176 | | .expect("Poisoned legacy serial lock")
| |__________________________________________________^ creates a temporary which is freed while still in use
177 | .interrupt_evt();
| - temporary value is freed at the end of this statement
178 | vm.register_irqfd(int_evt, self.irq)
| ------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
The original code (which compiles fine) is:
vm.register_irqfd(&serial
.lock()
.expect("Poisoned legacy serial lock")
.interrupt_evt(), self.irq)
.map_err(Error::RegisterIrqFd)?;
I don't understand the difference. The error message seems to state that expect() is returning a temporary and that I'm taking a const reference to it, in C++ this would extend the lifetime of the temporary, does it not in Rust? Either way, why does it work in the original code but not after I bind to an l-value (C++ parlance, I'm not sure if it is the same for Rust)?
I tried creating a SSCE here, but it worked as expected!
A simple, reproducible example of the problem (playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let item: &i32 = mutex.lock().unwrap().get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
mutex.lock().unwrap() returns a MutexGuard<'_, Option<i32>>, which borrows the data inside the mutex. It also owns a lock on the data, that is released when the guard is dropped, which means that noone else may borrow the data at the same time.
When you call a method of the inner type on that guard (like .get in the above example, or .interrupt_evt in your code), it will borrow with the lifetime of the guard, since you can only access the data safely while the guard exist. But the guard isn't stored in any variable, so it only exists temporarily for that statement, and is immediately dropped at the end of it. So you cannot get a reference to the data outside of the statement.
To solve this problem is very simple: first store the guard in a variable, and then borrow from it. That will ensure that the guard lives longer than the references you get from it (playground):
// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);
// get reference to item inside array
let guard = mutex.lock().unwrap();
let item: &i32 = guard.get(0).unwrap();
// use reference for something
println!("item = {:?}", item);
// guard is now destroyed at end of scope
// and mutex lock is released here

Lack of lifetime of line from buffered reader prevents splitting line

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
}

Resources