Variable lifetime qs in function [duplicate] - rust

This question already has answers here:
Return local String as a slice (&str)
(7 answers)
Proper way to return a new string in Rust
(2 answers)
Why can I return a reference to a local literal but not a variable?
(1 answer)
Closed 4 years ago.
fn get_str1<'a>() -> &'a str {
let x = "hello";
return x;
}
fn get_str2<'a>(str1: &str) -> &'a str {
let x: &'a str = (str1.to_string() + "123").as_str();
return x;
}
fn get_str3<'a>(str1: &str) -> &'a str {
let tmp = str1.to_string() + "123";
let x: &'a str = tmp.as_str();
return x;
}
#[test]
fn lifetime_test() {
println!("{}", get_str1());
println!("{}", get_str2("hello"));
println!("{}", get_str3("hello"))
}
When I call get_str1, it has no problem, but when I call get_str2, it has a compilation error:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:7:22
|
7 | let x: &'a str = (str1.to_string() + "123").as_str();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 6:1...
--> src/main.rs:6:1
|
6 | fn get_str2<'a>(str1: &str) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: consider using a `let` binding to increase its lifetime
When I call get_str3, it also has a compilation error:
error[E0597]: `tmp` does not live long enough
--> src/main.rs:13:22
|
13 | let x: &'a str = tmp.as_str();
| ^^^ borrowed value does not live long enough
14 | return x;
15 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 11:1...
--> src/main.rs:11:1
|
11 | fn get_str3<'a>(str1: &str) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Why do I get these errors and how do I fix get_str2 and get_str3?

The first function works because the string has the 'static lifetime and will be promoted by the compiler.
As for the others..
fn get_str2<'a>(str1: &str) -> &'a str {
let x = (str1.to_string() + "123").as_str();
return x
}
This part: str1.to_string(), isn't returning a string slice... but a new instance of a String. This is basically the same as this:
let x = str1.to_string(); // this is of type String, and its lifetime is local to this function
let y = str1 + "123"; // This consumes str1 and appends a &str to it - thus the lifetime is still of the new String instance above
let z = y.as_str(); // This returns a reference to the local String instance
Reading each comment above, it becomes clear that you're actually trying to return a reference to a local String. You can't do this, because the String will be destroyed at the end of the function and the reference will be invalid.
This applies to your third function as well. You're returning a reference to a String instance that will be destroyed at the end of the function.

Related

Rust borrow checker throwing error in if statement

I'm running through some LeetCode challenges to build up my understanding of Rust. I'm trying to write the following program that takes an i32 input, converts it to a String, reverses the digits, and returns back an i32 number.
In the case of negative numbers, e.g. -132, when the number is reversed the hyphen has to be popped off the stack: -132 -> 231- -> 231.
I've written the following code but I'm bumping up against the borrow checker, can anyone help?
impl Solution {
pub fn reverse(x: i32) -> i32 {
if(x == 0){
return x;
}
let reversed : std::iter::Rev<std::str::Chars> = x.to_string().chars().rev();
if reversed.last().unwrap() == '-' { //error occurs here
return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
} else {
return reversed.collect::<String>().parse::<i32>().unwrap();
}
}
}
Line 6, Char 61: temporary value dropped while borrowed (solution.rs)
|
6 | let reversed : &std::iter::Rev<std::str::Chars> = &x.to_string().chars().rev();
| ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
7 | if reversed.last().unwrap() == '-' {
| -------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
Line 7, Char 12: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
7 | if reversed.last().unwrap() == '-' {
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 8, Char 20: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
8 | return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 8, Char 52: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
8 | return reversed.collect::<String>()[0..reversed.count()].parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Line 10, Char 20: cannot move out of `*reversed` which is behind a shared reference (solution.rs)
|
10 | return reversed.collect::<String>().parse::<i32>().unwrap();
| ^^^^^^^^ move occurs because `*reversed` has type `std::iter::Rev<std::str::Chars<'_>>`, which does not implement the `Copy` trait
Here's the error reproduced in a playground
Original error reproduced in this playground
Here's a solution that is close to your approach that fixes the error:
fn reverse(x: i32) -> i32 {
if x == 0 {
return x;
}
let mut reversed:String = x.to_string().chars().rev().collect::<String>();
if reversed.chars().last() == Some('-') {
reversed.pop();
}
reversed.parse::<i32>().unwrap()
}
working version: playground
This other post has good explanation of why. In the context of this question:
x.to_string().chars().rev();
// ^ ^
// String <- &str
to_string returns a String, but the code has no reference to that String after this statement, so it needs to free the String, but the iterator refers to the &str from chars() which then becomes a reference to something that no longer exists. By changing the type of reversed to String and using collect then Rust can bind the new data to the local variable and doesn't have to drop it at the end of the statement.
Does the LeetCode challenge impose the int→string→int conversions? I would do it directly on ints:
fn reverse (x: i32) -> i32 {
let mut x = x.abs();
let mut y = 0;
while x != 0 {
y = y*10 + x%10;
x = x/10;
}
return y;
}
Why not just take the absolute value of x before converting it to a String so you don't have to deal with the hyphen edge case?
fn reverse(x: i32) -> i32 {
x.abs()
.to_string()
.chars()
.rev()
.collect::<String>()
.parse::<i32>()
.unwrap()
}
fn main() {
assert_eq!(reverse(1234567), 7654321);
assert_eq!(reverse(-1234567), 7654321);
}
playground
Even if we get the input as a String and have to deal with the hyphen the most idiomatic solution would be to filter() it out:
fn reverse(x: String) -> i32 {
x.chars()
.filter(|&c| c != '-')
.rev()
.collect::<String>()
.parse::<i32>()
.unwrap()
}
fn main() {
assert_eq!(reverse(1234567.to_string()), 7654321);
assert_eq!(reverse((-1234567).to_string()), 7654321);
}
playground

Cannot use the entry API to mutate a HashMap using a reference as the key inside of a function

I'm trying to get a handle to an element in a mutable HashMap reference where the keys are &str.
In the example below, I'm trying to get value dict[key] so I can mutate it. How do I do this?
I've tried:
dict.entry(key): lifetime mismatch
dict.entry(&String::from(key)): borrowed value does not live long enough
e.g. this:
use std::collections::HashMap;
fn do_thing(key: &str, dict: &mut HashMap<&str, u32>) -> u32 {
let num = dict.entry(&String::from(key)).or_insert(0);
*num += 1;
return 42;
}
Errors out with:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:4:27
|
3 | fn do_thing(key: &str, dict: &mut HashMap<&str, u32>) -> u32 {
| - let's call the lifetime of this reference `'1`
4 | let num = dict.entry(&String::from(key)).or_insert(0);
| ------------^^^^^^^^^^^^^^^^^- - temporary value is freed at the end of this statement
| | |
| | creates a temporary which is freed while still in use
| argument requires that borrow lasts for `'1`
Link the lifetime of the key argument to the lifetime of the keys in the HashMap:
use std::collections::HashMap;
fn do_thing<'a>(key: &'a str, dict: &mut HashMap<&'a str, u32>) -> u32 {
*dict.entry(key).or_insert(0) += 1;
42
}
dict.entry(key)
The error message for this version helps understand the problem:
use std::collections::HashMap;
fn do_thing(key: &str, dict: &mut HashMap<&str, u32>) -> u32 {
*dict.entry(key).or_insert(0) += 1;
42
}
error[E0623]: lifetime mismatch
--> src/lib.rs:4:17
|
3 | fn do_thing(key: &str, dict: &mut HashMap<&str, u32>) -> u32 {
| ---- ----
| |
| these two types are declared with different lifetimes...
4 | *dict.entry(key).or_insert(0) += 1;
| ^^^ ...but data from `key` flows into `dict` here
Specifically, entry will store key in the HashMap, but the value referenced by key might become invalid before the HashMap does. If that happened, the HashMap would contain a dangling reference, pointing to invalid memory. That's exactly what Rust's borrow checker prevents.
See also:
When is it required to use lifetimes?
Why are explicit lifetimes needed in Rust?
dict.entry(&String::from(key))
This can never work here, for much the same reason.
See also:
Return local String as a slice (&str)

Rust - Lifetime of struct member depends on another struct member [duplicate]

This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 3 years ago.
I'm trying to write a Rust struct. The struct owns a Reference counted pointer to a string and also owns a vector of string slices to the same string.
Furthermore I'm trying to write a function to generate this struct. I'm unsure how to proceed.
struct MyStruct<'a> {
rc_string: Rc<String>,
vec: Vec<&'a str>
}
fn build_my_struct<'a>(s: &Rc<String>) -> MyStruct<'a> {
let rc_string = s.clone();
let mut vec = Vec::new();
vec.push(&rc_string[0..2]);
MyStruct {
rc_string: rc_string,
vec: vec
}
}
error[E0515]: cannot return value referencing local variable `rc_string`
--> src/main.rs:13:5
|
11 | vec.push(&rc_string[0..2]);
| --------- `rc_string` is borrowed here
12 |
13 | / MyStruct {
14 | | rc_string: rc_string,
15 | | vec: vec
16 | | }
| |_____^ returns a value referencing data owned by the current function
I understand that the vec variable has borrowed the rc_string. The compiler doesn't like returning vec because it has the borrow to the local variable rc_string.
However rc_string is being returned as well? The string slices are valid for the duration of the life of MyStruct.rc_string?
You need to borrow Rc for life time 'a as well. Compiler needs to know that slice from a String is living in 'a or not. In this case we need to borrow Rc for 'a and compiler will know inner of Rc will also live in 'a.
If you clone s and assign it to rc_string:
s will stay in the function's scope as borrowed Rc for lifetime 'a
rc_string will be the owner of the Rc pointer
and compiler won't be able to know slice of a rc_string is living for 'a or not.
Using slice from a s will work :
fn build_my_struct<'a>(s: &'a Rc<String>) -> MyStruct<'a> {
let mut vec = Vec::new();
let rc_string = s.clone();
vec.push(&s[0..2]);
MyStruct { rc_string, vec }
}
Playground

What is the correct way to get values via a method without moving it? [duplicate]

This question already has answers here:
How to prevent a value from being moved?
(2 answers)
When would an implementation want to take ownership of self in Rust?
(2 answers)
What do I have to do to solve a "use of moved value" error?
(3 answers)
How can I solve "use of moved value" and "which does not implement the `Copy` trait"?
(1 answer)
Closed 3 years ago.
I don't understand why Rust moves the value. Do I oversee a major point in the ownership?
The struct MyData is a smaller version. I store some values in this struct, and want to access the stored values, but the compiler tells me after the second access, that the value was moved.
I want to make some getters for my structs. I already derived Clone, but that does not help.
The problem occurs on Windows 10 with the GNU-Compiler and on Kubuntu 18.04 LTS.
My current workaround is to clone the data beforehand, but this can't be the correct way.
#[derive(Debug, Clone)]
struct MyData {
val1: i32,
val2: String,
}
impl MyData {
pub fn get_val1(self) -> i32 {
return self.val1.clone();
}
pub fn get_val2(self) -> String {
return self.val2.clone();
}
pub fn get_both(self) -> (i32, String) {
return (self.val1, self.val2);
}
}
fn main() {
let d = MyData {
val1: 35,
val2: String::from("Hello World"),
};
let both = d.get_both();
let x = d.get_val1();
let y = d.get_val2();
}
error[E0382]: use of moved value: `d`
--> src/main.rs:28:13
|
27 | let both = d.get_both();
| - value moved here
28 | let x = d.get_val1();
| ^ value used here after move
|
= note: move occurs because `d` has type `MyData`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `d`
--> src/main.rs:29:13
|
28 | let x = d.get_val1();
| - value moved here
29 | let y = d.get_val2();
| ^ value used here after move
|
= note: move occurs because `d` has type `MyData`, which does not implement the `Copy` trait
I expect that let x = d.get_val1(); won't cause an error. In my understanding of ownership in Rust, I did not move the value, since I'm calling a method of MyData and want to work with the value.
Why does Rust move the value and to whom?

Understanding lifetime of borrowed variables when stacking function calls [duplicate]

This question already has answers here:
How do I pass modified string parameters?
(2 answers)
Return local String as a slice (&str)
(7 answers)
Closed 4 years ago.
I can't seem to find anything about lifetimes for my specific situation. The search function has correct lifetimes and works fine, but then the search_case_insensitive function tells me the lifetime is too short, but I don't understand why.
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<(&'a str, i32)> {
//do something
}
pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<(&'a str, i32)> {
return search(&query.to_lowercase(), &contents.to_lowercase());
}
I get:
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:44:43
|
44 | return search(&query.to_lowercase(), &contents.to_lowercase());
| ^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 43:1...
--> src/lib.rs:43:1
|
43 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<(&'a str, i32)> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: consider using a `let` binding to increase its lifetime
I have tried doing things like let c = contents and using this new c value instead but get the same issue.

Resources