-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Getter functions and indexing don't seem to follow the same lifetime rules #58419
Comments
Cc'ing @nikomatsakis as I've been told he'd be particulary interested in issues like this one. |
Hmm, you're right. This should be possible with NLL, e.g. using let idx = collection[0].index;
collection[idx].index = 1; does work. |
Hmm I wonder if this is due to some weakness in how we try to limit two-phase borrows to just auto-refs on method calls and indexing. (This was implemented as part fo addressing #48431) In particular, my hypothesis is that whatever method the compiler is using to pattern-match the code to see if we should attempt to apply two-phase borrows, it is probably getting foiled by the Generalizing two-phase borrow in some manner is generally tracked in #49434, though I imagine we might attempt to land a more targetted fix for this particular issue sooner than we attempt a more complete generalization. |
triage: P-medium. I am assuming that we will consider widening the semantics of 2-phase borrows in the future in a manner that might allow this code to pass. But such a change in the static semantics is not as high priority as other tasks for the NLL team. |
Related thread on the internals forum In that thread, SNCPlay42 notes that this already works on types that implement the |
(Sorry for the length of this comment; it is transcribed from #74319 which I where I had mistakenly posted it initially.) After reading this and related issues, and re-reading: rust/compiler/rustc_middle/src/ty/adjustment.rs Lines 135 to 151 in 96859db
Here is a summary of my understanding of the situation:
So, what is currently blocking support for
I've already expressed concerns about trying to deliver fully generalized two-phase borrows without a proof of soundness; you can see those comments on #49434. (If you want to see an example of why we are trying to be conservative here, maybe take a look at #56254, which led us to ratchet back some of two-phase borrows initial expressiveness because we weren't confident that it was compatible with the stacked-borrows model from that time.) A lot has happened since those discussions; e.g. models have evolved. But we still don't have any proof of soundness of generalized two-phase borrows, as far as I know. (Having said all that, you all might be able to talk me into treating |
@pnkfelix I would try to talk you into at least supporting Below is a related real-life use case: #[derive(Debug)]
struct Matrix {
elements: Vec<f32>,
rows: usize,
cols: usize,
}
impl Matrix {
pub fn set(&mut self, row: usize, col: usize, value: f32) {
self.elements[self.idx(row, col)] = value; // ERR
}
fn idx(&self, row: usize, col: usize) -> usize {
row * self.cols + col
}
}
fn main() {
let mut mat = Matrix { elements: vec![1.0,2.0,3.0,4.0,5.0,6.0], rows: 2, cols: 3 };
mat.set(1,2, 3.0);
println!("{:?}", mat);
} |
@Danvil It would be more direct to implement use std::ops::{Index, IndexMut};
#[derive(Debug)]
struct Matrix {
elements: Vec<f32>,
rows: usize,
cols: usize,
}
impl Matrix {
pub fn set(&mut self, row: usize, col: usize, value: f32) {
self[(row, col)] = value;
}
}
impl Index<(usize, usize)> for Matrix {
type Output = f32;
fn index(&self, (row, col): (usize, usize)) -> &f32 {
&self.elements[row * self.cols + col]
}
}
impl IndexMut<(usize, usize)> for Matrix {
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut f32 {
&mut self.elements[row * self.cols + col]
}
}
fn main() {
let mut mat = Matrix {
elements: vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
rows: 2,
cols: 3,
};
mat.set(1, 2, 3.0);
println!("{:?}", mat);
} |
@workingjubilee The issue was that I can't define the helper function |
This is my first issue for the Rust project. Apologies up front if it is somehow unclear. I'd be happy to provide any additional information you need.
DESCRIPTION
The issue I'm running into is this. Consider the following code:
collection.get_mut(collection.get(0).unwrap().index).unwrap().index = 1;
The inner call to
get
causes an immutable borrow oncollection
. We then obtain a index from the return value, and use that for the outer call toget_mut
. This causes a mutable borrow oncollection
.The above code compiles just fine. My assumption is that because
index
implementsCopy
, NLL allow the immutable borrow oncollection
to be dropped as soon as we have a copy ofindex
, so we can borrow it mutably again after that.So far, so good. However, now consider the following code:
collection[collection[0].index].index = 1
;EXPECTED BEHAVIOR
This should be equivalent to the earlier code. In fact, I've implemented the
Index
trait to forward toget(index).unwrap()
.ACTUAL BEHAVIOR
This time, however, the compiler complains:
error[E0502]: cannot borrow
collectionas immutable because it is also borrowed as mutable
For more context, here is a playground link that illustrates the problem:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=21d19fde2c8c58d4859bfdefa3b7720a
The text was updated successfully, but these errors were encountered: