This code is an inefficient way of producing a unique set of items from an iterator. To accomplish this, I am attempting to use a Vec to keep track of values I've seen. I believe that this Vec needs to be owned by the innermost closure:
fn main() {
let mut seen = vec![];
let items = vec![vec![1i32, 2], vec![3], vec![1]];
let a: Vec<_> = items
.iter()
.flat_map(move |inner_numbers| {
inner_numbers.iter().filter_map(move |&number| {
if !seen.contains(&number) {
seen.push(number);
Some(number)
} else {
None
}
})
})
.collect();
println!("{:?}", a);
}
However, compilation fails with:
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> src/main.rs:8:45
|
2 | let mut seen = vec![];
| -------- captured outer variable
...
8 | inner_numbers.iter().filter_map(move |&number| {
| ^^^^^^^^^^^^^^ cannot move out of captured outer variable in an `FnMut` closure
This is a little surprising, but isn't a bug.
flat_map takes a FnMut as it needs to call the closure multiple times. The code with move on the inner closure fails because that closure is created multiple times, once for each inner_numbers. If I write the closures in explicit form (i.e. a struct that stores the captures and an implementation of one of the closure traits) your code looks (a bit) like
struct OuterClosure {
seen: Vec<i32>
}
struct InnerClosure {
seen: Vec<i32>
}
impl FnMut(&Vec<i32>) -> iter::FilterMap<..., InnerClosure> for OuterClosure {
fn call_mut(&mut self, (inner_numbers,): &Vec<i32>) -> iter::FilterMap<..., InnerClosure> {
let inner = InnerClosure {
seen: self.seen // uh oh! a move out of a &mut pointer
};
inner_numbers.iter().filter_map(inner)
}
}
impl FnMut(&i32) -> Option<i32> for InnerClosure { ... }
Which makes the illegality clearer: attempting to move out of the &mut OuterClosure variable.
Theoretically, just capturing a mutable reference is sufficient, since the seen is only being modified (not moved) inside the closure. However things are too lazy for this to work...
error: lifetime of `seen` is too short to guarantee its contents can be safely reborrowed
--> src/main.rs:9:45
|
9 | inner_numbers.iter().filter_map(|&number| {
| ^^^^^^^^^
|
note: `seen` would have to be valid for the method call at 7:20...
--> src/main.rs:7:21
|
7 | let a: Vec<_> = items.iter()
| _____________________^
8 | | .flat_map(|inner_numbers| {
9 | | inner_numbers.iter().filter_map(|&number| {
10| | if !seen.contains(&number) {
... |
17| | })
18| | .collect();
| |__________________^
note: ...but `seen` is only valid for the lifetime as defined on the body at 8:34
--> src/main.rs:8:35
|
8 | .flat_map(|inner_numbers| {
| ___________________________________^
9 | | inner_numbers.iter().filter_map(|&number| {
10| | if !seen.contains(&number) {
11| | seen.push(number);
... |
16| | })
17| | })
| |_________^
Removing the moves makes the closure captures work like
struct OuterClosure<'a> {
seen: &'a mut Vec<i32>
}
struct InnerClosure<'a> {
seen: &'a mut Vec<i32>
}
impl<'a> FnMut(&Vec<i32>) -> iter::FilterMap<..., InnerClosure<??>> for OuterClosure<'a> {
fn call_mut<'b>(&'b mut self, inner_numbers: &Vec<i32>) -> iter::FilterMap<..., InnerClosure<??>> {
let inner = InnerClosure {
seen: &mut *self.seen // can't move out, so must be a reborrow
};
inner_numbers.iter().filter_map(inner)
}
}
impl<'a> FnMut(&i32) -> Option<i32> for InnerClosure<'a> { ... }
(I've named the &mut self lifetime in this one, for pedagogical purposes.)
This case is definitely more subtle. The FilterMap iterator stores the closure internally, meaning any references in the closure value (that is, any references it captures) have to be valid as long as the FilterMap values are being thrown around, and, for &mut references, any references have to be careful to be non-aliased.
The compiler can't be sure flat_map won't, e.g. store all the returned iterators in a Vec<FilterMap<...>> which would result in a pile of aliased &muts... very bad! I think this specific use of flat_map happens to be safe, but I'm not sure it is in general, and there's certainly functions with the same style of signature as flat_map (e.g. map) would definitely be unsafe. (In fact, replacing flat_map with map in the code gives the Vec situation I just described.)
For the error message: self is effectively (ignoring the struct wrapper) &'b mut (&'a mut Vec<i32>) where 'b is the lifetime of &mut self reference and 'a is the lifetime of the reference in the struct. Moving the inner &mut out is illegal: can't move an affine type like &mut out of a reference (it would work with &Vec<i32>, though), so the only choice is to reborrow. A reborrow is going through the outer reference and so cannot outlive it, that is, the &mut *self.seen reborrow is a &'b mut Vec<i32>, not a &'a mut Vec<i32>.
This makes the inner closure have type InnerClosure<'b>, and hence the call_mut method is trying to return a FilterMap<..., InnerClosure<'b>>. Unfortunately, the FnMut trait defines call_mut as just
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
In particular, there's no connection between the lifetime of the self reference itself and the returned value, and so it is illegal to try to return InnerClosure<'b> which has that link. This is why the compiler is complaining that the lifetime is too short to be able to reborrow.
This is extremely similar to the Iterator::next method, and the code here is failing for basically the same reason that one cannot have an iterator over references into memory that the iterator itself owns. (I imagine a "streaming iterator" (iterators with a link between &mut self and the return value in next) library would be able to provide a flat_map that works with the code nearly written: would need "closure" traits with a similar link.)
Work-arounds include:
the RefCell suggested by Renato Zannon, which allows seen to be borrowed as a shared &. The desugared closure code is basically the same other than changing the &mut Vec<i32> to &Vec<i32>. This change means "reborrow" of the &'b mut &'a RefCell<Vec<i32>> can just be a copy of the &'a ... out of the &mut. It's a literal copy, so the lifetime is retained.
avoiding the laziness of iterators, to avoid returning the inner closure, specifically.collect::<Vec<_>>()ing inside the loop to run through the whole filter_map before returning.
fn main() {
let mut seen = vec![];
let items = vec![vec![1i32, 2], vec![3], vec![1]];
let a: Vec<_> = items
.iter()
.flat_map(|inner_numbers| {
inner_numbers
.iter()
.filter_map(|&number| if !seen.contains(&number) {
seen.push(number);
Some(number)
} else {
None
})
.collect::<Vec<_>>()
.into_iter()
})
.collect();
println!("{:?}", a);
}
I imagine the RefCell version is more efficient.
It seems that the borrow checker is getting confused at the nested closures + mutable borrow. It might be worth filing an issue. Edit: See huon's answer for why this isn't a bug.
As a workaround, it's possible to resort to RefCell here:
use std::cell::RefCell;
fn main() {
let seen = vec![];
let items = vec![vec![1i32, 2], vec![3], vec![1]];
let seen_cell = RefCell::new(seen);
let a: Vec<_> = items
.iter()
.flat_map(|inner_numbers| {
inner_numbers.iter().filter_map(|&number| {
let mut borrowed = seen_cell.borrow_mut();
if !borrowed.contains(&number) {
borrowed.push(number);
Some(number)
} else {
None
}
})
})
.collect();
println!("{:?}", a);
}
I came across this question after I ran into a similar issue, using flat_map and filter_map. I solved it by moving the filter_map outside the flat_map closure.
Using your example:
let a: Vec<_> = items
.iter()
.flat_map(|inner_numbers| inner_numbers.iter())
.filter_map(move |&number| {
if !seen.contains(&number) {
seen.push(number);
Some(number)
} else {
None
}
})
.collect();
Related
So I have a function which looks like this
fn foo() {
let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];
let mut groups: HashMap<u32, String> = HashMap::new();
let mut group = |idx: f32| -> &mut String {
let rounded = (idx / 0.2).floor() as u32;
groups
.entry(rounded)
.or_insert_with(|| format!("{}:", rounded))
};
for item in items.iter() {
group(*item).push_str(&format!(" {}", item))
}
}
and this code does not compile, with the following error:
error: captured variable cannot escape `FnMut` closure body
--> src/main.rs:9:9
|
5 | let mut groups: HashMap<u32, String> = HashMap::new();
| ---------- variable defined here
6 |
7 | let mut group = |idx: f32| -> &mut String {
| - inferred to be a `FnMut` closure
8 | let rounded = (idx / 0.2).floor() as u32;
9 | groups
| ^-----
| |
| _________variable captured here
| |
10 | | .entry(rounded)
11 | | .or_insert_with(|| format!("{}:", rounded))
| |_______________________________________________________^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
Edit
As #Sven Marnach pointed out, the problem here is that I could create 2 mutable references to the same object:
fn foo() {
// ...
let ok = groups(0.1);
let problem = groups(0.1);
}
Original (incorrect)
I think that Rust is telling me, the closure group is mutably capturing the variable groups and then returning a reference to an object owned by groups. So the danger here is that the following code would return a dangling pointer (since groups is dropped when it goes out of scope after foo finishes).
fn foo() -> &String {
/* ... */ return groups(0.1); }
So is there any way to return a reference from a captured mutable HashMap like this?
I think that Rust is telling me, the closure group is mutably capturing the variable groups and then returning a reference to an object owned by groups. So the danger here is that if I were to write:
then I would be returning a dangling pointer (since groups is dropped when it goes out of scope after foo finishes).
No. If that were the case Rust could (and would) warn about that.
The problem is that the lifetimes around function traits are problematic, because they don't have a way to match the lifetime of the result to the lifetime of the function itself.
As a result, rust blanket bans returning any reference to captured data from a closure.
As far as I can tell the solutions are:
don't use a closure, instead pass in groups as an argument into the function (anonymous or named)
use some sort of shared ownership and interior mutability e.g. have the map store and return Rc<RefCell<String>>
desugar the closure to a structure with a method, that way the lifetimes become manageable:
use std::collections::HashMap;
struct F<'a>(&'a mut HashMap<u32, String>);
impl F<'_> {
fn call(&mut self, idx: f32) -> &mut String {
let rounded = (idx / 0.2).floor() as u32;
self.0
.entry(rounded)
.or_insert_with(|| format!("{}:", rounded))
}
}
pub fn foo() {
let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];
let mut groups: HashMap<u32, String> = HashMap::new();
let mut group = F(&mut groups);
for item in items.iter() {
group.call(*item).push_str(&format!(" {}", item))
}
}
Note that the above stores a reference to match the original closure, but in reality I see no reason not to move the hashmap into the wrapper entirely (and the struct can init itself fully without the need for a two-step initialisation as well):
use std::collections::HashMap;
struct F(HashMap<u32, String>);
impl F {
fn new() -> Self { Self(HashMap::new()) }
fn call(&mut self, idx: f32) -> &mut String {
let rounded = (idx / 0.2).floor() as u32;
self.0
.entry(rounded)
.or_insert_with(|| format!("{}:", rounded))
}
}
pub fn foo() {
let items = vec![0.2, 1.5, 0.22, 0.8, 0.7, 2.1];
let mut group = F::new();
for item in items.iter() {
group.call(*item).push_str(&format!(" {}", item))
}
}
Stripped down to the bare essentials, my problematic code looks as follows:
pub struct Item;
impl Item {
/// Partial copy. Not the same as simple assignment.
pub fn copy_from(&mut self, _other: &Item) {
}
}
pub struct Container {
items: Vec<Item>,
}
impl Container {
pub fn copy_from(&mut self, self_idx: usize, other: &Container, other_idx: usize) {
self.items[self_idx].copy_from(&other.items[other_idx]);
}
}
fn main() {
let mut container = Container { items: vec![Item, Item] };
container.copy_from(0, &container, 1);
}
This is of course rejected by the borrow checker:
error[E0502]: cannot borrow `container` as mutable because it is also borrowed as immutable
--> src/main.rs:21:5
|
21 | container.copy_from(0, &container, 1);
| ^^^^^^^^^^---------^^^^----------^^^^
| | | |
| | | immutable borrow occurs here
| | immutable borrow later used by call
| mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
I understand why that happens, but I don't have a good solution.
I've considered adding a dedicated copy_from_self function that callers need to use in cases where self == other:
pub fn copy_from_self(&mut self, to_idx: usize, from_idx: usize) {
if to_idx != from_idx {
unsafe {
let from_item: *const Item = &self.items[from_idx];
self.items[to_idx].copy_from(&*from_item);
}
}
}
But this is un-ergonomic, bloats the API surface, and needs unsafe code inside.
Note that in reality, the internal items data structure is not a simple Vec, so any approach specific to Vec or slice will not work.
Is there an elegant, idiomatic solution to this problem?
If I understand the comments on the question correctly, a general solution seems to be impossible, so this answer is necessarily specific to my actual situation.
As mentioned, the actual data structure is not a Vec. If it were a Vec, we could use split_at_mut to at least implement copy_from_self safely.
But as it happens, my actual data structure is backed by a Vec, so I was able to add a helper function:
/// Returns a pair of mutable references to different items. Useful if you need to pass
/// a reference to one item to a function that takes `&mut self` on another item.
/// Panics if `a == b`.
fn get_mut_2(&mut self, a: usize, b: usize) -> (&mut T, &mut T) {
assert!(a != b);
if a < b {
let (first, second) = self.items.split_at_mut(b);
(&mut first[a], &mut second[0])
} else if a > b {
let (first, second) = self.items.split_at_mut(a);
(&mut second[0], &mut first[b])
} else {
panic!("cannot call get_mut_2 with the same index {} == {}", a, b);
}
}
Now we can implement copy_from_self without unsafe code:
pub fn copy_from_self(&mut self, to_idx: usize, from_idx: usize) {
let (to, from) = self.items.get_mut_2(to_idx, from_idx);
to.unwrap().copy_from(from.unwrap());
}
playground link
I'm having trouble trouble understanding why this code can't compile:
struct Ctx<'a> {
n: u64,
phantom: std::marker::PhantomData<&'a u8>,
}
fn process<'a, 'b: 'a>(ctx: &'b mut Ctx<'a>) {
ctx.n += 1;
}
fn main() {
let mut ctx = Ctx { n: 0, phantom: std::marker::PhantomData };
let ref_ctx = &mut ctx;
process(ref_ctx);
process(ref_ctx);
}
The code is silly, but the error is the same as on the "real" code:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `*ref_ctx` as mutable more than once at a time
--> src/main.rs:14:13
|
13 | process(ref_ctx);
| ------- first mutable borrow occurs here
14 | process(ref_ctx);
| ^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to previous error
If change the process signature to
fn process(ctx: &mut Ctx<'a>) {
Then it compiles, but I need the lifetimes annotations because in practice Ctx contains references that needs to be annotated. I feel like the problem is 'b: 'a because is could be telling that the borrow MUST outlive the references in Ctx, but it feels weird that just calling a function that returns nothing would extend the borrow,
That feels like a beginner's question, but it definitely has me stumped.
edit:
The code above is minimal because I wanted to avoid explaining the whole context (pun intended), but it might be necessary to find a practical solution. Sorry, that's going to be a bit long.
The real Ctx is here, it's struct that wraps a few Varnish C pointers and gets passed to plugins so they can read it and possibly change it, notably manipulating HTTP objects.
An important part of Ctx is the WS contained in it. It's a workspace that can return non-overlapping slices of writable data that will be deleted automatically once a task has been completed. For the compiler, the best we can tell it is "any slice allocated in the WS will survive for as long as the Ctx wil live".
WS can be used in two ways:
either directly by a plugin to request a slice it'll fill in
or by the boilerplate that's currently python-generated to convert Varnish types into C types. Typically, the plugin returns a String and the boilerplate will allocate a WS slice and copy the data for storage. And example of generated code looks like this:
// the C-matching function that needs to exist for the plugin frame work to pick it up
unsafe extern "C" fn vmod_c_unset_hdr(vrt_ctx: * mut varnish_sys::vrt_ctx, arg1: varnish_sys::VCL_STRING) {
// create the rust Ctx from the pointer
let mut _ctx = Ctx::new(vrt_ctx);
// call the plugin function
match vmod::unset_hdr(
&mut _ctx,
&*arg1.into_rust()
).into_result() { // make sure we have a Result by wrapping lone values
Err(ref e) => { _ctx.fail(e); }, // yell if we didn't got and Err
Ok(v) => v.into_vcl(&mut _ctx.ws), // convert the Ok value into a C type, possibly by copying it into the workspace
}
}
Now, so far, the code at the commit I shared works (hurray), but I get into trouble when I try to implement WS_ReserveAll and WS_Release: basically, grab all the free space you can, write what you need, and then tell Varnish how much you used so the rest is reclaimed and can be used by future calls.
Of course, you can only WS_ReserveAll() once before you need to WS_Release so that felt like a perfect job for Rust.
I though I'd create a new type ReservedBuf, by adding this to the code:
impl<'a> struct WS<'a> {
// some lines omitted
pub fn reserve(&'a mut self) -> ReservedBuf<'a> {
let wsp = unsafe { self.raw.as_mut().unwrap() };
assert_eq!(wsp.magic, varnish_sys::WS_MAGIC);
unsafe {
let sz = varnish_sys::WS_ReserveAll(wsp) as usize;
let buf = from_raw_parts_mut(wsp.f as *mut u8, sz as usize);
ReservedBuf {
buf,
wsp: self.raw,
b: wsp.f as *const u8,
len: 0,
}
}
}
}
pub struct ReservedBuf<'a> {
pub buf: &'a mut [u8],
wsp: *mut varnish_sys::ws,
b: *const u8,
len: usize,
}
impl<'a> ReservedBuf<'a> {
pub fn seal(mut self, sz: usize) -> &'a [u8] {
unsafe {
self.len = std::cmp::min(sz, self.buf.as_ptr().add(sz).offset_from(self.b) as usize);
from_raw_parts_mut(self.buf.as_mut_ptr(), self.len)
}
}
}
impl<'a> Drop for ReservedBuf<'a> {
fn drop(&mut self) {
unsafe {
let wsp = self.wsp.as_mut().unwrap();
assert_eq!(wsp.magic, varnish_sys::WS_MAGIC);
varnish_sys::WS_Release(wsp, self.len as u32);
}
}
}
And it appears to work well enough in the limited tests I ran. My plugin function can be this for example:
pub fn ws_reserve<'a, 'b: 'a>(ctx: &'b mut Ctx<'a>, s: &str) -> Result<varnish_sys::VCL_STRING, String> {
let mut rbuf = ctx.ws.reserve();
let s_buf = s.as_bytes();
let vcl_string = rbuf.buf.as_ptr() as *const i8;
rbuf.buf.write(s_buf);
rbuf.buf.write(b" ");
rbuf.buf.write(s_buf);
rbuf.buf.write(b" ");
rbuf.buf.write(s_buf);
rbuf.buf.write(b"\0");
rbuf.seal(0);
Ok(vcl_string)
}
but I need the lifetime annotations, otherwise I get:
--> src/vmod.rs:21:27
|
20 | pub fn ws_reserve(ctx: &mut Ctx, s: &str) -> Result<varnish_sys::VCL_STRING, String> {
| --------
| |
| these two types are declared with different lifetimes...
21 | let mut rbuf = ctx.ws.reserve();
| ^^^^^^^ ...but data from `ctx` flows into `ctx` here
but with the lifetime annotations, the generated-code calling ws_reserve complains:
unsafe extern "C" fn vmod_c_ws_reserve(vrt_ctx: * mut varnish_sys::vrt_ctx, arg1: varnish_sys::VCL_STRING) -> varnish_sys::VCL_STRING {
let mut _ctx = Ctx::new(vrt_ctx);
match vmod::ws_reserve(
&mut _ctx,
&*arg1.into_rust()
).into_result() {
Err(ref e) => { _ctx.fail(e); ptr::null() },
Ok(v) => v.into_vcl(&mut _ctx.ws),
}
}
error[E0499]: cannot borrow `_ctx` as mutable more than once at a time
--> /home/gquintard/project/varnish-rs/vmod_test/target/debug/build/vmod_example-7ff3d57b642f2bfa/out/generated.rs:69:29
|
66 | &mut _ctx,
| --------- first mutable borrow occurs here
...
69 | Err(ref e) => { _ctx.fail(e); ptr::null() },
| ^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
error[E0499]: cannot borrow `_ctx.ws` as mutable more than once at a time
--> /home/gquintard/project/varnish-rs/vmod_test/target/debug/build/vmod_example-7ff3d57b642f2bfa/out/generated.rs:70:33
|
66 | &mut _ctx,
| --------- first mutable borrow occurs here
...
70 | Ok(v) => v.into_vcl(&mut _ctx.ws),
| ^^^^^^^^^^^^
| |
| second mutable borrow occurs here
| first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `vmod_example` due to 2 previous errors
I guess I sort of understand the compiler qualms about the issues, and I might have painted myself into a corner, but I don't see how to solve this.
When we take a closer look at your code we see you write this:
fn process<'a, 'b: 'a>(ctx: &'b mut Ctx<'a>) {
This 'b: 'a reads as 'b outlives 'a. So you're basically writing a method which borrows ctx longer than what ctx is holding internally with 'a.
With that knowledge it seems very logical why this won't work.
let mut ctx = Ctx { n: 0, phantom: std::marker::PhantomData };
let ref_ctx = &mut ctx;
process(ref_ctx); // Borrow ref_ctx for at least the lifetime of `Ctx::<'a>`
process(ref_ctx); // Can't borrow it again since `'a` has not expired yet
What you're trying to do is probably this:
fn process<'a: 'b, 'b>(ctx: &'b mut Ctx<'a>) {
Borrow ctx for the lifetime 'b which is less long than 'a
If that's the case you can just write this:
fn process<'a>(ctx: &mut Ctx<'a>) {
Or even this:
fn process(ctx: &mut Ctx) {
If this isn't what you're trying to do I hope it still makes sense that using the constraint 'b: 'a creates an impossible borrow.
My goal is to make a function (specifically, floodfill) that works independent of the underlying data structure. I tried to do this by passing in two closures: one for querying, that borrows some data immutably, and another for mutating, that borrows the same data mutably.
Example (tested on the Rust Playground):
#![feature(nll)]
fn foo<F, G>(n: i32, closure: &F, mut_closure: &mut G)
where
F: Fn(i32) -> bool,
G: FnMut(i32) -> (),
{
if closure(n) {
mut_closure(n);
}
}
fn main() {
let mut data = 0;
let closure = |n| data == n;
let mut mut_closure = |n| {
data += n;
};
foo(0, &closure, &mut mut_closure);
}
Error: (Debug, Nightly)
error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
--> src/main.rs:16:27
|
15 | let closure = |n| data == n;
| --- ---- previous borrow occurs due to use of `data` in closure
| |
| immutable borrow occurs here
16 | let mut mut_closure = |n| {
| ^^^ mutable borrow occurs here
17 | data += n;
| ---- borrow occurs due to use of `data` in closure
18 | };
19 | foo(0, &closure, &mut mut_closure);
| -------- borrow later used here
I did come up with a solution, but it is very ugly. It works if I combine the closures into one and specify which behavior I want with a parameter:
// #![feature(nll)] not required for this solution
fn foo<F>(n: i32, closure: &mut F)
where
F: FnMut(i32, bool) -> Option<bool>,
{
if closure(n, false).unwrap() {
closure(n, true);
}
}
fn main() {
let mut data = 0;
let mut closure = |n, mutate| {
if mutate {
data += n;
None
} else {
Some(data == n)
}
};
foo(0, &mut closure);
}
Is there any way I can appease the borrow checker without this weird way of combining closures?
The problem is rooted in the fact that there's information that you know that the compiler doesn't.
As mentioned in the comments, you cannot mutate a value while there is a immutable reference to it — otherwise it wouldn't be immutable! It happens that your function needs to access the data immutably once and then mutably, but the compiler doesn't know that from the signature of the function. All it can tell is that the function can call the closures in any order and any number of times, which would include using the immutable data after it's been mutated.
I'm guessing that your original code indeed does that — it probably loops and accesses the "immutable" data after mutating it.
Compile-time borrow checking
One solution is to stop capturing the data in the closure. Instead, "promote" the data to an argument of the function and the closures:
fn foo<T, F, G>(n: i32, data: &mut T, closure: F, mut mut_closure: G)
where
F: Fn(&mut T, i32) -> bool,
G: FnMut(&mut T, i32),
{
if closure(data, n) {
mut_closure(data, n);
}
}
fn main() {
let mut data = 0;
foo(
0,
&mut data,
|data, n| *data == n,
|data, n| *data += n,
);
}
However, I believe that two inter-related closures like that will lead to poor maintainability. Instead, give a name to the concept and make a trait:
trait FillTarget {
fn test(&self, _: i32) -> bool;
fn do_it(&mut self, _: i32);
}
fn foo<F>(n: i32, mut target: F)
where
F: FillTarget,
{
if target.test(n) {
target.do_it(n);
}
}
struct Simple(i32);
impl FillTarget for Simple {
fn test(&self, n: i32) -> bool {
self.0 == n
}
fn do_it(&mut self, n: i32) {
self.0 += n
}
}
fn main() {
let data = Simple(0);
foo(0, data);
}
Run-time borrow checking
Because your code has a temporal need for the mutability (you only need it either mutable or immutable at a given time), you could also switch from compile-time borrow checking to run-time borrow checking. As mentioned in the comments, tools like Cell, RefCell, and Mutex can be used for this:
use std::cell::Cell;
fn main() {
let data = Cell::new(0);
foo(
0,
|n| data.get() == n,
|n| data.set(data.get() + n),
);
}
See also:
When I can use either Cell or RefCell, which should I choose?
Situations where Cell or RefCell is the best choice
Need holistic explanation about Rust's cell and reference counted types
Unsafe programmer-brain-time borrow checking
RefCell and Mutex have a (small) amount of runtime overhead. If you've profiled and determined that to be a bottleneck, you can use unsafe code. The usual unsafe caveats apply: it's now up to you, the fallible programmer, to ensure your code doesn't perform any undefined behavior. This means you have to know what is and is not undefined behavior!
use std::cell::UnsafeCell;
fn main() {
let data = UnsafeCell::new(0);
foo(
0,
|n| unsafe { *data.get() == n },
|n| unsafe { *data.get() += n },
);
}
I, another fallible programmer, believe this code to be safe because there will never be temporal mutable aliasing of data. However, that requires knowledge of what foo does. If it called one closure at the same time as the other, this would most likely become undefined behavior.
Additional comments
There's no reason to take references to your generic closure types for the closures
There's no reason to use -> () on the closure type, you can just omit it.
In the following code (playground):
struct Node {
datum: &'static str,
edges: Vec<Node>,
}
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node {
node.edges.push(Node {
datum: data,
edges: Vec::new(),
});
&node.edges[node.edges.len() - 1] // return just added one
}
fn traverse<F>(root: &Node, callback: &F)
where
F: Fn(&'static str),
{
callback(root.datum);
for node in &root.edges {
traverse(node, callback);
}
}
fn main() {
let mut tree = Node {
datum: "start",
edges: Vec::new(),
};
let lvl1 = add(&mut tree, "level1");
traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
}
I have this error:
error[E0499]: cannot borrow `tree` as mutable more than once at a time
--> src/main.rs:32:19
|
30 | let lvl1 = add(&mut tree, "level1");
| ---- first mutable borrow occurs here
31 |
32 | traverse(&mut tree, &|x| println!("{:}", x)); //I actually don't need mutability here
| ^^^^ second mutable borrow occurs here
33 | }
| - first borrow ends here
My question seems to be very similar to Why does Rust want to borrow a variable as mutable more than once at a time?, but I'm not sure. If so, is there a workaround for this case?
This happens because of how add is defined:
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node
Here it is specified that the lifetime of the resulting reference should be equal to the lifetime of the incoming reference. The only way it is possible (except for unsafe code) is that the resulting reference is somehow derived from the incoming reference, for example, it references some field inside the object the incoming reference points at:
struct X {
a: u32,
b: u32,
}
fn borrow_a<'a>(x: &'a mut X) -> &'a mut u32 {
&mut x.a
}
However, there is no way for the compiler to know what exactly from the incoming structure is borrowed by looking only at the function signature (which, in general, is the only thing it can do when compiling code which uses this function). Therefore, it can't know that the following code is technically correct:
let mut x = X { a: 1, b: 2 };
let a = borrow_a(&mut x);
let b = &mut x.b;
We know that a and b are disjoint because they point at different parts of the structure, but the compiler can't know that because there is nothing in borrow_a's signature which would suggest it (and there can't be, Rust does not support it).
Therefore, the only sensible thing the compiler could do is to consider the whole x to be borrowed until the reference returned by borrow_a() is dropped. Otherwise it would be possible to create two mutable references for the same data, which is a violation of Rust aliasing guarantees.
Note that the following code is correct:
let mut x = X { a: 1, b: 2 };
let a = &mut x.a;
let b = &mut x.b;
Here the compiler can see that a and b never point to the same data, even though they do point inside of the same structure.
There is no workaround for this, and the only solution would be to restructure the code so it doesn't have such borrowing patterns.
The behaviour is logical. Consider what
fn add<'a>(node: &'a mut Node, data: &'static str) -> &'a Node
means.
This says that &mut Node has a lifetime equal to the lifetime of its return value. Because you assign the return value to a name, it lives until the end of the scope. Thus, the mutable borrow also lives that long.
If you can easily discard the return value, do so. You can just drop it on the floor:
let mut tree = Node {
datum: "start",
edges: Vec::new(),
};
add(&mut tree, "level1");
traverse(&mut tree, &|x| println!("{:}", x));
or you can use a lexical scope to constrain it without dropping it completely.
If you want to borrow the return without forcing the mutable borrow to live that long too, you are probably going to have to split the function in two. This is because you are not able to borrow the return value from the mutable borrow to do so.