Restrict lifetime parameter to scope of parameters of a function - rust

Consider the following example
trait MyTrait<'a> {
type N: 'a;
fn func(&'a self) -> Self::N;
}
fn myfunc<'a, T: 'a + MyTrait<'a>>(g: T) {
g.func();
}
fn main() {}
Compiling this small program fails with:
error[E0597]: `g` does not live long enough
--> src/main.rs:8:5
|
8 | g.func();
| ^ borrowed value does not live long enough
9 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 7:1...
--> src/main.rs:7:1
|
7 | fn myfunc<'a, T: 'a + MyTrait<'a>>(g: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As far as I understand, the lifetime parameter 'a is not restricted and could be arbitrary. However, g is a parameter and its lifetime is only the function scope, therefore it does not satisfy the condition of lifetime 'a in the definition of method func.
What I really want is that the associated type N is always restricted to the lifetime of self in MyTrait. That's why I came up with the explicit lifetime parameter 'a of MyTrait. I want function myfunc to work, i.e. 'a should somehow be restricted to the lifetime of of the parameter g.
What is the "correct" way to solve this problem?
A very simple example is
struct MyPtr<'a> {
x: &'a usize,
}
struct MyStruct {
data: Vec<usize>,
}
impl<'a> MyTrait<'a> for MyStruct {
type N = MyPtr<'a>;
fn func(&'a self) -> Self::N {
MyPtr { x: &self.data[0] }
}
}
Note that this is extremely simplified, of course. The idea is that N always contains a reference to something contained in MyTrait and should therefore never outlive MyTrait.

What you want is not to bind a generic lifetime, but to allow "any" lifetime:
fn myfunc<T: for<'a> MyTrait<'a>>(g: T) {
g.func();
}
Fully working example in the playground.
The best source for an explanation is How does for<> syntax differ from a regular lifetime bound?.

Related

What difference between Self and the struct name produces this error?

This code works fine:
#[derive(Debug)]
struct Foo<'a> {
data: Vec<&'a str>,
}
impl Foo<'_> {
fn bar_func(arg1: &str) {
let f = Foo { data : arg1.split_whitespace().collect() };
println!("{:?}", f.data);
}
}
fn main() {
Foo::bar_func(&"hey split me!");
}
But if I change Foo for Self when creating the struct:
fn bar_func(arg1: &str) {
let f = Self { data : arg1.split_whitespace().collect() };
println!("{:?}", f.data);
}
I get a lifetime error:
error: lifetime may not live long enough
--> src/main.rs:9:31
|
6 | impl Foo<'_> {
| -- lifetime `'2` appears in the `impl`'s self type
7 |
8 | fn bar_func(arg1: &str) {
| - let's call the lifetime of this reference `'1`
9 | let f = Self { data : arg1.split_whitespace().collect() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
|
= note: requirement occurs because of the type `SplitWhitespace<'_>`, which makes the generic argument `'_` invariant
= note: the struct `SplitWhitespace<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
Until now I assumed that these would be interchangeable, and I tend to use Self most of the time, so you can imagine, this got me stuck for a very long time, and it was just luck how I found that it worked fine with the struct name when trying to isolate the problem in a minimal example.
Are they not equivalent, and if so, what is the difference?
They're not the same.
Foo is Foo<'_>, with inferred (or elided) lifetime, and that means we can use the lifetime of arg1.
Self, however, keeps generic parameters of the current type, and that includes lifetimes. Thus, the lifetime of Foo with Self refer to the elided '_ in impl Foo<'_>, which is a distinct generic lifetime that is incompatible with arg1's lifetime.

Returning a higher-kinded closure that captures a reference

I'm trying to write a method on a struct that returns a closure. This closure should take a &[u8] with an arbitrary lifetime 'inner as an argument and return the same type, &'inner [u8]. To perform its function, the closure also needs a (shared) reference to a member of the struct &self. Here is my code:
#![warn(clippy::pedantic)]
// placeholder for a large type that I can't afford to clone
struct Opaque(usize);
struct MyStruct<'a>(Vec<&'a Opaque>);
impl<'a> MyStruct<'a> {
pub fn get_func_for_index(
&'a self,
n: usize,
) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
Some(move |i: &[u8]| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
})
}
}
fn main() {
let o1 = Opaque(1);
let o5 = Opaque(5);
let o7 = Opaque(7);
let x = MyStruct(vec![&o1, &o5, &o7]);
let drop_five = x.get_func_for_index(1 /*Opaque(5)*/).unwrap();
let data: Vec<u8> = Vec::from(&b"testing"[..]);
assert_eq!(drop_five(&data[..]), b"ng");
}
If I try to compile it with rustc 1.54.0 (a178d0322 2021-07-26), I get the following error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 14:14...
--> /tmp/lifetimes.rs:14:14
|
14 | Some(move |i: &[u8]| {
| ______________^
15 | | // placeholder: do something with the input using the &Opaque
16 | | &i[opaque.0..]
17 | | })
| |_________^
note: ...so that reference does not outlive borrowed content
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
--> /tmp/lifetimes.rs:6:6
|
6 | impl<'a> MyStruct<'a> {
| ^^
note: ...so that return value is valid for the call
--> /tmp/lifetimes.rs:10:17
|
10 | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> /tmp/lifetimes.rs:7:5
|
7 | / pub fn get_func_for_index(
8 | | &'a self,
9 | | n: usize,
10 | | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| |_______________________________________________________________________^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
It's quite a mouthful and I don't really understand what it's trying to tell me. The first part (first, the lifetime...) makes sense to me, the returned slice must not outlive the closure argument. The second part (but, the lifetime...) however seems weird to me - the + 'a annotation in the method signature refers to the closure itself (because it captures &'a self.foo), not to the value the closure returns.
Is it possible to change the code to model this correctly in rust, or is this construct just not supported at this time?
The problem is here:
Some(move |i: &[u8]| {
Every & has a lifetime on it, explicit or not. What is the lifetime of &[u8]? Clearly it should be "a lifetime chosen by the caller of the closure" (that is, a higher-ranked lifetime). But when the compiler encounters a reference type with a free lifetime parameter, even in the argument list of a closure, it will not assume that the lifetime is higher-ranked. The error message you get about the "anonymous lifetime #1" is the compiler confusedly trying to make a non-higher-ranked lifetime work.
The compiler could, in theory, work "backwards" from the impl Fn in the return type and recognize that the type of the closure needs to have this higher ranked lifetime. It's not quite clever enough to do that in this case, but there is a way to convince it: use a local function with a bounded type parameter to constrain the type of the closure to exactly the bound you want.
pub fn get_func_for_index(
&self, // note 1
n: usize,
) -> Option<impl 'a + for<'inner> Fn(&'inner [u8]) -> &'inner [u8]> { // note 2
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
// helper function to constrain the closure type
fn check<F: Fn(&[u8]) -> &[u8]>(f: F) -> F { // note 3
f
}
Some(check(move |i| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
}))
}
Playground
Please note the following:
&'a self is too conservative for this function because 'a is the lifetime parameter of the reference self contains, not the lifetime for which self is borrowed. In general, you should almost never write &'a self or &'a mut self where 'a is a named lifetime from an outer scope.
I find the + 'a easy to miss at the end of a long trait, especially a Fn trait with a return type. I recommend fronting the lifetime (putting it first) in cases like this to make clear that it relates more to the impl than to the &'inner [u8]. This is a stylistic choice.
Fn(&[u8]) -> &[u8] is actually exactly the same as for<'inner> Fn(&'inner [u8]) -> &'inner [u8], because the elision rules for Fn traits are the same as for regular functions. Either way is fine; I find the shorter version easier to read.
Similar questions
Expected bound lifetime parameter, found concrete lifetime [E0271]
How to declare a higher-ranked lifetime for a closure argument?
Why does this closure require inlining or `dyn`? What does `dyn` do here?

How to declare generic parameters of the same type except for lifetime in rust?

I wrote the code below, but I can't write life time constraint to work and get an error:
use futures::Future;
async fn foo<'a>(a: &'a str) -> &'a str {
let task = get();
f(a, task).await
}
async fn f<T>(v: T, task: impl Future<Output = T>) -> T {
if true {
v
} else {
task.await
}
}
async fn get() -> &'static str {
"foo"
}
error:
error[E0759]: `a` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:3:18
|
3 | async fn foo<'a>(a: &'a str) -> &'a str {
| ^ ------- this data with lifetime `'a`...
| |
| ...is captured here...
4 | let task = get();
5 | f(a, task).await
| - ...and is required to live as long as `'static` here
playground
I think it can be solved if two parameters in function f can have their own lifetimes.
For example,
v: T,
task: S,
T: 'a,
S: 'b,
'b: 'a,
S == T
How to solve this issue?
The same problem can be reproduced with another minimal example, using function interfaces instead of async functions.
fn get() -> impl FnOnce() -> &'static str {
|| "foo"
}
fn foo<'a, T: 'a, F>(_: &'a str, _: F)
where
F: Fn() -> T,
T: FnOnce() -> &'a str,
{
}
let x = "".to_string();
foo(&*x, &get);
error[E0597]: `x` does not live long enough
--> src/main.rs:22:11
|
22 | foo(&*x, &get);
| ------^-------
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
23 | }
| - `x` dropped here while still borrowed
This example allows us to turn get into a function parameter and observe that passing this function imposes a hard constraint for the lifetime 'a to be 'static. Despite the program's best intentions, a function returning a supplier function (or a promise) does not provide co-variance with respect to the output's lifetime. That is, () -> &'static str does not fulfill for<'a> () -> &'a str. Occasionally, the compiler will fallback to suggesting you to stick to the weakest link that is the 'static lifetime, even though this may not be desirable.
Note that the means of representing types which are generic over their lifetimes is quite limited at the moment. These are a form of higher kinded types, which can be specified only to some level of expressiveness via higher ranked trait bounds (and eventually generic associated types, once they are fully implemented and stabilized). In this case, rather than trying to make f work for a kind T<'a> (pseudo-code), it is much better to just make our get generic over the lifetime 'a. Subtyping may then take place at the implementation, as we know that a string literal can fulfill any lifetime.
fn get<'a>() -> impl FnOnce() -> &'a str {
|| "foo"
}
In the async case (Playground):
async fn get<'a>() -> &'a str {
"foo"
}
See also:
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
How can this instance seemingly outlive its own parameter lifetime?
How can we write a generic function for checking Serde serialization and deserialization?

HRTB: Concrete type bounded by a trait containing lifetime type parameter vs bounded by only a lifetime type parameter

This is actually an offshoot of this SO question.
Consider the following code:
trait Trait<'b> {
fn func(&'b self) {}
}
struct Struct {}
impl<'s> Trait<'s> for Struct {}
fn test<'s, T:'s>(t: T)
where
T: Trait<'s>,
{
t.func();
}
It fails, as the compiler sees that t lives for less than the 's and 's is set by the caller (i.e longer than the stack-frame of the func) and traits are invariant over type parameters. However, if I introduce HRTB (Higher Rank Trait Bounds) here, the code compiles:
fn test<T>(t: T)
where
T: for<'s> Trait<'s>,
{
t.func();
}
Now, the trait Trait<'s> is implemented for all possible lifetimes 's represents and accordingly the right one gets picked by the compiler.
But then why the following code fails ?
struct Temp<'a> {
x: &'a i32,
}
fn test<T>(t: T)
where
for<'s> T: 's,
{
}
fn main() {
let d = 1i32;
test(Temp { x: &d });
}
It gives the error:
error[E0597]: `d` does not live long enough
--> src/main.rs:13:20
|
13 | test(Temp { x: &d });
| ---------------^^---
| | |
| | borrowed value does not live long enough
| argument requires that `d` is borrowed for `'static`
14 | }
| - `d` dropped here while still borrowed
What is the difference here? Conceptually, I found the earlier example same as this one. Why it is expected that the concrete type T should only be/contain reference(s) with 'static lifetime(s) ?

How do I return a boxed closure from a method that has a reference to the struct?

I have a structure that contains a value and I want to obtain a function that operates on this value:
struct Returner {
val: i32,
}
impl<'a> Returner {
fn get(&'a self) -> Box<Fn(i32) -> i32> {
Box::new(|x| x + self.val)
}
}
This fails compilation:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:7:18
|
7 | Box::new(|x| x + self.val)
| ^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 5:1...
--> src/main.rs:5:1
|
5 | impl<'a> Returner {
| ^^^^^^^^^^^^^^^^^
= note: ...so that the types are compatible:
expected &&Returner
found &&'a Returner
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<std::ops::Fn(i32) -> i32 + 'static>
found std::boxed::Box<std::ops::Fn(i32) -> i32>
This is because the closure borrows self, which is fine by me, because I don't intend to use the obtained function after the struct is destroyed. From what I've gathered so far, there's two ways to make it possible:
Use the move keyword. I don't want to use it because it will take ownership on the object, and want I to use it after it has returned this function.
Explicitly specify the lifetime of the closure to tell the compiler that it has the same lifetime as the struct it was called from.
I think that 2 is the correct way in my situation, but I've not been able to find out how to specify the closure's lifetime. Is there a direct way to do it or I've got it all wrong and it contradicts Rust lifetime logic?
In general, you can specify the lifetime of a boxed trait object by writing Box<Trait + 'a> and analogously for trait objects behind other kinds of pointers (if it's omitted, it defaults to 'static at least in the case of Box). So in this specific case you want the return type Box<(Fn(i32) -> i32) + 'a>.
However, when you do that you will see another error about self not living long enough. The reason is that (without move) the closure will capture a reference to the local variable self. The solution is to use move. This does not move the Returner object, it moves self which is a reference to the Returner object.
In summary:
struct Returner {
val: i32,
}
impl<'a> Returner {
fn get(&'a self) -> Box<Fn(i32) -> i32 + 'a> {
Box::new(move |x| x + self.val)
}
}
As said in an existing answer:
Add a lifetime that ties the lifetime of self to the lifetime of the returned value.
Move the reference to self into the closure.
Since Rust 1.26, you no longer need to return a boxed closure if you are only returning a single type. Instead, you can use impl Trait:
impl Returner {
fn get<'a>(&'a self) -> impl Fn(i32) -> i32 + 'a {
move |x| x + self.val
}
}
See also:
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
What is the correct way to return an Iterator (or any other trait)?
Returning a closure from a function
Conditionally iterate over one of several possible iterators

Resources