I'm making a Combinatory Optimization project to learn Rust and I've got a problem I cannot resolve myself...
I've got 2 functions :
pub fn get_pareto_front_offline<'a>(scheduling_jobs: &'a Vec<Vec<u32>>, costs_vector: &'a Vec<(u32, u32)>) -> Vec<(&'a Vec<u32>, &'a (u32, u32))> {
// ...
}
and
pub fn pareto_approach_offline<'a>(list_of_jobs: &'a mut Vec<Vec<u32>>, neighborhood: &'a mut Vec<Vec<u32>>, costs: &'a Vec<(u32, u32)>) -> Vec<(&'a Vec<u32>, &'a (u32, u32))> {
let pareto_front = get_pareto_front_offline(neighborhood, costs);
loop {
if pareto_front == vec![] {
break;
}
neighborhood.clear();
for front in pareto_front.iter() {
neighborhood.push((front.0).clone());
}
}
pareto_front
}
I've got a problem because the compiler tells me:
cannot borrow '*neighborhood' as mutable because it is also borrowed as immutableat line 15 col 9
cannot borrow '*neighborhood' as mutable because it is also borrowed as immutableat line 19 col 13
In this case the compiler helped you avoid a use-after-free error by not accepting the code. The problem can be reduced to this piece of code:
A Vec has a length and a capacity. If length equals capacity it means that there is no space left in the buffer for a new element. In such a case
push
involves moving all the elements to a new buffer that is big enough to store new data. But this action would invalidate the referencer
becauser
still stores the old and now invalid address of the 3rd vector element. This is exactly the kind of error Rust is trying to prevent with the borrowing rules and the borrow checker.But if you just add new stuff to the vector, it won't change the order of the elements that were already in there. Maybe you could just replace some references to Vec elements with indices which stay the same regardless of where the Vec elements are stored.
Some aside suggestions: Prefer
&[T]
over&Vec<T>
for function arguments. It's more flexible. Also,pareto_front == vec![]
can be replaced withpareto_front.is_empty()
.You're trying to do something fundamentally impossible.
When you call
get_pareto_front_offline
, you pass a re-borrowing ofneighborhood
into that function. This re-borrow must be maintained in order forpareto_front
to remain valid. In other words, as long aspareto_front
exists, the compiler will not allow you to accessneighborhood
in any fashion whatsoever.This is a good thing, because you then proceed to try and clear our
neighborhood
, which would almost certainly invalidatepareto_front
, likely leading to use-after-free and corrupting your program's state.It's not clear what it is you're attempting to do; but you cannot do it this way.
As an aside, even if it compiled, that loop would probably never finish running: your termination condition (
pareto_front == vec![]
) will never be satisfied because you never modifypareto_front
; it'll either stop immediately, or run forever.The simplest way to get out from under borrowing problems is to make copies of things, so that you don't need a long-lived borrow; if
get_pareto_front_offline
returned aVec<(Vec<u32>, (u32, u32))>
instead, you wouldn't have this issue. That, or modify to code to not touchneighborhood
once you callget_pareto_front_offline
.