References
In the Ownership and Scope section, we explained that when a value is passed to a function, it is moved to the function's scope. This means that the function becomes the owner of the value, and the original scope (owner) can no longer use it. This is an important concept in Move, as it ensures that the value is not used in multiple places at the same time. However, there are use cases when we want to pass a value to a function but retain the ownership. This is where references come into play.
To illustrate this, let's consider a simple example - an application for a metro (subway) pass. We will look at 4 different scenarios:
- Card can be purchased at the kiosk for a fixed price
- Card can be shown to inspectors to prove that the passenger has a valid pass
- Card can be used at the turnstile to enter the metro, and spend a ride
- Card can be recycled once it's empty
Layout
The initial layout of the metro pass application is simple. We define the Card
type and the USES
constant that represents the number of rides for a single card. We also add an error constant for the case when the card is empty.
Reference
References are a way to show a value to a function without giving up the ownership. In our case, when we show the Card to the inspector, we don't want to give up the ownership of it, and we don't allow them to spend the rides. We just want to allow reading the value of the Card and prove its ownership.
To do so, in the function signature, we use the &
symbol to indicate that we are passing a reference to the value, not the value itself.
Now the function can't take the ownership of the card, and it can't spend the rides. But it can read its value. Worth noting, that a signature like this makes it impossible to call the function without a Card at all. This is an important property which allows the Capability Pattern which we will cover in the next chapters.
Mutable Reference
In some cases, we want to allow the function to change the value of the Card. For example, when we use the Card at the turnstile, we want to spend a ride. To implement it, we use the &mut
keyword in the function signature.
As you can see in the function body, the &mut
reference allows mutating the value, and the function can spend the rides.
Passing by Value
Lastly, let's give an illustration of what happens when we pass the value itself to the function. In this case, the function takes the ownership of the value, and the original scope can no longer use it. The owner of the Card can recycle it, and, hence, lose the ownership.
In the recycle
function, the Card is taken by value and can be unpacked and destroyed. The original scope can't use it anymore.
Full Example
To illustrate the full flow of the application, let's put all the pieces together in a test.