You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 09_01_shared_references.md
+54-17
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,13 @@
1
1
# Shared References
2
2
3
-
In addition to a *mutable* reference, as indicated by the `&mut` keyword, we can also pass around a *shared* reference to a variable, which can be done by simply prefacing a variable with the `&` symbol. A mutable reference can modify the underlying data where as the shared reference cannot and is read-only. We already saw an example of a shared reference in chapter 6 where we created a slice reference to data on the heap by prepending `[u8]` with the `&` symbol.
3
+
In addition to a *mutable* reference, as indicated by the `&mut` keyword, we can also pass around a *shared* reference to a variable, which can be done by simply prefacing a variable with the `&` symbol.
4
+
A mutable reference can modify the underlying data where as the shared reference cannot and is read-only.
5
+
We already saw an example of a shared reference in chapter 6 where we created a slice reference to data on the heap by prepending `[u8]` with the `&` symbol.
4
6
5
-
A reference is a kind of pointer. It points to some data elsewhere and "borrows" it instead of "owns" it. What does this mean? Well let's see with an [example](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#references-and-borrowing):
7
+
A reference is a kind of pointer.
8
+
It points to some data elsewhere and "borrows" it instead of "owning" it.
9
+
What does this mean?
10
+
Well let's see with an [example](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#references-and-borrowing):
You can run this code online with [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cf786ab3bacd43e260e73ae5efa49d50). If you run it, you'll notice that there will be a compiler error:
26
+
You can run this code online with [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cf786ab3bacd43e260e73ae5efa49d50).
27
+
If you run it, you'll notice that there will be a compiler error:
22
28
```console
23
29
Compiling playground v0.0.1 (/playground)
24
30
error[E0382]: borrow of moved value: `s1`
@@ -34,13 +40,25 @@ error[E0382]: borrow of moved value: `s1`
34
40
| ^^ value borrowed here after move
35
41
```
36
42
37
-
The error points out that a move occurs and that we are unable to "borrow" the value after a move. It also mentions that the type, `String` does not implement the `Copy` trait.
43
+
The error points out that a move occurs and that we are unable to "borrow" the value after a move.
44
+
It also mentions that the type, `String` does not implement the `Copy` trait.
38
45
39
-
By default, any type that does not implement the `Copy` trait will get "moved" when passed as an argument. This means the variable that contains it will no longer be available within the scope of original function. It's "ownership" is now passed to the function being called, which in this case is `calculate_length`. Why does Rust do this? Well this has to do with how Rust handles memory management internally. This way, it's always clear which variable "owns" data so that when that variable goes out of scope, the memory associated with it can be automatically freed. This is different from how other languages handle memory with garbage collection or reference counting to know when memory should be freed.
46
+
By default, any type that does not implement the `Copy` trait will get "moved" when passed as an argument.
47
+
This means the variable that contains it will no longer be available within the scope of original function.
48
+
It's "ownership" is now passed to the function being called, which in this case is `calculate_length`.
40
49
41
-
So if we want to keep referring to the string after it has been passed to the `calculate_length` function, we need to use a *reference*. The reference will "borrow" the value and won't actually "own" the underlying data. This means that when the reference goes out of scope and is no longer in use, the heap data and its owner will still remain.
50
+
Why does Rust do this?
51
+
Well this has to do with how Rust handles memory management internally.
52
+
This way, it's always clear which variable "owns" data so that when that variable goes out of scope, the memory associated with it can be automatically freed.
53
+
This is different from how other languages handle memory with garbage collection or reference counting to know when memory should be freed.
42
54
43
-
We can create a reference by placing the `&` symbol in front and modifying the function argument type:
55
+
Since `s1` was moved to the scope of `calculate_length`, the `String` data will be deallocated when the function returns.
56
+
That's why we can't print it when we are back in `main`, the data would not exist anymore.
57
+
If we want to keep referring to the string after it has been passed to the `calculate_length` function, we need to pass a *reference* as argument instead.
58
+
The reference will "borrow" the value and won't actually "own" the underlying data.
59
+
This means that when the reference goes out of scope and is no longer in use, the heap data and its owner will still remain.
60
+
61
+
We can create a reference by placing the `&` symbol in front of an identifier and modifying the function argument type:
44
62
45
63
```rust
46
64
fnmain() {
@@ -61,7 +79,10 @@ This will work now and correctly print:
61
79
The length of 'hello' is 5.
62
80
```
63
81
64
-
`s1` remains the owner of the String. `s` in the `calculate_length` will merely borrow a shared, immutable reference to the String. This means that we wouldn't be able to mutate the String in `calculate_length`. For example this won't work,
82
+
`s1` remains the owner of the String.
83
+
`s` in the `calculate_length` will merely borrow a shared, immutable reference to the String.
84
+
This means that we wouldn't be able to mutate the String in `calculate_length`.
85
+
For example this won't work,
65
86
66
87
```rust
67
88
fnmain() {
@@ -118,7 +139,8 @@ The length of 'hell' is 4.
118
139
119
140
That's the basics of shared and mutable references!
120
141
121
-
It's important to point out here that there is an exception to this rule, which we alluded to earlier and that is any type that implements the `Copy` trait, such as an integer type or an array `[u8; N]`. Typically, these are types that are stack allocated and don't require any heap allocations.
142
+
It's important to point out here that there is an exception to this rule, which we alluded to earlier and that is any type that implements the `Copy` trait, such as an integer type or an array `[u8; N]`.
143
+
Typically, these are types that are stack allocated and don't require any heap allocations.
122
144
123
145
Let's take a look at an example (see [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bbc4ad315069566df4b5d853280492d1)):
This will run just fine without needing to pass a shared reference. Why? Because a copy will automatically be made in the `calculate_len` function and Rust won't have to worry about or think about ownership of data on the heap.
160
+
This will run just fine without needing to pass a shared reference.
161
+
Why? Because a copy will automatically be made in the `calculate_len` function and Rust won't have to worry about or think about ownership of data on the heap.
139
162
140
163
### Single Writer or Multiple Readers
141
-
Rust enforces a simple, yet important rule when it comes to passing references and that is single writer OR multiple readers. In other words, you can have many different immutable, shared references to an object OR you can have just one mutable reference at any given time. You can't have both a shared reference and a mutable reference at the same time.
164
+
Rust enforces a simple, yet important rule when it comes to passing references and that is single writer OR multiple readers.
165
+
In other words, you can have many different immutable, shared references to an object OR you can have just one mutable reference at any given time.
166
+
You can't have both a shared reference and a mutable reference at the same time.
142
167
143
168
Let's walk through an example to see why:
144
169
@@ -150,9 +175,11 @@ fn main() {
150
175
r[0] // r still points to v, which doesn't point to anything and so is a dangling pointer
151
176
}
152
177
```
153
-
The statement `let aside = v`*moves* the vec from `v` into `aside`. However, `r` still references the variable `v` which is now uninitialized.
178
+
The statement `let aside = v`*moves* the vec from `v` into `aside`.
179
+
However, `r` still references the variable `v` which is now uninitialized.
154
180
155
-
If we have a shared reference to some variable and that variable goes out of scope or becomes uninitialized, we would end up with a dangling pointer. This can lead to unexpected behavior in a program and so Rust will attempt to avoid these possibilities.
181
+
If we have a shared reference to some variable and that variable goes out of scope or becomes uninitialized, we would end up with a dangling pointer.
182
+
This can lead to unexpected behavior in a program and so Rust will attempt to avoid these possibilities.
156
183
157
184
This is what the compiler will complain:
158
185
```console
@@ -168,14 +195,24 @@ error[E0505]: cannot move out of `v` because it is borrowed
168
195
17 | r[0]; // r still points to v, which doesn't point to anything and so is a dangling pointer
169
196
| - borrow later used here
170
197
```
171
-
In other words, Rust will complain and enforce the rule that we cannot make any changes to `v` while it is being borrowed as an immutable, shared reference. This will prevent a case of a dangling pointer.
198
+
In other words, Rust will complain and enforce the rule that we cannot make any changes to `v` while it is being borrowed as an immutable, shared reference.
199
+
This will prevent a case of a dangling pointer.
172
200
173
-
You may see this compiler error from time to time. Just remember the rule: you can only have a single writer (mutable reference) OR multiple readers (shared references).
201
+
You may see this compiler error from time to time.
202
+
Just remember the rule: you can only have a single writer (mutable reference) OR multiple readers (shared references).
174
203
175
-
Ok! That's it for references! Take a breather as you just got past one of the hardest aspects of understanding the Rust programmming language. Don't worry if it hasn't fully "sunk in" yet. This is something that takes time and practice to get familiar with. Just know that with time these concepts will make more sense and you might even begin to start appreciating them.
204
+
Ok! That's it for references!
205
+
Take a breather as you just got past one of the hardest aspects of understanding the Rust programmming language.
206
+
Don't worry if it hasn't fully "sunk in" yet.
207
+
This is something that takes time and practice to get familiar with.
208
+
Just know that with time these concepts will make more sense and you might even begin to start appreciating them.
176
209
177
210
### Quiz
178
-
*What do you think would happen if we attempted to modify the vector in our project while we have a slice that borrows a reference to it? Experiment by calling `.clear()` on the vector (after declaring it mutable). See example below. Run it and see what happens. Can you explain why the compiler is returning an error and the meaning of that error?*
211
+
*What do you think would happen if we attempted to modify the vector in our project while we have a slice that borrows a reference to it?
212
+
Experiment by calling `.clear()` on the vector (after declaring it mutable).
213
+
See example below.
214
+
Run it and see what happens.
215
+
Can you explain why the compiler is returning an error and the meaning of that error?*
0 commit comments