Can't closure return a reference? - rust

fn main() {
let a = String::from("foo");
let f = || &a;
fn_immut(f);
println!("{}", a);
}
fn fn_immut<F>(f: F)
where F: Fn() -> &String
{
println!("calling Fn closure from fn, {}", f());
}
This code can't compile, rustc tells me that I should add a 'static like this:
fn fn_immut<F>(f: F)
where F: Fn() -> &'static String
I tried to do it, but it still doesn't work. And rustc also tells me " this function's return type contains a borrowed value, but there is no value for it to be borrowed from".
My question is: in this code,the closure already captures the reference of variable a in its scope, why does rustc still tell me that "there is no value for it to be borrowed from"?

The key message from the compiler is indeed the lack of a lifetime specifier for the closure returning a string. Since the signature defined by the trait Fn() -> &String does not have any function parameters, there are no values from which the compiler can infer the lifetime of the returned reference.
error[E0106]: missing lifetime specifier
--> src/main.rs:10:16
|
10 | F: Fn() -> &String,
| ^ help: consider giving it a 'static lifetime: `&'static`
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
Adding 'static here does not contribute to solving the problem, because in practice the returned string won't have that lifetime. A new lifetime parameter needs to be introduced in fn_immut, from which can be transferred to F's constraint.
fn fn_immut<'a, F>(f: F)
where
F: Fn() -> &'a String,
You can also return a string slice (&str) instead of &String. The full code:
fn main() {
let a = String::from("foo");
let f = || &*a;
fn_immut(f);
println!("{}", a);
}
fn fn_immut<'a, F>(f: F)
where
F: Fn() -> &'a str,
{
println!("calling Fn closure from fn, {}", f());
}
Playground

Related

Why does Rust closure not follow the lifetime elision rule? [duplicate]

fn main() {
let _ref_in_ref_out = |var: &i64| var;
}
This does not compile:
error: lifetime may not live long enough
--> src/main.rs:2:39
|
2 | let _ref_in_ref_out = |var: &i64| var;
| - - ^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i64
| let's call the lifetime of this reference `'1`
Apparently the compiler infers two different lifetimes (for the argument and the return type), instead of it being the same.
Is it possible to write a closure so that the input lifetime is the same as the output lifetime?
Something like
fn ref_in_ref_out<'a> (var: &'a i64) -> &'a i64 { var }
but as a closure
Lifetime elisions rules do not apply to closures, neither can you explicitly specify lifetimes for closures. There are several ways to make this code work, though.
The easiest solution is to simply omit the type annotations and let the compiler infer everything:
let ref_in_ref_out = |var| var;
let i: i64 = 42;
ref_in_ref_out(&i);
Alternatively, it's actually fine to specify the return type. This compiles:
let _ref_in_ref_out = |var| -> &i64 { var };
Yet another option for the case that your closure does not close over any local variables is to convert it to a function pointer, since lifetime elision rules apply to function pointers:
let ref_in_ref_out: fn(&i64) -> &i64 = |var| var;
And finally the most general solution is to use a helper function to apply a function trait bound to your closure:
fn constrain_closure<F: Fn(&i64) -> &i64>(f: F) -> F {
f
}
let _ref_in_ref_out = constrain_closure(|var| var);
I've just found out a way to solve this issue, but It'd be great if there is an easier solution:
fn infer_lifetime<'a, T: 'a, F: Fn(&'a T) -> &'a T>(f: F) -> F {
f
}
fn main() {
let _ref_in_ref_out = infer_lifetime(|var: &i64| var);
}

What does a static lifetime of a Fn closure type mean?

The following error goes away if I do as rustc tells me to, and change the bound to
where F: Fn() -> () + 'static
pub struct Struct(Box<dyn Fn() -> ()>);
pub fn example<F>(f: F)
where
F: Fn() -> ()
{
Struct(Box::new(|| ())); // ok
Struct(Box::new(f)); // error: The parameter type `F` may not live long eneough
// help: consider adding an explicit lifetime bound: `F: 'static`
}
However, I just don't understand what 'static means here. It doesn't seem to mean that the closure itself lives forever.
At least, it doesn't seem like the closure lives forever. If I have the closure own some data, that data gets dropped at some point, which makes me suspect the closure is dropped:
pub struct Struct(Box<dyn Fn() -> ()>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
/// prints:
/// "got DropMe"
/// "got DropMe"
/// "dropped"
/// "end of program"
pub fn main() {
let d = DropMe;
example(move || {
println!("got {:?}", d);
});
println!("end of program")
}
pub fn example<F>(f: F)
where
F: Fn() -> () + 'static
{
let s = Struct(Box::new(f));
s.0();
s.0();
}
Is 'static an upper bound on the lifetime of the closure rather than a lower bound? 'static makes sense as an upper bound, then example would be saying "give me a function that I can hold on to as long as I want", which in practice can be less than the 'static lifetime.
How can I tell when + 'lifetime adds 'lifetime as an upper bound vs. a lower bound?
The Rustonomicon chapter on subtyping+variance doesn't seem to cover this, and I couldn't find the information I was looking for in the Rust Book or Rust Reference.
T: 'a isn't a constraint on the lifetime of instances of T, but of things that T borrows (if any): that is, all borrows in T must outlive 'a.
Thus F: Trait + 'static requires that any borrows in F be for 'static lifetime (or longer, which doesn't exist), regardless that Trait in this case is Fn() -> ().
In your case, the closure takes ownership of d (and borrows the &'static str literal); hence it satisfies F: 'static. But if instead of move || ... the closure merely borrowed d with || ..., then it would not be able to satisfy 'static (as the lifetime of the borrow of d cannot exceed the scope of the call to main).
Yes, 'static is an upper bound.
In fact, all lifetimes constraints are upper bounds. For the callee (that is, whom uses the lifetime).
For the caller (i.e. the provider of the lifetime), on the other hand, they're usually lower bounds: give me something that lives at least as 'static (of course, nothing lives more than 'static, so it actually means "give me something 'static". But it does matter when talking about other lifetimes).
Variance is about changing the caller's respect regarding the lifetime: whether it can pass a longer lifetime (covariance, i.e. a lower bound), a shorter lifetime (contravariance, i.e. an upper bound), or only exactly this lifetime (invariance).
The 'static variant is not obligatory. It seems like it's just what compiler proposes by default.
The two edits below should hopefully illustrate the capture of lifetime of variables from environment.
//rustc 1.62.1 (e092d0b6b)
pub struct Struct<'a>(Box<dyn Fn() -> () + 'a>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
pub fn main() {
let d = DropMe;
let x = 43;
example(move || {
println!("got {:?} {:?}", d, &x);
});
println!("end of program")
}
pub fn example<'env_borrow, F>(f: F)
where
F: Fn() -> () + 'env_borrow
{
let s = Struct(Box::new(f));
s.0();
s.0();
// drop(s);
println!("end of higher bound func");
}
// rustc 1.62.1 (e092d0b6b)
pub struct Struct<'a>(Box<dyn FnMut() -> () + 'a>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
pub fn main() {
let d = DropMe;
let mut x = 43;
let y = &mut x;
let closure_boxed = example(move || {
println!("got {:?} , *&mut x = {:?}", d, y);
*y += 1;
});
// drop(x);
// ^ ERROR: try uncomment
drop(closure_boxed);
// ^ ERROR: try comment
println!("x is now {}", x);
println!("end of program")
}
pub fn example<'env_borrow, F>(f: F) -> Struct<'env_borrow>
where
F: FnMut() -> () + 'env_borrow
{
let mut s = Struct(Box::new(f));
s.0();
s.0();
s
}
I believe the issue is that Struct has no generic lifetime parameters, which means the lifetime of one of its instances has no name. The function an instance of Struct boxes (obviously) has to live as long as the instance, but the only way to guarantee that to the compiler is to give it a named lifetime. But because Struct has no lifetime parameters, the only named lifetime that is guaranteed to last sufficient long is 'static.
To fix this, you can give Struct a generic lifetime parameter and then constrain the boxed function to also only live that long.
pub struct Struct<'a>(Box<dyn 'a + Fn() -> ()>);
pub fn example<F>(f: F)
where
F: Fn() -> (),
{
Struct(Box::new(|| ())); // ok
Struct(Box::new(f)); // also ok; 'a will be inferred from the lifetime of f
}

Borrow checker issue trying to pass a function pointer as paramerter

I am seeking help to understand why the borrow checker fails for the following minimal non-working example, and I would be very happy to learn how to correctly implement what I was trying to do:
use std::collections::HashSet;
struct Foo {
data: HashSet<usize>
}
impl Foo {
fn test<'a, F, T>(&mut self, _operation: F) -> ()
where F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item=&'a usize>
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo { data: HashSet::new() }
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
My main source of confusion is that, if I replace the call to _operation with HashSet::intersection, the code compiles. I thought that the type of the parameter _operation would allow me to pass both HashSet::intersection and HashSet::union as operations here.
For the record, this is the error I receive:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 8:23...
--> src\main.rs:8:23
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the method body at 8:13...
--> src\main.rs:8:13
|
8 | fn test<'a, F, T>(&mut self, _operation: F) -> ()
| ^^
note: ...so that reference does not outlive borrowed content
--> src\main.rs:13:32
|
13 | self.data = _operation(&self.data, &update).copied().collect();
| ^^^^^^^^^^
For more information about this error, try `rustc --explain E0495`.
error: could not compile `aoc06` due to previous error
The issue, as the (albeit cryptic) compiler message suggests, is that there is a lifetime mismatch: _operation expects HashSet references that live as long as 'a, but &self.data has a lifetime 'b, the elided lifetime of &mut self, and &update has a different lifetime that lasts the duration of the test function body.
To resolve this issue, we must specify that the function type F takes in HashMap references of arbitrary lifetimes, not just the specific lifetime 'a -- this lets the compiler infer the appropriate lifetime when _operation is invoked. This is why we need Higher-Rank Trait Bounds (HRTBs):
fn test<F, T>(&mut self, _operation: F) -> ()
where F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
However, this raises another issue. How do we apply the higher-ranked lifetime 'a to the type parameter T? Unfortunately Rust does not support higher-kinded types, but we can get away with "abstracting" out the the function type F and the higher-kinded type T to a trait and an associated type on said trait.
trait Operation<'a, T: 'a> {
type Output: Iterator<Item = &'a T>;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output;
}
The Operation trait represents an operation on two HashSets that returns an iterator over references, equivalent to the functions HashSet::union, HashSet::intersection, and the like. We can achieve this using the following impl, which ensures that HashSet::intersection and the like implement Operation:
impl<'a, T: 'a, I, F> Operation<'a, T> for F
where
I: Iterator<Item = &'a T>,
F: FnOnce(&'a HashSet<T>, &'a HashSet<T>) -> I,
{
type Output = I;
fn operate(self, a: &'a HashSet<T>, b: &'a HashSet<T>) -> Self::Output {
self(a, b)
}
}
We can then use a HRTB on the Operation trait instead, which does not require any nested higher-kinded types:
fn test(&mut self, _operation: impl for<'a> Operation<'a, usize>) -> () {
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.operate(&self.data, &update).copied().collect();
println!("{:?}", self.data);
}
Playground
The arguments you are passing do not match the lifetime which you declared the Fn bound with.
fn test<'a, F, T>(&mut self, _operation: F) -> ()
'a is some arbitrary lifetime that may be specified by the caller,
F: Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
which must be adequate for the references given to _operation,
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation(&self.data, &update).copied().collect();
but here you pass in a borrow from self (whose lifetime is not specified to outlive 'a) and a borrow from update (which is a local variable, which cannot outlive 'a).
In order to correctly write this, you need to specify that _operation may be called with any lifetime (which thus includes the lifetimes of borrows of local variables). That's simple, by itself:
fn test<F, T>(&mut self, _operation: F) -> ()
where
F: for<'a> Fn(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
Note that 'a is no longer a lifetime parameter of test. Instead it's part of the bound on F: you can read this for<'a> notation as “for any lifetime, which we will call 'a, F may be called as a function with references &'a ...”.
However, this isn't actually a solution, because you also have T: Iterator<Item = &'a usize>, which uses 'a again. It isn't currently possible to write a where clause that expresses this relationship, particularly as even without the item being a reference, the iterator will be borrowing the &'a HashSets.
This is an unfortunate limitation of current Rust — it also comes up in trying to write a function that takes an async function which borrows an input (which is structurally the same as your situation, with Future in place of Iterator). However, there is a workaround: you can define a trait for the function, which has a single lifetime parameter that links everything together. (This doesn't impose any extra work on the caller of your function, because the trait is blanket implemented for all suitable functions.)
Here's your code with such a trait added and the needed modifications to fn test():
use std::collections::HashSet;
trait IteratorCallback<'a> {
type Output: Iterator<Item = &'a usize> + 'a;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> Self::Output;
}
impl<'a, F, T> IteratorCallback<'a> for F
where
F: FnOnce(&'a HashSet<usize>, &'a HashSet<usize>) -> T,
T: Iterator<Item = &'a usize> + 'a,
{
type Output = T;
fn call(self, a: &'a HashSet<usize>, b: &'a HashSet<usize>) -> T {
// Delegate to FnOnce
self(a, b)
}
}
struct Foo {
data: HashSet<usize>,
}
impl Foo {
fn test<F>(&mut self, _operation: F) -> ()
where
F: for<'a> IteratorCallback<'a>,
{
let update: HashSet<usize> = vec![4, 2, 9].into_iter().collect();
self.data = _operation.call(&self.data, &update).copied().collect();
}
fn new() -> Self {
Foo {
data: HashSet::new(),
}
}
}
fn main() {
let mut foo: Foo = Foo::new();
foo.test(HashSet::intersection);
}
Note: I changed the function bound to FnOnce because that's more permissive than Fn and all you need in this case, but the same technique will work with Fn as long as you change fn call(self, to fn call(&self,.
Credit: I used this Reddit comment by user Lej77 as an example to work from for the trait technique.

How can I return an impl Iterator that has multiple lifetimes?

I'm having trouble with lifetimes on an impl trait. I'm trying to get the following code to work:
struct Foo<'op, Input> {
op: Box<dyn Fn(Input) -> i32 + 'op>,
}
impl<'op, Input> Foo<'op, Input> {
fn new<Op>(op: Op) -> Foo<'op, Input>
where
Op: Fn(Input) -> i32 + 'op,
{
Foo { op: Box::new(op) }
}
fn apply<'input_iter, InputIter>(
self,
input_iter: InputIter,
) -> impl Iterator<Item = i32> + 'op + 'input_iter
where
InputIter: IntoIterator<Item = Input> + 'input_iter,
{
input_iter.into_iter().map(move |input| (self.op)(input))
}
}
(Playground)
This is giving me the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/lib.rs:20:36
|
20 | input_iter.into_iter().map(move |input| (self.op)(input))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'op as defined on the impl at 5:6...
--> src/lib.rs:5:6
|
5 | impl<'op, Input> Foo<'op, Input> {
| ^^^
= note: ...so that the types are compatible:
expected Foo<'_, _>
found Foo<'op, _>
note: but, the lifetime must be valid for the lifetime 'input_iter as defined on the method body at 13:14...
--> src/lib.rs:13:14
|
13 | fn apply<'input_iter, InputIter>(
| ^^^^^^^^^^^
note: ...so that return value is valid for the call
--> src/lib.rs:16:10
|
16 | ) -> impl Iterator<Item = i32> + 'op + 'input_iter
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here's my understanding of the lifetimes involved. Foo owns an op, which is a closure that may have a reference in it somewhere, so it may have a bound lifetime. This is denoted by 'op, and Foo is constrained such that it can't outlive it. So far, so good.
In apply(), the idea is that we want to consume an input_iter and self and return an iterator of each element in input_iter mapped using self.op. input_iterator may also contain references, so it may have its own lifetime bounds, denoted by 'input_iter.
What I want is to return an iterator that takes ownership of both self and input_iter. In doing so, it would have to take on both of their lifetime parameters to ensure that it didn't outlast either the input_iter references or the op references. I thought impl Iterator<Item = i32> + 'op + 'input_iter would accomplish this, but I seem to have taken a wrong turn somewhere.
It's also weird that it's complaining about the closure. I understand that the closure can't outlive 'op, because it takes ownership of the operator and its references. That makes perfect sense. What I don't understand is why it needs to live as long as 'input_iter. The closure and the input iterator shouldn't care about each other at all; the only thing connecting them is that they both have the same owner (the output iterator).
What am I missing here?
Lifetime parameters don't always represent the exact lifetime of an object (or of a borrow). Let's consider this example:
fn main() {
let a = 3;
let b = 5;
let c = min(&a, &b);
println!("{:?}", c);
}
fn min<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
if a < b {
a
} else {
b
}
}
Here, min takes two reference arguments with the same lifetime. However, we call it with borrows of different variables, which have different lifetimes. So why does this code compile?
The answer is subtyping and variance. Larger lifetimes are subtypes of shorter lifetimes; conversely, shorter lifetimes are supertypes of larger lifetimes. In the example above, the compiler has to find one lifetime that is compatible with both input lifetimes. The compiler does this by finding the common supertype of both lifetimes (i.e. the shortest lifetime that contains both). This is called unification.
Back to your problem. It seems like the compiler doesn't handle multiple lifetime bounds on impl Trait too well at the moment. However, there's no real need to have two lifetime bounds: one is enough. The compiler will shorten this single lifetime if necessary to satisfy the requirements of both self and input_iter.
struct Foo<'op, Input> {
op: Box<dyn Fn(Input) -> i32 + 'op>,
}
impl<'op, Input> Foo<'op, Input> {
fn new<Op>(op: Op) -> Foo<'op, Input>
where
Op: Fn(Input) -> i32 + 'op,
{
Foo { op: Box::new(op) }
}
fn apply<InputIter>(
self,
input_iter: InputIter,
) -> impl Iterator<Item = i32> + 'op
where
Input: 'op,
InputIter: IntoIterator<Item = Input> + 'op,
{
input_iter.into_iter().map(move |input| (self.op)(input))
}
}
fn main() {
let y = 1;
let foo = Foo::new(|x| x as i32 + y);
let s = "abc".to_string();
let sr: &str = &*s;
let bar = sr.chars();
let baz: Vec<_> = foo.apply(bar).collect();
println!("{:?}", baz);
}
Try moving the lines in main around to convince yourself that the lifetimes are indeed unified.
At this point, calling the lifetime 'op is somewhat misleading; it might as well be called 'a.

Identity closure on references

I have a function that takes a closure. The closure takes a reference and returns any type K:
fn take_closure<T, K>(f: impl FnMut(&T)->K) {/*...*/}
If I call take_closure with an identity function, i.e.
struct S;
fn test() {
take_closure(|s: &S| s)
}
the compiler complains:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
I think the problem is that K is one single type, but the closure must work for all lifetimes so that the closure's result must be something like K + '(lifetime of closure parameter).
Specifying that K lives as long as &T does not really help:
fn take_closure<'a, T: 'a + Default, K: 'a>(mut f: impl FnMut(&'a T)->K) {
let t = Default::default();
f(&t); // error[E0597]: `t` does not live long enough
}
Thus, I tried higher-rank trait bounds:
fn take_closure<F, T: Default, K>(mut f: F)
where for<'a> F: FnMut(&'a T)->K // <- (how) can I specify that K: 'a
{
let t = Default::default();
f(&t);
}
#[derive(Default)]
struct S;
fn test() {
take_closure(|s: &S| s) // error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
}
Is there a way to specify take_closure accordingly? I.e. can I somehow specify that for<'a>, my FnMut(&'a T) produces a K satisfying lifetime 'a?
You can specify when you declare your function that K must not outlive &T:
fn take_closure<'a, T: 'a, K: 'a>(f: impl FnMut(&'a T)->K) {}
struct S;
fn test() {
take_closure(|s: &S| s)
}

Resources