Stream: general

Topic: What allows AddAssign to get multiple references to a Vec?


Jake Goulding (May 11 2020 at 15:47, on Zulip):

This works:

let mut a = vec![0, 1];
a[0] += a[1];

What special knowledge exists to allow this? I would have thought that this would result in a "cannot borrow ... as immutable because it is also borrowed as mutable" error. And replacing the number with a newtype does result in that error. This makes me think there's some built-in implementation for Vec / AddAssign / i32 or some combination.

simulacrum (May 11 2020 at 15:51, on Zulip):

I'm pretty sure you're not getting two &mut references there?

simulacrum (May 11 2020 at 15:52, on Zulip):

based on the MIR it looks like that desugars into roughly speaking:

let mut a = vec![0, 1];
let mut p0 = &mut a[0];
let p1 = a[1];
*p0 += p1;
simulacrum (May 11 2020 at 15:52, on Zulip):

and that works because of 2 phase borrows letting the &mut kick in "late"

Jake Goulding (May 11 2020 at 15:53, on Zulip):

I had a feeling 2pb would be involved, so maybe the question is why doesn't 2pb kick in for the newtype version?

simulacrum (May 11 2020 at 15:53, on Zulip):

hm, is your newtype Copy?

Jake Goulding (May 11 2020 at 15:53, on Zulip):

https://stackoverflow.com/q/61732690/155423

Jake Goulding (May 11 2020 at 15:53, on Zulip):

It is

lcnr (May 11 2020 at 15:57, on Zulip):

Seems weird as Vec is not a lang item...

simulacrum (May 11 2020 at 15:58, on Zulip):

here's a theory: the add no longer happens "in the compiler"

Jake Goulding (May 11 2020 at 15:58, on Zulip):

I was also wondering if AddAssign is a compiler builtin or something

Jake Goulding (May 11 2020 at 15:59, on Zulip):

And maybe Vec decays to a slice

simulacrum (May 11 2020 at 15:59, on Zulip):

here's a diff of the mir https://gist.github.com/Mark-Simulacrum/93386e15a03cd562bdaf9c95dff900b0

simulacrum (May 11 2020 at 15:59, on Zulip):

still inspecting it

Jake Goulding (May 11 2020 at 15:59, on Zulip):

So between that, we end up completely in language space, not in library space.

simulacrum (May 11 2020 at 16:01, on Zulip):

hm actually it almost looks like the indexing order switched?

Jake Goulding (May 11 2020 at 16:01, on Zulip):

so the mut happens first?

simulacrum (May 11 2020 at 16:02, on Zulip):

so there is this https://github.com/rust-lang/rust/pull/47489/files#diff-b3d7798e2a3c43eca68203216e36de65R544 but not sure if it's still the case

Jake Goulding (May 11 2020 at 16:02, on Zulip):

And this:

In practice, this covers the following cases:
- binary assignment operators (+=, -=, *=, etc.) Note this does /not/ include IndexMut operations at this time

simulacrum (May 11 2020 at 16:03, on Zulip):

but yeah the MIR for the newtype has IndexMut happening first, which activates the 2 phase borrow (if you can even call it that since there's not really 2 phases)

Jake Goulding (May 11 2020 at 16:04, on Zulip):

You wanna write that up as an answer?

simulacrum (May 11 2020 at 16:04, on Zulip):

nah that would require more time than I have :)

Jake Goulding (May 11 2020 at 16:27, on Zulip):

What flags did you use to get the MIR before the borrow checker said "stop doing that"?

Jake Goulding (May 11 2020 at 16:31, on Zulip):

Ah, I see you commented

Last update: Jun 05 2020 at 22:35UTC