(
continued )

As you, I think, know, Rast is one of the languages that implement pattern matching (pattern matching). If you are unfamiliar with this term, you can think of it as a generalized
switch expression in which we compare objects not only by value, but also by
structure :
match hashmap.get(&key) { Some(value) => do_something_with(value), None => { panic!("Oh noes!"); }, }
Of course the comparison is not limited to the case. As you can see in the example above, the objects can also be
destructured during matching (
Some (value) ) and their parts are assigned to other variables (
value ), which can be further used in the corresponding branch of the expression.
Elegant, isn't it? In Rasté, pattern matching is bread and butter not only for
match , but also for
for , (
if )
let and even for ordinary function arguments.
')
However, for a long time I had a rather vague idea of what was happening in the case when we added links and borrowing to the mapping.
& and
ref are two “operators” often used for this. This article will be devoted to the difference between them.
Probably you are already quite familiar with the first "operator" used to create links and types with links.
ref also very transparently hints at a link to links. However, these two expressions play
very different roles when used internally.
In order to add confusion, they are often used together:
use hyper::Url;
At best, the absence of one of them will be noted by the compiler along with the sentence where to add one or another expression, which of course does not always happen. Therefore, it is highly desirable to understand what is actually happening here.
Link part and match part
Rast is very flexible in terms of what values can be part of the comparison. You will have to make an effort to find something that cannot be part of the
match expression. Both objects and links to them can be used without problems in it:
struct Foo(i32);
However, in the previous example we are usually not interested in the link itself, but then where it
points :
match foo { &Foo(num) => println!("Matched with number {}", num), }
As you can see, ampersand is used for this purpose. As well as types (
Some ,
Ok or the above-indicated
Foo ),
& indicates what value we expect when matching. Seeing the ampersand, the program knows that we want to match
references to certain objects, not the objects themselves.
But why is this distinction between an object and a link to it so important? In other places, Rast is quite flexible in blurring the border between the links and the objects themselves, for example, in the case of invoking object methods. (the mechanism for this is called "
conversion by dereference " or in English "
Deref coercion")
However, pattern matching, due to the possibility of unpacking values into their component parts, is a
destructive operation. Everything to which we apply
match (or another similar expression) will by default be
moved to this block:
let maybe_name = Some(String::from("Alice"));
In full accordance with the semantics of
ownership , the
match expression will prevent subsequent attempts to move and in essence absorb the object. Thus, the code above will give the following error message:
error: use of partially moved value: `maybe_name` [E0382] do_something_with(maybe_name); ^~~~~~~~~~
So, like
Some, an ampersand is essentially just a
part of the pattern with which we are matching. And just like with
Some and other types, we have an obvious symmetry: if we used
& to create value, we would need to use an ampersand and to unpack it.
The syntax used in the patterns for unpacking an object is similar to that used to create it.
Prevent moving
Errors similar to the above, often contain useful notes:
note: `(maybe_name:core::option::Option::Some).0` moved here because it has type `collections::string::String`, which is moved by default Some(n) => println!("Hello, {}", n), ^
And ways to potentially correct the error:
help: if you would like to borrow the value instead, use a `ref` binding as shown: Some(ref n) => println!("Hello, {}", n),
It is here that goes on the scene
ref .
The message tells us that if we add the
ref keyword to the proposed location, we will change the move to
borrow for the variable used in this match. (in our case it is
n ) As before, this variable will capture the required value, but this time without
owning it.
And this distinction is extremely important.
Unlike ampersand,
ref does not mean something with which we produce a comparison. It does not in any way affect whether the value will be matched to this pattern or not.
The only thing that it changes is how parts of the associated value will be
captured by the pattern variables:
- By default, without ref, they are moved to the match branch
- If there is a ref , they are borrowed and presented as links.
In our example, the variable
n in the pattern
Some (n) is of type
String , i.e. the same type as in the associated structure. In contrast, the other
n in the
Some pattern
(ref n) is of type
& String , i.e. reference to the field of the object.
The first is moving, the second is borrowing.
The ref keyword means that a variable inside a matching pattern should borrow a value, not move it. This is not part of the pattern in terms of the mapping itself.
We use them together
Let's now analyze what exactly happens in that confusing example from the beginning of the article:
for &(ref name, ref value) in &query_params { println!("{}={}", name, value); }
Since we know that
ref does not in any way affect whether a pattern is matched or not, we could just insert something like
& (a, b) , this option is much more readable: it is clear that we expect a link to an object that is a two-element tuple . Of course, it is just such tuples that are members of the vector by which the iteration occurs.
The problem is that without
refs we are trying to
move the values of the tuple into the loop, but since we iterate over
& query_params , we just borrow each of the tuples, so such a move is actually impossible. In essence, this would be a classic attempt to move a value out of the borrowing context.
But we do not need this movement. The only thing that our cycle does is that it prints the values, so access to them via links will be more than enough.
And this is exactly what the
ref gives us. Having returned this keyword, we will move from moving to borrowing.
Summing up
- & indicates that the pattern expects an object reference . Thus & is part of the pattern. From here & Foo will be matched differently than Foo.
- ref notes that you want to get a link to the unpacked value . It does not directly participate in pattern matching. Therefore, Foo (ref foo) will be mapped to the same object as Foo (foo)