
The Un-Sandwiching rule
All references and reference related problems follow the"un-sandwiching"
rule.
The"un-sandwiching" rule states that:
Given a value, you cannot use a mutable reference between an immutable reference's declaration "zone" and immutable reference's usage "zone". Also, you cannot use mutable or immutable reference between a mutable's declaration zone and mutable's usage zone.
Note:
"Un-Sandwiching rule" is just a made up name that I created to better explain the quirks of immutable and mutable references. Don't use this name in forums!
What does it mean?
This rule is a mashed and simplified form of:
At any given time, you can have either one mutable reference or any number of immutable references. References must always be valid.
(I agree that the "un-sandwiching" rule looks more lengthy, but it will help
you to simplify things.)
Explaination of the rule
The rule can be broken down into two parts.
You cannot use a mutable reference while you are between the immutable reference's declaration and usage zone. However, you may have unlimited immutable references of the same value.
You cannot have any reference (immutable or mutable), if you are between a mutable reference's declaration and usage zone.
A demonstration
fnmain(){letmuta=String::from("hello world");letimmut_ref=&a;// <- declaration zone/* This is the "un-sandwiching" zone. You can have unlimited immutable references to `a`, directly and indirectly (more on that below), but *NOT A SINGLE MUTABLE REFERENCE IS ALLOWED*. */println!("{}",immut_ref);// <- usage zone}
What is allowed?
You may have these types of references between that zone (not an exhaustive list):
letb=&a;// (direct) *immutable* reference to `a`letc=&b;// (indirect) *immutable* reference to `b` (as `b` is immutable too)letd=&(*b);// (indirect) *immutable* reference to `a` via `b`
What is not allowed?
These (or any variations of these) are illegal:
letb_mut=&muta;// You can see why. It clearly violates the "un-sandwiching"// rule as we are creating a mutable reference between the// declaration and usage zone.a.push_str("breeze");// Cannot do this because `a` is an "immutable" now.// More on this below.
Let us look at thea.push_str("breeze")
statement at a more granular level.
The signature ofpush_str
is:
fnpush_str(&mutself,string:&str){// ...}
Can you see what's going on here?
In the signature, we have a&mut self
, which basically means&mut a
(sinceself
isa
). But going back to the "un-sandwiching" rule, we see that we cannot have amutable reference in the declaration-usage zone.
What do I mean bya
becomes "immutable"?
I saya
becomes "immutable" because you cannot mutatea
as long as you are in the "un-sandwiching" zone. You can only have multiple immutable references.
What if we have multiple immutable references?
Let's say we have:
fnmain(){letmuta=String::from("great breeze");letb=&a;// use `b`// use some more// ...letc=&a;// use `c`// use `b`// ...letd=&c;println!("{}",b);// last usage of `b`.// use `d`// use `c`do_something(d);// last usage of `d`// use `c`// ...do_something_else(c);// last usage of `c`}
Whew! That's a lot of jumbled references! Let's see this through our declaration-usage lens.
b│││ c│ ││ │▼ │ d~b │ │ │ │ │ │ │ ▼ │ ~d │ │ ▼ ~c
The starting points are declarations, and the~
s are the last usage of the references. Note that we don't care if a reference is a direct or an indirect one (eg.d
is an indirect reference).
From this diagram, we can clearly see that the "un-sandwiching" zone ranges from the first immutable referenceb
, to the last usage of immutable referencec
. Betweenb
and~c
, you cannot have any mutable reference.
And what about moves?
letmuta=String::from("some");letc=&a;letother=a;// is this possible?println!("{}",c);
This is very simple to answer.
If the object cannot be copied, then you cannot perform a move within the zone.
Your homework
Deduce whether the following code is possible or not:
fnmain(){letmuta=String::from("mutable");letb=&muta;// a *mutable* reference// ...letc=&a;// 1. Is this possible?// ...println!("{}",b);// last usage}
And what about this?
fnmain(){letmuta=String::from("mutable");letb=&muta;// a *mutable* reference// ...letd=&muta;// 2. Is this possible?// ...println!("{}",b);// last usage}
Explaination
let c = &a
is not possible.
By the "un-sandwiching" rule we cannot have a immutable reference while we are within a mutable reference's "un-sandwiching" zone.
let d = &mut a
is not possible.
You can see why. We are trying to create a mutable reference of the value while we are within the mutable reference's "un-sandwiching" zone. This is not allowed.
Conclusion
I hope this article helps you in understanding Rust a bit better.
Questions? Comments? Concerns? Please put them down below and I'd be happy to help you.
Image Source: Manjaro's/usr/share/backgrounds
folder 😃
Top comments(2)
For further actions, you may consider blocking this person and/orreporting abuse