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
}
Related
I have started to learn Rust. Currently I'm trying to learn how to properly use lifetime annotations and think I have understood the basics quite well. However, I have on several occasions encountered the following structure:
fn<'a> foo(a: &'a str, ...) -> &str + 'a
The str is not relevant it can be any type really, my question is specifically what &str + 'a mean (I might not be using it correctly, which is why I'm asking about it) as opposed to &'a str. As a real world example I have encountered it in this tutorial for async rust where they write:
fn foo_expanded<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a
I'm speculating that it might have to do with that Future is a trait and not a type, but I have been unable to verify it in any official documentation and have not found any source on what the syntax mean.
First of all, the syntax shown in your post is not allowed.
fn<'a> foo(a: &'a str, ...) -> &str + 'a
There are two reasons:
lifetime generics must be specified after the function name.
the displayed way of specifying return lifetimes is allowed only for traits, not complete types.
Otherwise you would get one of the two following errors:
error[E0178]: expected a path on the left-hand side of `+`, not `&str`
--> ./ex_056.rs:11:43
|
11 | fn _get<'a>(ms: &'a MyStruct, s: &str) -> &str + 'a {
| ^^^^^^^^^ help: try adding parentheses: `&(str + 'a)`
or
error[E0404]: expected trait, found builtin type `str`
--> ./ex_056.rs:15:31
|
15 | fn _get2<'a>(s: &'a str) -> &(str + 'a) {
| ^^^ not a trait
Thus it's not valid.
As a crude guess, I imagine that you have been misled by not a complete type but just a trait object. Since such a notation was allowed in 2015 but now it is deprecated, as you can see in the following warning:
warning: trait objects without an explicit `dyn` are deprecated
--> ./ex_056.rs:15:31
|
15 | fn _get2<'a>(s: &'a str) -> &(str + 'a) {
| ^^^^^^^^ help: use `dyn`: `dyn str + 'a`
|
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
Your first example (&str + 'a) is not valid. The + 'a notation can only be applied to a trait.
Your second example: impl Future<Output = u8> + 'a means that foo_expanded returns some unknown type that implements the trait Future<Output = u8> and that this unknown type may contain references with the 'a lifetime. Therefore you won't be able to use the returned value once the 'a lifetime expires.
Consider the following Rust program:
#![feature(generic_associated_types)]
pub trait Func {
type Input<'a>;
type Output;
fn call(self, input: Self::Input<'_>) -> Self::Output;
}
fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
where F: 'static + Func<Input<'cx> = &'cx u8, Output = u8>,
{
let input = &*ctx;
let out = f.call(input);
*ctx = out;
}
I've used #![feature(generic_associated_types)], but I think the question I'm asking is still relevant if you move 'a from Func::Input to Func and use a higher-rank trait bound on invoke.
This code errors, but I don't think it's unsound:
error[E0506]: cannot assign to `*ctx` because it is borrowed
--> src/lib.rs:15:5
|
10 | fn invoke<'cx, F>(f: F, ctx: &'cx mut u8)
| --- lifetime `'cx` defined here
...
13 | let input = &*ctx;
| ----- borrow of `*ctx` occurs here
14 | let out = f.call(input);
| ------------- argument requires that `*ctx` is borrowed for `'cx`
15 | *ctx = out;
| ^^^^^^^^^^ assignment to borrowed `*ctx` occurs here
First ctx is reborrowed as input, which is passed to f.call and then never used again. f.call returns a value that does not contain any lifetimes (u8: 'static), so there is no connection between out and ctx.
Likewise, the type of f contains no lifetimes (F: 'static), so it cannot hold a reference with lifetime 'cx. Furthermore, the lifetime 'cx cannot be safely coerced to 'static inside call, so there's no way to "smuggle out" a reference with that lifetime that's accessible beyond the invocation of f.call. Therefore, I don't see how anything can alias ctx, and I think assigning to it in the last line should be sound.
Am I missing something? Would accepting this code be unsound? If not, why does Rust fail to take advantage of 'static bounds in this way?
The lifetime 'cx could be 'static meaning input can be smuggled elsewhere and be invalidated by *ctx = out.
There's no way to constrain that a lifetime is strictly less than another, so I don't think adding a "broader" lifetime constraint to a generic type is even considered by the borrow checker.
The code as written is unsound. The lifetime bound of 'static on F is completely irrelevant, because the lifetime bounds of F::Input and F are two distinct lifetimes, and it's the associated type's lifetime that's causing the error. By declaring F::Input<'ctx> = &'ctx u8, you are declaring that the immutable borrow lives the length of the mutable one, making the mutable reference unsafe to use.
As #Stargateur mentioned, the thing that can make this work are Higher Ranked Trait bounds:
fn invoke<F>(f: F, ctx: &mut u8)
where F: for<'ctx> Func<Input<'ctx> = &'ctx u8, Output = u8>,
{
let input = ctx;
let out = f.call(input);
*input = out;
}
Playground
That is, instead of declaring that the function call is valid for some specific lifetime 'ctx it is valid for all lifetime's 'ctx. This way, the compiler can freely pick an appropriate lifetime for the reborrow to make this work.
As a side note, you might think that using two specific lifetimes in the function definition would be able to work, but any attempt to do so results in the compiler failing to choose the appropriate lifetime that makes things work.
This question already has an answer here:
Why can't I assign one dereference of a reference of a reference to another when the outer lifetimes differ?
(1 answer)
Closed 2 years ago.
I'm not able to borrow where I thought I could. I reduced the problem down to this case:
struct A<'a> {
borrow: &'a mut u8,
}
fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
a.borrow = a.borrow;
}
error[E0623]: lifetime mismatch
--> src/lib.rs:6:16
|
5 | fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
| -------------
| |
| these two types are declared with different lifetimes...
6 | a.borrow = a.borrow;
| ^^^^^^^^ ...but data from `a` flows into `a` here
It seems that a.borrow has the intersection of 'b and 'c and therefore cannot be guaranteed to still have the lifetime 'c.
I don't have any real problem with this and can work around it by making both lifetimes the same, but why this does not borrow check?
It seems structs are unimportant to showing this issue and double borrows show it easily.
I have three functions which are pretty similar, but I would have trouble knowing which one compiles and which error the non-compiling ones would give.
The simple generic function:
fn only_borrow<T>(a: &mut T) {
*a = *a;
}
results in the error:
error[E0507]: cannot move out of `*a` which is behind a mutable reference
--> src/lib.rs:2:10
|
2 | *a = *a;
| ^^ move occurs because `*a` has type `T`, which does not implement the `Copy` trait
Including an extra level of indirection changes the error
fn only_borrow_double<T>(a: &mut &mut T) {
*a = *a;
}
error[E0623]: lifetime mismatch
--> src/lib.rs:2:10
|
1 | fn only_borrow_double<T>(a: &mut &mut T) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | *a = *a;
| ^^ ...but data from `a` flows into `a` here
Changing away from the implied lifetimes can fix the error:
fn working_double<'b, T>(a: &'b mut &'b mut T) {
*a = *a;
}
You'll have to take a look at your lifetimes 'b and 'c:
&'b mut ... means that you have a reference that is live and valid for a "time" 'b.
A<'c> means that you have an object that is live and valid for 'c.
What you don't have is a specific relation between these two lifetimes. The only thing the compiler can deduce is that since A<'c> is behind a &'b, 'c must outlive 'b, i.e., whenever 'b is valid, so is 'c. Though, crucially, not the other way around.
As you show, the compiler requires 'b and 'c to be the same lifetime.
Why is this?
Let us have a look at our possibilities:
'c and 'b have no relation: It is easy to see that without any relation, the compiler cannot guarantee anything about what is put in A.borrow and as such won't allow it.
'c strictly outlives 'b, i.e., some places 'c is valid 'b is not:
a.borrow = a.borrow becomes a reborrow of a using the 'b lifetime. This answer explains why that happens. However, this means that a is now dependent on the 'b lifetime, which is not valid for some of the time a is valid (since a has the lifetime 'c). This gives the error.
'b strictly outlives 'c: If we had this relation, it might work. The reborrow will be valid, since we get a "bigger" lifetime ('b) than we asked for ('c). However, we already have 'c: 'b inferred by the compiler behind the scenes. Therefore, adding this lifetime means the two lifetimes become equal and we are then back where we started:
struct A<'a> {
borrow: &'a mut u8,
}
/// We add the relation 'b: 'c
fn does_nothing<'b: 'c, 'c>(a: &'b mut A<'c>) {
a.borrow = a.borrow;
}
I can't call Foo::new(words).split_first() in the following code
fn main() {
let words = "Sometimes think, the greatest sorrow than older";
/*
let foo = Foo::new(words);
let first = foo.split_first();
*/
let first = Foo::new(words).split_first();
println!("{}", first);
}
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn split_first(&'a self) -> &'a str {
self.part.split(',').next().expect("Could not find a ','")
}
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
the compiler will give me an error message
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:8:17
|
8 | let first = Foo::new(words).split_first();
| ^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
9 |
10 | println!("{}", first);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
If I bind the value of Foo::new(words) first, then call the split_first method there is no problem.
These two methods of calling should intuitively be the same but are somehow different.
Short answer: remove the 'a lifetime for the self parameter of split_first: fn split_first(&self) -> &'a str (playground).
Long answer:
When you write this code:
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
You are telling the compiler that all Foo instances are related to some lifetime 'a that must be equal to or shorter than the lifetime of the string passed as parameter to Foo::new. That lifetime 'a may be different from the lifetime of each Foo instance. When you then write:
let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)
The compiler infers that the lifetime 'a must be equal to or shorter than the lifetime of words. Barring any other constraints the compiler will use the lifetime of words, which is 'static so it is valid for the full life of the program.
When you add your definition of split_first:
fn split_first(&'a self) -> &'a str
You are adding an extra constraint: you are saying that 'a must also be equal to or shorter than the lifetime of self. The compiler will therefore take the shorter of the lifetime of words and the lifetime of the temporary Foo instance, which is the lifetime of the temporary. #AndersKaseorg's answer explains why that doesn't work.
By removing the 'a lifetime on the self parameter, I am decorrelating 'a from the lifetime of the temporary, so the compiler can again infer that 'a is the lifetime of words, which is long enough for the program to work.
Foo::new(words).split_first() would be interpreted roughly as
let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret
If Rust allowed you to do this, the references in ret would point [edit: would be allowed by the type of split_first to point*] into the now dropped value of tmp. So it’s a good thing that Rust disallows this. If you wrote the equivalent one-liner in C++, you’d silently get undefined behavior.
By writing the let binding yourself, you delay the drop until the end of the scope, thus extending the region where it’s safe to have these references.
For more details, see temporary lifetimes in the Rust Reference.
* Edit: As pointed out by Jmb, the real problem in this particular example is that the type
fn split_first(&'a self) -> &'a str
isn’t specific enough, and a better solution is to refine the type to:
fn split_first<'b>(&'b self) -> &'a str
which can be abbreviated:
fn split_first(&self) -> &'a str
This conveys the intended guarantee that the returned references do not point into the Foo<'a> (only into the string itself).
Why does the Rust compiler emit an error requesting me to constrain the lifetime of the generic parameter in the following structure?
pub struct NewType<'a, T> {
x: &'a T,
}
error[E0309]: the parameter type `T` may not live long enough
--> src/main.rs:2:5
|
2 | x: &'a T,
| ^^^^^^^^
|
= help: consider adding an explicit lifetime bound `T: 'a`...
note: ...so that the reference type `&'a T` does not outlive the data it points at
--> src/main.rs:2:5
|
2 | x: &'a T,
| ^^^^^^^^
I can fix it by changing to
pub struct NewType<'a, T>
where
T: 'a,
{
x: &'a T,
}
I don't understand why it is necessary to add the T: 'a part to the structure definition. I cannot think of a way that the data contained in T could outlive the reference to T. The referent of x needs to outlive the NewType struct and if T is another structure then it would need to meet the same criteria for any references it contains as well.
Is there a specific example where this type of annotation would be necessary or is the Rust compiler just being pedantic?
What T: 'a is saying is that any references in T must outlive 'a.
What this means is that you can't do something like:
let mut o: Option<&str> = Some("foo");
let mut nt = NewType { x: &o }; // o has a reference to &'static str, ok.
{
let s = "bar".to_string();
let o2: Option<&str> = Some(&s);
nt.x = &o2;
}
This would be dangerous because nt would have a dangling reference to s after the block. In this case it would also complain that o2 didn't live long enough either.
I can't think of a way you can have a &'a reference to something which contains shorter-lifetime references, and clearly the compiler knows this in some way (because it's telling you to add the constraint). However I think it's helpful in some ways to spell out the restriction, since it makes the borrow checker less magic: you can reason about it just from just type declarations and function signatures, without having to look at how the fields are defined (often implementation details which aren't in documentation) or how the implementation of a function.