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
+50-16
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 "owns" 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,11 +40,20 @@ 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`.
49
+
Why does Rust do this?
50
+
Well this has to do with how Rust handles memory management internally.
51
+
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.
52
+
This is different from how other languages handle memory with garbage collection or reference counting to know when memory should be freed.
40
53
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.
54
+
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*.
55
+
The reference will "borrow" the value and won't actually "own" the underlying data.
56
+
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.
42
57
43
58
We can create a reference by placing the `&` symbol in front and modifying the function argument type:
44
59
@@ -61,7 +76,10 @@ This will work now and correctly print:
61
76
The length of 'hello' is 5.
62
77
```
63
78
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,
79
+
`s1` remains the owner of the String.
80
+
`s` in the `calculate_length` will merely borrow a shared, immutable reference to the String.
81
+
This means that we wouldn't be able to mutate the String in `calculate_length`.
82
+
For example this won't work,
65
83
66
84
```rust
67
85
fnmain() {
@@ -118,7 +136,8 @@ The length of 'hell' is 4.
118
136
119
137
That's the basics of shared and mutable references!
120
138
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.
139
+
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]`.
140
+
Typically, these are types that are stack allocated and don't require any heap allocations.
122
141
123
142
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.
157
+
This will run just fine without needing to pass a shared reference.
158
+
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
159
140
160
### 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.
161
+
Rust enforces a simple, yet important rule when it comes to passing references and that is single writer OR multiple readers.
162
+
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.
163
+
You can't have both a shared reference and a mutable reference at the same time.
142
164
143
165
Let's walk through an example to see why:
144
166
@@ -150,9 +172,11 @@ fn main() {
150
172
r[0] // r still points to v, which doesn't point to anything and so is a dangling pointer
151
173
}
152
174
```
153
-
The statement `let aside = v`*moves* the vec from `v` into `aside`. However, `r` still references the variable `v` which is now uninitialized.
175
+
The statement `let aside = v`*moves* the vec from `v` into `aside`.
176
+
However, `r` still references the variable `v` which is now uninitialized.
154
177
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.
178
+
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.
179
+
This can lead to unexpected behavior in a program and so Rust will attempt to avoid these possibilities.
156
180
157
181
This is what the compiler will complain:
158
182
```console
@@ -168,14 +192,24 @@ error[E0505]: cannot move out of `v` because it is borrowed
168
192
17 | r[0]; // r still points to v, which doesn't point to anything and so is a dangling pointer
169
193
| - borrow later used here
170
194
```
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.
195
+
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.
196
+
This will prevent a case of a dangling pointer.
172
197
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).
198
+
You may see this compiler error from time to time.
199
+
Just remember the rule: you can only have a single writer (mutable reference) OR multiple readers (shared references).
174
200
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.
201
+
Ok! That's it for references!
202
+
Take a breather as you just got past one of the hardest aspects of understanding the Rust programmming language.
203
+
Don't worry if it hasn't fully "sunk in" yet.
204
+
This is something that takes time and practice to get familiar with.
205
+
Just know that with time these concepts will make more sense and you might even begin to start appreciating them.
176
206
177
207
### 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?*
208
+
*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?
209
+
Experiment by calling `.clear()` on the vector (after declaring it mutable).
210
+
See example below.
211
+
Run it and see what happens.
212
+
Can you explain why the compiler is returning an error and the meaning of that error?*
0 commit comments