How to define lifetimes properly in closure - rust

impl Rate for Vec<VolumeRanged> {
fn costs<'a, I>(&'a self, issues: &I, period: u32) -> Box<'a + Iterator<Item = f32>>
where
I: IntoIterator<Item=f32>,
I::IntoIter: 'a
{
fn issue_cost(issue: f32, percentage: f32, constant: f32, max: f32) -> f32 {
let r = issue * percentage / 100.0 + constant;
match r.partial_cmp(&max) {
Some(Less) => r,
_ => max
}
}
Box::new(issues.into_iter().map(|i| {
let (pr, nr) = pairwise(self)
.find(|&(_, n)| in_range(&i, &n.range))
.expect("No range found");
issue_cost(
i,
nr.percentage,
pr.map_or(0.0, |x| x.max),
nr.max,
)
}))
}
}
Rust is saying
error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
--> src/main.rs:43:41
|
43 | Box::new(issues.into_iter().map(|i| {
| ^^^ may outlive borrowed value `self`
44 | let (pr, nr) = pairwise(self)
| ---- `self` is borrowed here
|
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword
|
43 | Box::new(issues.into_iter().map(move |i| {
| ^
But I don't want to move ownership into closure. What I want is that returned boxed iterator should live as long as both self and issues. As soon as they go away - it should go away.
I know that this can be solved by passing cloned iterator to issues instead of a reference to it, I don't think that It's needed here.
playground

Add move to the closure. self has type &Vec<VolumeRanged>, so closure will not take ownership of the vector, it will capture the reference to the vector.

Related

How to shorten the lifetime of each item in IntoIterator?

The following is the code:
struct A;
fn f<'a, I>(default_args: I)
where
I: IntoIterator<Item = &'a A>,
{
{
let a1 = A;
let more_args = [&a1];
{
let i = default_args.into_iter();
let i = i.chain(more_args);
// I will use `i` here.
}
}
// I will NOT use `*_args` here.
}
The following is error:
error[E0597]: `a1` does not live long enough
--> src\main.rs:27:26
|
21 | fn f<'a, I>(default_args: I)
| -- lifetime `'a` defined here
...
27 | let more_args = [&a1];
| ^^^ borrowed value does not live long enough
...
30 | let i = i.chain(more_args);
| ------------------ argument requires that `a1` is borrowed for `'a`
...
33 | }
| - `a1` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
I want to get a new default_args_2 from default_args, where the Item inside default_args_2 has a shorter lifetime, as long as it valid inside the function.
You can add a little (almost) noop .map(|v| v):
let i = default_args.into_iter().map(|v| v);
The problem was that the iterator item types have to match, and therefore the chained iterator has to also yield &'a T, which it does not (it yields a shorter lifetime).
Since you don't really need to use the items for 'a, the trick of the noop map() is to insert a subtyping point: Now, the closure inside map() (conceptually) converts the &'a T, to a &'shorter_lifetime T, which is fine since 'a: 'shorter_lifetime, but now the chained iterator can produce the desired item type.
Putting it differently, you cannot convert impl Iterator<Item = &'a T> into impl Iterator<Item = &'shorter_lifetime T> (as it may not be covariant over the lifetime), but you can convert &'a T to &'shorter_lifetime T, and we use that fact because from the outside we have impl Iterator<Item = &'a T>, but inside map()'s closure we got &'a T.

Why is Rust forcing to use move in case of i32 while spawing threads?

I'm new to Rust and looks like I'm seriously missing some concept here.
use std::thread;
fn main() {
let mut children = vec![];
//spawn threads
for i in 0..10 {
let c = thread::spawn(|| {
println!("thread id is {}", i);
});
children.push(c);
}
for j in children {
j.join().expect("thread joining issue");
}
}
It fails with the error:
error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
Since the type of i is i32 , and there are no references involved shouldn't Rust copy the value instead of forcing to move ?
The answer to your original question is that println! borrows its arguments. However, as you pointed out in the comments, even (apparently) moving the integer into the closure still causes a compile error.
For the purposes of this answer, we'll work with this code.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(|| {
let _y = x;
});
}
(playground)
use_closure simulates what thread::spawn does in the original code: it consumes a closure whose type has to be 'static.
Attempting to compile this gives the error
error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
--> src/main.rs:5:17
|
5 | use_closure(|| {
| ^^ may outlive borrowed value `x`
6 | let _y = x;
| - `x` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:5:5
|
5 | / use_closure(|| {
6 | | let _y = x;
7 | | });
| |______^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
5 | use_closure(move || {
| ^^^^^^^
Wait, what?
6 | let _y = x;
| - `x` is borrowed here
Why is x borrowed there? Shouldn't it be a copy? The answer lies in "capture modes" for closures. From the documentation
The compiler prefers to capture a closed-over variable by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally by move. It will pick the first choice of these that allows the closure to compile. The choice is made only with regards to the contents of the closure expression; the compiler does not take into account surrounding code, such as the lifetimes of involved variables.
Precisely because x has a Copy type, the closure itself can compile with a mere immutable borrow. Given an immutable borrow of x (call it bor), we can do our assignment to _y with _y = *bor. This isn't a "move out of data behind a reference" because this is a copy instead of a move.
However, since the closure borrows a local variable, its type won't be 'static, so it won't be usable in use_closure or thread::spawn.
Trying the same code with a type that isn't Copy, it actually works perfectly, since the closure is forced to capture x by moving it.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: Vec<i32> = vec![];
use_closure(|| {
let _y = x;
});
}
(playground)
Of course, as you already know, the solution is to use the move keyword in front of the closure. This forces all captured variables to be moved into the closure. Since the variable won't be borrowed, the closure will have a static type and will be able to be used in use_closure or thread::spawn.
fn use_closure<F: FnOnce() + 'static>(_: F) {}
fn main() {
let x: i32 = 0;
use_closure(move || {
let _y = x;
});
}
(playground)

Chain iterators to references of different lifetimies

I want to build a recursive function for traversing a tree in Rust. The function should always get the next element and an iterator over references to the ancestor elements.
For the iterator over ancestor elements, one could in principle use the chain and once methods. Consider the following simple example, where the tree is jsut a Vec (for the purpose of this demonstration):
fn proceed<'a, I>(mut remaining: Vec<String>, ancestors: I)
where
I: Iterator<Item = &'a String> + Clone,
{
if let Some(next) = remaining.pop() {
let next_ancestors = ancestors.chain(std::iter::once(&next));
proceed(remaining, next_ancestors);
}
}
Playground
This fails to compile because &next has a shorter lifetime than 'a:
error[E0597]: `next` does not live long enough
--> src/lib.rs:6:62
|
1 | fn proceed<'a, I>(mut remaining: Vec<String>, ancestors: I)
| -- lifetime `'a` defined here
...
6 | let next_ancestors = ancestors.chain(std::iter::once(&next));
| --------------------------------^^^^^--
| | |
| | borrowed value does not live long enough
| argument requires that `next` is borrowed for `'a`
7 | proceed(remaining, next_ancestors);
8 | }
| - `next` dropped here while still borrowed
I tried to overcome this by adding an explicit second lifetime 'b: 'a and forcing an explicit reference by something like let next_ref: &'b String = &next, but that yields a (different) error message as well.
One solution I came up with was to call map as follows:
let next_ancestors = ancestors.map(|r| r).chain(std::iter::once(&next));
As pointed out by #trentcl, this doesn't actually solve the problem, as the compiler then gets stuck in an infinite loop when compiling proceed for all the nested Chains when one actually tries to call the function.
The pieces of solution are already around, just to summarize:
As you already know, using map(|r| r) "decouples" the lifetime requirement of ancestors
from the lifetime of &next.
As already stated in the comments, fixing
the infinite recursion is a matter to change ancestors into a trait object.
fn proceed<'a>(mut remaining: Vec<String>, ancestors: &mut dyn Iterator<Item = &'a String>) {
if let Some(next) = remaining.pop() {
let mut next_ancestors = ancestors.map(|r| r).chain(std::iter::once(&next));
proceed(remaining, &mut next_ancestors);
}
}
fn main() {
let v = vec!["a".to_string(), "b".to_string()];
proceed(v, &mut std::iter::empty());
}

Is "this represents potential undefined behavior in your code" error when using a match arm guard a bug in the compiler?

I'm having an issue with a match expression in my code raising a warning when a guard is included. I believe that this warning has to do with the non-lexical lifetimes used by the borrow checker. My function either returns a mutable reference to an item from a collection, or a clone of the whole collection.
#[derive(Debug, Clone)]
enum Value {
Int(i32),
List(Vec<Value>),
}
#[derive(Debug)]
struct Error(&'static str, Value);
fn main() {
let mut value = Value::List(vec![
Value::Int(1),
Value::Int(2),
Value::Int(34),
Value::Int(12),
]);
let y = index_list(&mut value, 2);
let _ = dbg!(y);
}
fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
match *value {
Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
Value::List(_) => Err(Error("index out of range", value.clone())),
_ => Err(Error("tried to index int", value.clone())),
}
}
playground
It compiles and runs, but I get a very ominous looking warning:
warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
--> src/main.rs:25:59
|
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
| -- lifetime `'a` defined here
23 | match *value {
24 | Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
| ------------ ------------------ returning this value requires that `value.0` is borrowed for `'a`
| |
| mutable borrow occurs here
25 | Value::List(_) => Err(Error("index out of range", value.clone())),
| ^^^^^ immutable borrow occurs here
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
--> src/main.rs:26:46
|
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
| -- lifetime `'a` defined here
23 | match *value {
24 | Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
| ------------ ------------------ returning this value requires that `value.0` is borrowed for `'a`
| |
| mutable borrow occurs here
25 | Value::List(_) => Err(Error("index out of range", value.clone())),
26 | _ => Err(Error("tried to index int", value.clone())),
| ^^^^^ immutable borrow occurs here
|
= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future
I don't understand why the Err(value.clone()) line requires that the Ok(&mut ...) borrow still be active, because they are mutually exclusive and both result in a function return. This warning goes away if I remove the guard on the first match arm, but I need that guard to be there. Is this a bug in the NLL system? I never had this issue with the old borrowck. How can I make this better?
This error looks like a limitation of the borrow checker to me, similar to Double mutable borrow error in a loop happens even with NLL on. I can't see how this could lead to a soundness hole, and believe the code is safe.
When using an if statementat instead of match, the warning can be avoided, and the code becomes more readable as well:
fn bar<'a>(
map: &'a mut HashMap<String, Vec<i32>>,
name: &str,
idx: usize,
) -> Result<&'a mut i32, Vec<i32>> {
let value = map.get_mut(name).unwrap();
if idx < value.len() {
Ok(&mut value[idx])
} else {
Err(value.clone())
}
}

Linking the lifetimes of self and a reference in method

I have this piece of code:
#[derive(Debug)]
struct Foo<'a> {
x: &'a i32,
}
impl<'a> Foo<'a> {
fn set(&mut self, r: &'a i32) {
self.x = r;
}
}
fn main() {
let v = 5;
let w = 7;
let mut f = Foo { x: &v };
println!("f is {:?}", f);
f.set(&w);
println!("now f is {:?}", f);
}
My understanding is that in the first borrow of the value of v, the generic lifetime parameter 'a on the struct declaration is filled in with the lifetime of the value of v. This means that the resulting Foo object must not live longer than this 'a lifetime or that the value of v must live at least as long as the Foo object.
In the call to the method set, the lifetime parameter on the impl block is used and the lifetime of the value of w is filled in for 'a in the method signature. &mut self is assigned a different lifetime by the compiler, which is the lifetime of f (the Foo object). If I switched the order of the bindings of w and f in the main function, this would result in an error.
I wondered what would happen if I annotated the &mut self reference with the same lifetime parameter 'a as r in the set method:
impl<'a> Foo<'a> {
fn set(&'a mut self, r: &'a i32) {
self.x = r;
}
}
Which results in the following error:
error[E0502]: cannot borrow `f` as immutable because it is also borrowed as mutable
--> src/main.rs:21:31
|
19 | f.set(&w);
| - mutable borrow occurs here
20 |
21 | println!("now f is {:?}", f);
| ^ immutable borrow occurs here
22 | }
| - mutable borrow ends here
In contrast to the example above, f is still considered mutably borrowed by the time the second println! is called, so it cannot be borrowed simultaneously as immutable.
How did this come to be?
By not leaving off the lifetime annotation the compiler filled one in for me for &mut self in the first example. This happens by the rules of lifetime elision. However by explicitly setting it to 'a in the second example I linked the lifetimes of the value of f and the value of w.
Is f considered borrowed by itself somehow?
And if so, what is the scope of the borrow? Is it min(lifetime of f, lifetime of w) -> lifetime of f?
I assume I haven't fully understood the &mut self reference in the function call yet. I mean, the function returns, but f is still considered to be borrowed.
I am trying to fully understand lifetimes. I am primarily looking for corrective feedback on my understanding of the concepts. I am grateful for every bit of advice and further clarification.
In the call to the method set the lifetime parameter on the impl block is used and the lifetime of the value of w is filled in for 'a in the method signature.
No. The value of the lifetime parameter 'a is fixed at the creation of the Foo struct, and will never change as it is part of its type.
In your case, the compiler actually choses for 'a a value that is compatible with both the lifetimes of v and w. If that was not possible, it would fail, such as in this example:
fn main() {
let v = 5;
let mut f = Foo { x: &v };
println!("f is {:?}", f);
let w = 7;
f.set(&w);
println!("now f is {:?}", f);
}
which outputs:
error[E0597]: `w` does not live long enough
--> src/main.rs:21:1
|
18 | f.set(&w);
| - borrow occurs here
...
21 | }
| ^ `w` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
Exactly because the 'a lifetime imposed by v is not compatible with the shorter lifetime of w.
In the second example, by forcing the lifetime of self to be 'a as well, you are tying the mutable borrow to the lifetime 'a as well, and thus the borrow ends when all items of lifetime 'a goes out of scope, namely v and w.

Resources