The ability to interchangeably borrow to deal with structural problems
I am having problems with a struct that I am passing as a mutable link. The problem only occurs when the structure is defined to hold the link.
struct Global<'a> { one: i32, two: &'a i32 } fn do_task<'a, F, R: 'a>(global: &'a mut Global<'a>, task: F) where F: Fn(&'a mut Global<'a>) -> &'a R { let result = task(global); } fn one<'a>(global: &'a mut Global<'a>) -> &'a i32 { &global.one } fn two<'a>(global: &'a mut Global<'a>) -> &'a i32 { global.two } fn main() { let number = 2; let mut global = Global { one: 1, two: &number }; do_task(&mut global, one); do_task(&mut global, two); }
The borrow controller complains about the following:
error: cannot borrow `global` as mutable more than once at a time do_task(&mut global, two); ^~~~~~ note: previous borrow of `global` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `global` until the borrow ends do_task(&mut global, one); ^~~~~~ note: previous borrow ends here fn main() { ... } ^
But if I change the code so that the field is two
not a link like in the following example, it passes.
struct Global { one: i32, two: i32, } fn do_task<'a, F, R: 'a>(global: &'a mut Global, task: F) where F: Fn(&'a mut Global) -> &'a R { let result = task(global); } fn one<'a>(global: &'a mut Global) -> &'a i32 { &global.one } fn two<'a>(global: &'a mut Global) -> &'a i32 { &global.two } fn main() { let mut global = Global { one: 1, two: 2 }; do_task(&mut global, one); do_task(&mut global, two); }
I tried to surround the function calls with a do_task
different scope, but it had no effect.
Why does the link refer to mutable borrowing to the end of main, and is there any way to do this?
I use rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)
source to share
The problem is that you accidentally associated the shared lifetime parameter Global
s with the lifetime of the modified borrow.
If you require &'a mut Global<'a>
, this means that the length of the volatile borrowing Global
must be the same as the reference in two
-thus, the full length of existence Global
. So you've done what you think you would consider an ongoing loan Global
when you write &mut global
. I'll write it this way in syntax that is not valid, but gets a dot in:
fn main() { 'a: { let number: i32 + 'a = 2; let mut global: Global<'a> = Global { one: 1, two: &'a number, }; do_task(&mut global: &'a mut Global<'a>, one); do_task(&mut global: &'a mut Global<'a>, two); } }
Everyone &mut global
borrows Global
until the end of the block 'a
, so the second collides with the first.
You want to separate these two lifetimes. The function should, and not use a specific lifetime, bind the lifetime parameter: F: for<'b> Fn(&'b mut Global) -> &'b R
read it as follows: " F
should, given an arbitrary lifetime 'b
, implement Fn(&'b mut Global) -> &'b R
". The actual functions are then written with the lifetime until parameter Global
, so it can be outputted as another arbitrary lifetime, for example:
fn one<'a>(global: &'a mut Global) -> &'a i32 { &global.one }
struct Global<'a> { one: i32, two: &'a i32 } fn do_task<F, R>(global: &mut Global, task: F) where F: for<'a> Fn(&'a mut Global) -> &'a R { let result = task(global); } fn one<'a>(global: &'a mut Global) -> &'a i32 { &global.one } fn two<'a>(global: &'a mut Global) -> &'a i32 { global.two } fn main() { let number = 2; let mut global = Global { one: 1, two: &number }; do_task(&mut global, one); do_task(&mut global, two); }
source to share