Skip to content
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

Mutability issue while indexing static immutable array #46095

Closed
vitvakatu opened this issue Nov 19, 2017 · 5 comments · Fixed by #47167
Closed

Mutability issue while indexing static immutable array #46095

vitvakatu opened this issue Nov 19, 2017 · 5 comments · Fixed by #47167
Labels
A-typesystem Area: The type system C-bug Category: This is a bug.

Comments

@vitvakatu
Copy link

Hello, today we've found some weird behaviour of array indexing.
Here is the example
Code snippet:

fn foo<'a>(a1: &'a mut Bar, a2: &'a mut Bar)
{
	let a = [a1, a2];
	for i in 0..42
	{
		let index = i % 2;
		//a[i % 2].bar(); //<= error
		a[index].bar(); //<= no error
	}
}

The question is, why is the indexing operator behaves depending on whether we evaluate index in place or not.

@shepmaster
Copy link
Member

Highly related question from Stack Overflow:

trait Bar {
    fn bar(&mut self);
}

fn foo(a1: &mut Bar, j: usize) {
    let a = [a1];
    a[0].bar(); //compilation ok
    a[j % 2].bar();
}

fn main() {}
error[E0596]: cannot borrow immutable local variable `a` as mutable
 --> src/main.rs:8:5
  |
6 |     let a = [a1];
  |         - consider changing this to `mut a`
7 |     a[0].bar(); //compilation ok
8 |     a[j % 2].bar();
  |     ^ cannot borrow mutably

@scalar438
Copy link

Moreover, a[0 % 2].bar() does not compile too.

@shepmaster
Copy link
Member

shepmaster commented Nov 19, 2017

Conversation on IRC points to this being a difference in how builtin indexing works vs overloaded indexing. My cleaned up version of the transcript:

@eddyb
The type of j + 0 requires negative knowledge to find. That is, usize: Add<$0> where $0 is a builtin integer can only be usize: Add<usize> and we find this out at some point, basically by checking all usize: Add<_> impls and ignoring all of them which can't work with a built-in integer.

Before that point, we don't know the resulting type other than as <usize as Add<$0>>::Output which is not "obviously usize" so we don't use builtin indexing.

Builtin indexing is more straight-forward and allows knowing the target type immediately. In your case, it's <[&mut Bar; 1] as Index<<usize as Add<$0>>::Output>>::Output.

I think the overloaded indexing is what needs a &mut borrow on the array, so it might not even be unknown type, but overloaded indexing taking a borrow where builtin indexing doesn't.

@arielb1
Yeah that's because you can't have "by lvalue" use of an overloaded deref while you can have "by lvalue" use of non-overloaded deref. We should just be more consistent and switch to "by-lvalue" use whenever we can.

Highly related to #33903, which will cause these types of situations to occur more frequently.

@shepmaster
Copy link
Member

As a workaround, you can write an explicit type of usize which will then allow builtin indexing to happen:

fn foo<'a>(a1: &'a mut Bar, a2: &'a mut Bar) {
    let a = [a1, a2];
    for i in 0..42 {
        let index2: usize = i % 2;
        a[i].bar();
        a[index2].bar();
    }
}
fn foo(a1: &mut Bar, j: usize) {
    let a = [a1];
    a[0].bar(); 
    let x: usize = j % 2;
    a[x].bar();
}

@shepmaster shepmaster added A-typesystem Area: The type system C-bug Category: This is a bug. labels Nov 19, 2017
@aleksmelnikov
Copy link

aleksmelnikov commented Nov 20, 2017

trait Bar {
    fn bar(&mut self);
}

fn foo(a1: &mut Bar, j: usize) {
    //let a = [a1];
    // why doesn't vec work?
    let a = vec![a1];
    a[0].bar();
    let x: usize = j % 2;
    a[x].bar();
}

fn main() {}

bors added a commit that referenced this issue Jan 10, 2018
Fix built-in indexing not being used where index type wasn't "obviously" usize

Fixes #33903
Fixes #46095

This PR was made possible thanks to the generous help of @eddyb

Following the example of binary operators, builtin checking for indexing has been moved from the typecheck stage to a writeback stage, after type constraints have been resolved.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-typesystem Area: The type system C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants