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

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.

Related

Trouble with Rust Lifetime in Generic function [duplicate]

This question already has answers here:
How to fix lifetime error when function returns a serde Deserialize type?
(2 answers)
Closed 3 years ago.
I have a simple function that I want to make generic, in rust. I am getting a lifetime error. I am still getting the hang of the lifetime side of rust.
The function simply converts 1 struct into another using serde's serialization.
Here is a a rust playground with the full simple scenario.
Code:
pub fn convert<'de: 'a, 'a, T>(from: &'a Left, print: bool) -> (T, &'a Left)
where
T: Deserialize<'de> + std::fmt::Debug {
let serialized = serde_json::to_string(&from);
let serialized = serialized.unwrap();
let deserialized: T;
{
let deserialized_raw = serde_json::from_str(&serialized);
deserialized = deserialized_raw.unwrap();
}
if print {
println!("-------------A-----------------------------------");
println!("serialized = {}", &serialized);
println!("--------------B----------------------------------");
println!("deserialized = {:?}", deserialized);
println!("--------------C----------------------------------");
};
(deserialized, from)
}
Error:
error[E0597]: `serialized` does not live long enough
--> src/main.rs:38:49
|
30 | pub fn convert<'de: 'a, 'a, T>(from: &'a Left, print: bool) -> (T, &'a Left)
| --- lifetime `'de` defined here
...
38 | let deserialized_raw = serde_json::from_str(&serialized);
| ---------------------^^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `serialized` is borrowed for `'de`
...
49 | }
| - `serialized` dropped here while still borrowed
I tried this a few ways with and without lifetimes. I tried adding blocks to see if that changes things with no luck.
Any thoughts on what I am doing wrong?
Edited:
- Added the full compiler error output
To the compiler, lifetime parameters always represent lifetimes that live strictly longer than the function call. However, here you're trying to use a local variable and claim it has lifetime 'de, which is impossible because it's a local variable, thus it has a lifetime shorter than the function call.
In order to mix lifetime parameters in traits with local variables, we must use higher-rank trait bounds. We want T to implement Deserialize<'de> for every lifetime 'de (not just one specific lifetime chosen by the caller). This is written like this (note that we can now elide the 'a lifetime):
pub fn convert<T>(from: &Left, print: bool) -> (T, &Left)
where
T: for<'de> Deserialize<'de> + std::fmt::Debug
{
// no changes here
}

Is it possible to extend the lifetime of a locally borrowed chunk of memory to match a declared lifetime? [duplicate]

This question already has answers here:
How to fix lifetime error when function returns a serde Deserialize type?
(2 answers)
Is there any way to return a reference to a variable created in a function?
(5 answers)
Extend lifetime of variable
(1 answer)
Closed 3 years ago.
I realize this might be a duplicate question, but I'm unable to find a satisfactory answer.
Let's say I borrow something locally and try to use it for something that has a declared lifetime:
extern crate serde;
extern crate serde_json;
use serde::Deserialize;
pub trait Item: {}
fn transform<'de, ITM: Deserialize<'de> + Item>(item_json: String) -> ITM {
let item_typed: ITM = ::serde_json::from_str(&item_json).unwrap();
item_typed
}
This fails with the following error:
error[E0597]: `item_json` does not live long enough
--> src/main.rs:9:50
|
8 | fn transform<'de, ITM: Deserialize<'de> + Item>(item_json: String) -> ITM {
| --- lifetime `'de` defined here
9 | let item_typed: ITM = ::serde_json::from_str(&item_json).unwrap();
| -----------------------^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `item_json` is borrowed for `'de`
10 | item_typed
11 | }
| - `item_json` dropped here while still borrowed
Digging in, from_str wants a reference that lives as long as the lifetime declared for Deserialize
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
where
T: de::Deserialize<'a>, {
from_trait(read::StrRead::new(s))
}
Understandable.
Ok, but how to resolve this?
Changing the signature of the function so it uses a lifetimed &str just kicks the can down the road:
fn transform_str<'de, ITM: Deserialize<'de> + Item + Clone>(item_json: &'de str) -> ITM {
::serde_json::from_str(item_json).unwrap()
}
fn transform<'de, ITM: Deserialize<'de> + Item>(item_json: String) -> ITM {
transform_str(&item_json) //error
}

Why can't I call a mutating function more than once? [duplicate]

This question already has answers here:
Cannot borrow `x` as mutable more than once at a time
(2 answers)
Cannot borrow as mutable more than once at a time in one code - but can in another very similar
(2 answers)
What is the difference between '&self' and '&'a self'?
(1 answer)
Closed 3 years ago.
This is based on my previous question.
I have code that is failing with borrow checker error and I reduced it as much as I could. This is the result:
struct MyStruct<'a> {
s: &'a str,
}
impl<'a> MyStruct<'a> {
fn foo(&'a mut self) {}
}
fn main() {
let mut m = MyStruct { s: "aaa" };
m.foo();
m.foo();
}
It is failing with:
error[E0499]: cannot borrow `m` as mutable more than once at a time
--> src/main-x.rs:13:5
|
12 | m.foo();
| - first mutable borrow occurs here
13 | m.foo();
| ^
| |
| second mutable borrow occurs here
| first borrow later used here
The code was reduced so much that it does nothing useful and could be fixed for example by removing the 'a lifetime from the foo function. But I would like to understand why is the code not ok as it is.
My understanding is that MyStruct contains reference to str of some lifetime 'a and foo can be called with self pointing to MyStruct of the same lifetime. I don't see why is m considered mutably borrowed after the first call to foo.
When you declare foo as
fn foo(&'a mut self) {}
you say the mutable borrow of self has the same lifetime 'a as the embedded string. So it stays borrowed as long as the struct lives. Calling foo is like definitely giving away the ownership of the struct.
You can fix it by declaring foo as
fn foo(&mut self) {}

str::contains works with references but not with the actual value [duplicate]

This question already has answers here:
The trait `FnMut<(char,)>` is not implemented for `String` when trying to split a string
(2 answers)
str::contains doesn't work when the supplied input is a string reference [duplicate]
(1 answer)
Closed 3 years ago.
Consider a function that searches for a pattern in a huge string of lines and returns the lines on which matches are found:
fn search_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
let lowercase_query = query.to_lowercase();
let mut matches: Vec<&str> = Vec::new();
for line in content.lines() {
let lowercase_line = line.to_lowercase();
if lowercase_line.contains(&lowercase_query) {
matches.push(line)
}
}
matches
}
The question I have is around the line if lowercase_line.contains(&lowercase_query). Why is lowercase_query passed as a reference here? If I pass it as a value instead I get the error:
error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String`
--> src/lib.rs:6:27
|
6 | if lowercase_line.contains(lowercase_query) {
| ^^^^^^^^ expected an `FnMut<(char,)>` closure, found `std::string::String`
|
= help: the trait `std::ops::FnMut<(char,)>` is not implemented for `std::string::String`
= note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`
I checked the definition of the contains function:
pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
pat.is_contained_in(self)
}
I don't see anywhere the need for contains to take a reference. Can someone explain this?
Because Pattern is implemented for &'a String but not for String
impl<'a, 'b> Pattern<'a> for &'b String
But I still don't get the relation between the error message when I do pass it by value
Answered by Jmb
If you look at the docs for Pattern you will see that the last documented impl is for FnMut (char) -> bool, which might explain why the compiler chose to show that specific type. It would probably have been better if the compiler had said impl Pattern<'_>

Variable lifetime qs in function [duplicate]

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.

Resources