Aliasing of Mutable References

I was reading the Aliasing and References sections of The Rustonomicon, and I encountered some parts that I couldn't fully understand. I'd like to seek help here, thanks.

  1. The Aliasing section begins with the statement:

    "Variables and pointers alias if they refer to overlapping regions of memory."

    My understanding is:
    If an instance and a reference (or raw pointer) point to overlapping memory regions, then the instance and the reference are aliases of each other.

    Is my understanding correct?
    If my understanding is correct, then in the following code:

    let mut val = 123_i32;
    let ref0 = &mut val;
    
    • Is the instance val an alias of the reference ref0?
    • And is the reference ref0 an alias of the instance val?

    Another possible interpretation is that val is the owner of the data, not an alias.
    In this case, val itself does not have any alias; only ref0 is an alias of val.

  2. The References section states:

    "A mutable reference cannot be aliased."

    My understanding is:
    A mutable reference must satisfy the exclusivity principle; it must be the only alias to the instance.

    If this understanding is correct, then does reborrowing a new reference break the principle that "a mutable reference cannot be aliased"?

  3. If the instance val is not considered an alias, does this mean that only references or raw pointers can be aliases of val?

Ref: (Rust Playground)

The actual restriction the compiler enforces is that you cannot access an alias to the same variable. You can write code which creates an unused alias and just get a compiler warning.

So you can write

let mut val = 123_i32;
let ref0 = &mut val;

and you can access either val or ref0. But not both.

So, does the name of a variable count as an alias?

I think you should concentrate on refer to.
The construction &mut val changes it so val does not refer to anything.
Only when the lifetime the reference holds is dropped can val again refer to the memory.

So, are you implying that the variable name "val" is actually also an alias for a memory region?
My understanding is that while ref0 is active, val is in an inactive state, similar to reborrowing—val is sealed and can only be unsealed once ref0 automatically becomes invalid.

When you have a mutable reference A and reborrow it to create mutable reference B, the borrow checker makes A unusable for the duration of B. That is, between the creation and last use of B, attempting to use A results in a compilation failure.

So because A is "disabled" in this way, it doesn't count as aliasing.

I'm not sure if terminology is 100% established here, but for myself I consider a scope where variable is defined to be an owner and val to be an alias.

After all you can write something like this:

{
    let ref0 = &mut 123_i32;
}

That's perfectly valid code and yet there are no variable that can be considered “an owner” (and treated specially).

Only “active” references are counted. Otherwise it wouldn't be possible to ever pass mutable reference to another function – and that would make for extremely impractical language.

That means that there may be any number of mutable references, but only one can be “active” and accessible at any given time.

Are you saying that val is a variable rather than an alias? When it is mutably borrowed, the compiler marks val as inactive, or in other words, seals it. The compiler will only unseal val once ref0 automatically becomes invalid?

Hope this helps.
You need to split between address and memory. val always has the address but its content become unknown when the mutable reference is constructed. On the other hand a shared reference the memory remains known. Aliasing is when multiple variables know the memory.

The actual motivational example looks like this:

fn main() {
    let ref0 = &mut 123_i32;
    *ref0 += 1;
    foo(ref0);
    *ref0 += 1;
    println!("{ref0}");
}

fn foo(ref1: &mut i32) {
    *ref1 += 1;
    bar(ref1);
    *ref1 += 1;
}

fn bar(ref2: &mut i32) {
    *ref2 += 1;
}

Here we don't need to try to split the cat's hair because here we, quite obviously, have three separate references (ref0, ref1, ref2).

They are different (they exist in a different functions, but when bar is executing all three are still executing) and it would be exremely strange to not be able to write code like that.

That's where the need for the “active reference” comes from and that's how “there can only be one active mutable, unique reference” rule is relaxed.

Whether it applies to your case (that is: whether normal let mut val variable considered a reference or not) is not very interesting question: what would it change?

The idea applied to that case is the exact same while concrete bikeshedding discussion about how to name these wouldn't give us any new insight.

This thread is littered with incorrect terminology.

There is no such thing as "an alias". The word "alias" is a verb, not a noun. The word "instance" is also incorrect here; the correct word is "variable".

The correct wording would be: "Does the variable val alias the reference ref0?".

And the answer is yes. But the full aliasing rules allow aliasing in this case because val is mutably borrowed.

The correct wording would be: "Does the reference ref0 alias the variable val."

And the answer is also yes. But again, the full aliasing rules allow aliasing in this case because val is mutably borrowed.

Yes, but you're reading a section that does not explain the full rules. Aliasing of A and B is allowed when A is created from B, and when B is borrowed for the duration of A.

No, variables can also alias something.

Again, the word "instance" is wrong here.

Well, yes, val is a variable. There is no such thing as "an alias". However, it is true that val and ref0 alias.

Yes, but again the correct wording is that val is borrowed. Inactive or sealed are not real Rust words.

There's no such thing as "unsealing" a variable. But yes, the compiler will check that val is not used between the creation and last use of ref0.

5 Likes