1
00:00:01,540 --> 00:00:02,370
Welcome to module
2
00:00:02,380 --> 00:00:02,740
8,
3
00:00:02,990 --> 00:00:06,150
where we'll discuss how ownership applies to more than just memory.
4
00:00:06,710 --> 00:00:09,850
We've been talking about how ownership affects memory management,
5
00:00:10,340 --> 00:00:11,740
but it can also help manage other resources.
6
00:00:11,750 --> 00:00:17,510
We're going to go through an example of how ownership helps manage network sockets,
7
00:00:17,940 --> 00:00:18,149
we'll
8
00:00:18,150 --> 00:00:19,409
briefly discuss a few
9
00:00:19,410 --> 00:00:22,950
other resources in the Standard Library that ownership helps to manage,
10
00:00:23,440 --> 00:00:28,350
and we'll show how you can customize what happens when your types go out of scope by implementing the Drop trait.
11
00:00:29,800 --> 00:00:31,450
Let's get started with sockets.
12
00:00:32,940 --> 00:00:33,490
First,
13
00:00:33,550 --> 00:00:36,050
let's review what a socket is and how to use one.
14
00:00:36,990 --> 00:00:42,550
A socket is a system resource that's a connection to a network endpoint for sending and receiving data.
15
00:00:43,440 --> 00:00:45,650
One kind of socket is a TCP socket,
16
00:00:45,900 --> 00:00:48,550
which stands for transmission control protocol.
17
00:00:49,130 --> 00:00:55,150
TCP underlies much of the internet's communication. To use a TCP socket in code,
18
00:00:55,540 --> 00:00:58,050
we create it and bind it to a particular port.
19
00:00:58,490 --> 00:01:02,109
And we should close the socket when we're done with it to avoid overloading the system's socket capacity.
20
00:01:02,110 --> 00:01:08,820
The way sockets should be managed is similar to the way memory should be managed,
21
00:01:09,080 --> 00:01:10,750
and they both have similar problems.
22
00:01:11,740 --> 00:01:13,270
As we've discussed with memory,
23
00:01:13,390 --> 00:01:16,150
it's an error to use memory after deallocating it.
24
00:01:16,640 --> 00:01:17,400
Likewise,
25
00:01:17,560 --> 00:01:21,950
it's an error to try and send or receive data on a socket after it's been closed.
26
00:01:22,840 --> 00:01:25,800
Trying to free memory twice is an error called "double free."
27
00:01:26,290 --> 00:01:28,750
Trying to close a socket twice is also an error.
28
00:01:29,740 --> 00:01:33,520
Memory leaks that can overwhelm the system happen if you forget to free memory.
29
00:01:33,980 --> 00:01:40,650
Forgetting to close a socket can also overwhelm the system. In languages with a garbage collector, such as Ruby,
30
00:01:41,180 --> 00:01:43,920
the garbage collector helps to mitigate the memory problems,
31
00:01:44,020 --> 00:01:46,810
but managing resources like sockets is still manual.
32
00:01:47,110 --> 00:01:49,050
The garbage collector only helps with memory.
33
00:01:50,040 --> 00:01:50,650
However,
34
00:01:50,860 --> 00:01:56,150
Rust helps to mitigate these issues with both memory and sockets via the same mechanism - ownership.
35
00:01:57,670 --> 00:02:02,150
Let's take a look at a code example to see that the socket is closed when its owner goes out of scope.
36
00:02:03,040 --> 00:02:06,300
Here, we have a function called open_socket_for_five_seconds,
37
00:02:06,500 --> 00:02:08,460
which binds a new TcpListener,
38
00:02:08,540 --> 00:02:11,150
a kind of socket, on port 5000.
39
00:02:12,090 --> 00:02:14,250
Then, the function sleeps for 5 seconds.
40
00:02:15,240 --> 00:02:17,270
The listener variable is the owner of the socket.
41
00:02:17,570 --> 00:02:18,870
At the end of this function,
42
00:02:18,960 --> 00:02:22,650
the socket will be closed even though we don't have any code explicitly closing it.
43
00:02:23,640 --> 00:02:29,450
We call this function from main, then print that we're back in main, and sleep for 5 more seconds before exiting the program.
44
00:02:30,440 --> 00:02:32,680
This code doesn't transmit any data over the socket,
45
00:02:32,820 --> 00:02:36,080
so it isn't very useful, other than letting us observe Rust's
46
00:02:36,090 --> 00:02:36,950
socket cleanup.
47
00:02:38,640 --> 00:02:39,650
Let's run this program
48
00:02:39,970 --> 00:02:45,450
and in another terminal, run a command that shows open TCP sockets listening on port 5000.
49
00:02:46,340 --> 00:02:49,550
We see a socket listening during the first 5 seconds.
50
00:02:51,640 --> 00:02:54,120
And then the socket is closed and no longer listening
51
00:02:54,240 --> 00:02:55,380
when the function has ended,
52
00:02:55,390 --> 00:02:57,120
and execution has returned to main.
53
00:02:59,540 --> 00:03:05,679
Let's contrast the Rust program's behavior with that of a Ruby program implemented in the same way. Here,
54
00:03:05,680 --> 00:03:09,390
we also have a function that opens a new socket and sleeps for 5 seconds,
55
00:03:10,440 --> 00:03:12,950
and we call the function and sleep for 5 more seconds.
56
00:03:14,440 --> 00:03:19,650
If we run the Ruby program in one terminal window and the same command to list open ports in another,
57
00:03:20,140 --> 00:03:24,850
this time, we see this socket is listening for the full 10 seconds that the Ruby program is running.
58
00:03:29,280 --> 00:03:37,979
That's because, in Ruby, it's required to explicitly close sockets to prevent leaking them. Rust takes care of this automatically so that we don't have to remember to close
59
00:03:37,980 --> 00:03:43,550
the socket. Sockets aren't the only type in the Standard Library that are managed with ownership.
60
00:03:44,140 --> 00:03:46,349
Let's briefly look at a few more: Mutex,
61
00:03:46,350 --> 00:03:51,090
Rc, and file. The Mutex type,
62
00:03:51,210 --> 00:03:53,150
which is short for mutual exclusion,
63
00:03:53,640 --> 00:04:00,139
is a data type used in multithreaded contexts to ensure that only one thread is allowed to modify the value inside the
64
00:04:00,140 --> 00:04:00,820
mutex.
65
00:04:01,840 --> 00:04:05,510
This is done by acquiring a lock just before modifying the value.
66
00:04:06,640 --> 00:04:08,050
After modifying the value,
67
00:04:08,490 --> 00:04:13,590
you have to release the lock to allow other threads to acquire it. In Rust,
68
00:04:13,770 --> 00:04:15,750
when the owner of the lock goes out of scope,
69
00:04:16,170 --> 00:04:17,950
the lock is automatically released.
70
00:04:18,470 --> 00:04:21,250
This keeps us from accidentally forgetting to release a lock.
71
00:04:22,800 --> 00:04:23,820
The Rc,
72
00:04:23,830 --> 00:04:25,300
which stands for reference counted,
73
00:04:25,680 --> 00:04:29,150
is a type that allows for multiple owners of a single piece of data.
74
00:04:30,140 --> 00:04:38,589
It has an internal counter of how many owners exist, and when the last owner goes out of scope, then the value is cleaned up.
75
00:04:38,590 --> 00:04:40,509
Decrementing the reference counter when each owner goes
76
00:04:40,510 --> 00:04:43,050
out of scope is handled by Rust automatically.
77
00:04:44,540 --> 00:04:45,180
Finally,
78
00:04:45,350 --> 00:04:49,410
files are similar to sockets in that they must be closed when we're done with them
79
00:04:49,620 --> 00:04:51,450
to avoid overwhelming the system.
80
00:04:52,380 --> 00:04:56,750
Rust also handles closing files automatically when the owner of a file goes out of scope.
81
00:04:57,640 --> 00:05:00,870
We're going to be working with files a lot in the next unit on error handling,
82
00:05:00,970 --> 00:05:02,099
so you'll see this in action then.
83
00:05:02,100 --> 00:05:04,540
Finally,
84
00:05:04,660 --> 00:05:09,810
let's look at how to customize what our own types do when their owners go out of scope by using the Drop trait.
85
00:05:11,260 --> 00:05:12,900
The Drop trait has one method,
86
00:05:12,920 --> 00:05:13,950
also named drop.
87
00:05:14,390 --> 00:05:17,550
This method takes a mutable reference to self and no other parameters.
88
00:05:18,520 --> 00:05:21,169
This is where you'd put any logic necessary to clean up
89
00:05:21,170 --> 00:05:22,850
the resources a type uses.
90
00:05:24,410 --> 00:05:30,709
Let's take a look at an example. In this code, we've defined a struct named Noisy that has an id
91
00:05:30,710 --> 00:05:32,550
field of type i32.
92
00:05:33,540 --> 00:05:35,790
In order to see when the drop method is called,
93
00:05:36,020 --> 00:05:45,930
we've implemented the Drop trait for the Noisy struct to print a message indicating that the instance is going out of scope and included the instance's id value. In main,
94
00:05:46,220 --> 00:05:48,450
we're creating two Noisy instances,
95
00:05:48,940 --> 00:05:54,150
one with id: 1 and the other with id: 2. We're then printing the message End of main.
96
00:05:55,740 --> 00:05:59,150
When we run this code, it first prints the End of main message.
97
00:05:59,740 --> 00:06:03,200
Then, the two Noisy instances go out of scope at the end of main,
98
00:06:03,310 --> 00:06:12,250
and the program prints the messages, Noisy number 2 going out of scope! and Noisy number 1 going out of scope!, when the drop method is automatically run.
99
00:06:13,390 --> 00:06:21,020
This example also illustrates that variables go out of scope in the reverse order in which they were declared. In this module,
100
00:06:21,130 --> 00:06:25,820
we illustrated how ownership means sockets are managed in the same way that memory is,
101
00:06:26,060 --> 00:06:29,250
and that sockets are closed automatically when their owner goes out of scope.
102
00:06:30,340 --> 00:06:33,169
We also discussed how other resources, such as
103
00:06:33,170 --> 00:06:33,950
the Mutex type,
104
00:06:34,160 --> 00:06:38,150
the reference counter type, and the file type are also managed in this way.
105
00:06:39,240 --> 00:06:39,840
Finally,
106
00:06:39,970 --> 00:06:48,250
we looked at how the Drop trait is the future of Rust that lets us customize what happens when a value goes out of scope, and how to implement that trait on our own type.
107
00:06:49,990 --> 00:06:52,380
We hope you enjoyed this tour through ownership and borrowing,
108
00:06:52,390 --> 00:06:57,750
as well as seeing how these concepts are important to how Rust makes memory management safe and convenient. In the next unit, we'll be exploring the way error handling works in Rust.