]> git.lizzy.rs Git - rust.git/blob - src/doc/intro.md
auto merge of #17432 : nick29581/rust/contrib, r=brson
[rust.git] / src / doc / intro.md
1 % A 30-minute Introduction to Rust
2
3 Rust is a systems programming language that combines strong compile-time correctness guarantees with fast performance.
4 It improves upon the ideas of other systems languages like C++
5 by providing guaranteed memory safety (no crashes, no data races) and complete control over the lifecycle of memory.
6 Strong memory guarantees make writing correct concurrent Rust code easier than in other languages.
7 This introduction will give you an idea of what Rust is like in about thirty minutes.
8 It expects that you're at least vaguely familiar with a previous 'curly brace' language,
9 but does not require prior experience with systems programming.
10 The concepts are more important than the syntax,
11 so don't worry if you don't get every last detail:
12 the [guide](guide.html) can help you out with that later.
13
14 Let's talk about the most important concept in Rust, "ownership,"
15 and its implications on a task that programmers usually find very difficult: concurrency.
16
17 # The power of ownership
18
19 Ownership is central to Rust,
20 and is the feature from which many of Rust's powerful capabilities are derived.
21 "Ownership" refers to which parts of your code are allowed to read,
22 write, and ultimately release, memory.
23 Let's start by looking at some C++ code:
24
25 ```cpp
26 int* dangling(void)
27 {
28     int i = 1234;
29     return &i;
30 }
31
32 int add_one(void)
33 {
34     int* num = dangling();
35     return *num + 1;
36 }
37 ```
38
39 **Note: The above C++ code is deliberately simple and non-idiomatic for the purpose
40 of demonstration. It is not representative of production-quality C++ code.**
41
42 This function allocates an integer on the stack,
43 and stores it in a variable, `i`.
44 It then returns a reference to the variable `i`.
45 There's just one problem:
46 stack memory becomes invalid when the function returns.
47 This means that in the second line of `add_one`,
48 `num` points to some garbage values,
49 and we won't get the effect that we want.
50 While this is a trivial example,
51 it can happen quite often in C++ code.
52 There's a similar problem when memory on the heap is allocated with `malloc` (or `new`),
53 then freed with `free` (or `delete`),
54 yet your code attempts to do something with the pointer to that memory.
55 This problem is called a 'dangling pointer,'
56 and it's not possible to write Rust code that has it.
57 Let's try writing it in Rust:
58
59 ```ignore
60 fn dangling() -> &int {
61     let i = 1234;
62     return &i;
63 }
64
65 fn add_one() -> int {
66     let num = dangling();
67     return *num + 1;
68 }
69
70 fn main() {
71     add_one();
72 }
73 ```
74
75 Save this program as `dangling.rs`. When you try to compile this program with `rustc dangling.rs`, you'll get an interesting (and long) error message:
76
77 ```text
78 dangling.rs:3:12: 3:14 error: `i` does not live long enough
79 dangling.rs:3     return &i;
80                          ^~
81 dangling.rs:1:23: 4:2 note: reference must be valid for the anonymous lifetime #1 defined on the block at 1:22...
82 dangling.rs:1 fn dangling() -> &int {
83 dangling.rs:2     let i = 1234;
84 dangling.rs:3     return &i;
85 dangling.rs:4 }
86 dangling.rs:1:23: 4:2 note: ...but borrowed value is only valid for the block at 1:22
87 dangling.rs:1 fn dangling() -> &int {
88 dangling.rs:2     let i = 1234;
89 dangling.rs:3     return &i;
90 dangling.rs:4 }
91 error: aborting due to previous error
92 ```
93
94 In order to fully understand this error message,
95 we need to talk about what it means to "own" something.
96 So for now,
97 let's just accept that Rust will not allow us to write code with a dangling pointer,
98 and we'll come back to this code once we understand ownership.
99
100 Let's forget about programming for a second and talk about books.
101 I like to read physical books,
102 and sometimes I really like one and tell my friends they should read it.
103 While I'm reading my book, I own it: the book is in my possession.
104 When I loan the book out to someone else for a while, they "borrow" it from me.
105 And when you borrow a book, it's yours for a certain period of time,
106 and then you give it back to me, and I own it again. Right?
107
108 This concept applies directly to Rust code as well:
109 some code "owns" a particular pointer to memory.
110 It's the sole owner of that pointer.
111 It can also lend that memory out to some other code for a while:
112 that code "borrows" the memory,
113 and it borrows it for a precise period of time,
114 called a "lifetime."
115
116 That's all there is to it.
117 That doesn't seem so hard, right?
118 Let's go back to that error message:
119 `error: 'i' does not live long enough`.
120 We tried to loan out a particular variable, `i`,
121 using a reference (the `&` operator) but Rust knew that the variable would be invalid after the function returns,
122 and so it tells us that:
123 `reference must be valid for the anonymous lifetime #1...`.
124 Neat!
125
126 That's a great example for stack memory,
127 but what about heap memory?
128 Rust has a second kind of pointer,
129 an 'owned box',
130 that you can create with the `box` operator.
131 Check it out:
132
133 ```
134
135 fn dangling() -> Box<int> {
136     let i = box 1234i;
137     return i;
138 }
139
140 fn add_one() -> int {
141     let num = dangling();
142     return *num + 1;
143 }
144 ```
145
146 Now instead of a stack allocated `1234i`,
147 we have a heap allocated `box 1234i`.
148 Whereas `&` borrows a pointer to existing memory,
149 creating an owned box allocates memory on the heap and places a value in it,
150 giving you the sole pointer to that memory.
151 You can roughly compare these two lines:
152
153 ```
154 // Rust
155 let i = box 1234i;
156 ```
157
158 ```cpp
159 // C++
160 int *i = new int;
161 *i = 1234;
162 ```
163
164 Rust infers the correct type,
165 allocates the correct amount of memory and sets it to the value you asked for.
166 This means that it's impossible to allocate uninitialized memory:
167 *Rust does not have the concept of null*.
168 Hooray!
169 There's one other difference between this line of Rust and the C++:
170 The Rust compiler also figures out the lifetime of `i`,
171 and then inserts a corresponding `free` call after it's invalid,
172 like a destructor in C++.
173 You get all of the benefits of manually allocated heap memory without having to do all the bookkeeping yourself.
174 Furthermore, all of this checking is done at compile time,
175 so there's no runtime overhead.
176 You'll get (basically) the exact same code that you'd get if you wrote the correct C++,
177 but it's impossible to write the incorrect version, thanks to the compiler.
178
179 You've seen one way that ownership and borrowing are useful to prevent code that would normally be dangerous in a less-strict language,
180 but let's talk about another: concurrency.
181
182 # Owning concurrency
183
184 Concurrency is an incredibly hot topic in the software world right now.
185 It's always been an interesting area of study for computer scientists,
186 but as usage of the Internet explodes,
187 people are looking to improve the number of users a given service can handle.
188 Concurrency is one way of achieving this goal.
189 There is a pretty big drawback to concurrent code, though:
190 it can be hard to reason about, because it is non-deterministic.
191 There are a few different approaches to writing good concurrent code,
192 but let's talk about how Rust's notions of ownership and lifetimes contribute to correct but concurrent code.
193
194 First, let's go over a simple concurrency example.
195 Rust makes it easy to create "tasks",
196 otherwise known as "threads".
197 Typically, tasks do not share memory but instead communicate amongst each other with 'channels', like this:
198
199 ```
200 fn main() {
201     let numbers = vec![1i, 2i, 3i];
202
203     let (tx, rx)  = channel();
204     tx.send(numbers);
205
206     spawn(proc() {
207         let numbers = rx.recv();
208         println!("{}", numbers[0]);
209     })
210 }
211 ```
212
213 In this example, we create a boxed array of numbers.
214 We then make a 'channel',
215 Rust's primary means of passing messages between tasks.
216 The `channel` function returns two different ends of the channel:
217 a `Sender` and `Receiver` (commonly abbreviated `tx` and `rx`).
218 The `spawn` function spins up a new task,
219 given a *heap allocated closure* to run.
220 As you can see in the code,
221 we call `tx.send()` from the original task,
222 passing in our boxed array,
223 and we call `rx.recv()` (short for 'receive') inside of the new task:
224 values given to the `Sender` via the `send` method come out the other end via the `recv` method on the `Receiver`.
225
226 Now here's the exciting part:
227 because `numbers` is an owned type,
228 when it is sent across the channel,
229 it is actually *moved*,
230 transferring ownership of `numbers` between tasks.
231 This ownership transfer is *very fast* -
232 in this case simply copying a pointer -
233 while also ensuring that the original owning task cannot create data races by continuing to read or write to `numbers` in parallel with the new owner.
234
235 To prove that Rust performs the ownership transfer,
236 try to modify the previous example to continue using the variable `numbers`:
237
238 ```ignore
239 fn main() {
240     let numbers = vec![1i, 2i, 3i];
241
242     let (tx, rx)  = channel();
243     tx.send(numbers);
244
245     spawn(proc() {
246         let numbers = rx.recv();
247         println!("{}", numbers[0]);
248     });
249
250     // Try to print a number from the original task
251     println!("{}", numbers[0]);
252 }
253 ```
254
255 The compiler will produce an error indicating that the value is no longer in scope:
256
257 ```text
258 concurrency.rs:12:20: 12:27 error: use of moved value: 'numbers'
259 concurrency.rs:12     println!("{}", numbers[0]);
260                                      ^~~~~~~
261 ```
262
263 Since only one task can own a boxed array at a time,
264 if instead of distributing our `numbers` array to a single task we wanted to distribute it to many tasks,
265 we would need to copy the array for each.
266 Let's see an example that uses the `clone` method to create copies of the data:
267
268 ```
269 fn main() {
270     let numbers = vec![1i, 2i, 3i];
271
272     for num in range(0u, 3) {
273         let (tx, rx)  = channel();
274         // Use `clone` to send a *copy* of the array
275         tx.send(numbers.clone());
276
277         spawn(proc() {
278             let numbers = rx.recv();
279             println!("{:d}", numbers[num]);
280         })
281     }
282 }
283 ```
284
285 This is similar to the code we had before,
286 except now we loop three times,
287 making three tasks,
288 and *cloning* `numbers` before sending it.
289
290 However, if we're making a lot of tasks,
291 or if our data is very large,
292 creating a copy for each task requires a lot of work and a lot of extra memory for little benefit.
293 In practice, we might not want to do this because of the cost.
294 Enter `Arc`,
295 an atomically reference counted box ("A.R.C." == "atomically reference counted").
296 `Arc` is the most common way to *share* data between tasks.
297 Here's some code:
298
299 ```
300 use std::sync::Arc;
301
302 fn main() {
303     let numbers = vec![1i, 2i, 3i];
304     let numbers = Arc::new(numbers);
305
306     for num in range(0u, 3) {
307         let (tx, rx)  = channel();
308         tx.send(numbers.clone());
309
310         spawn(proc() {
311             let numbers = rx.recv();
312             println!("{:d}", (*numbers)[num as uint]);
313         })
314     }
315 }
316 ```
317
318 This is almost exactly the same,
319 except that this time `numbers` is first put into an `Arc`.
320 `Arc::new` creates the `Arc`,
321 `.clone()` makes another `Arc` that refers to the same contents.
322 So we clone the `Arc` for each task,
323 send that clone down the channel,
324 and then use it to print out a number.
325 Now instead of copying an entire array to send it to our multiple tasks we are just copying a pointer (the `Arc`) and *sharing* the array.
326
327 How can this work though?
328 Surely if we're sharing data then can't we cause data races if one task writes to the array while others read?
329
330 Well, Rust is super-smart and will only let you put data into an `Arc` that is provably safe to share.
331 In this case, it's safe to share the array *as long as it's immutable*,
332 i.e. many tasks may read the data in parallel as long as none can write.
333 So for this type and many others `Arc` will only give you an immutable view of the data.
334
335 Arcs are great for immutable data,
336 but what about mutable data?
337 Shared mutable state is the bane of the concurrent programmer:
338 you can use a mutex to protect shared mutable state,
339 but if you forget to acquire the mutex, bad things can happen, including crashes.
340 Rust provides mutexes but makes it impossible to use them in a way that subverts memory safety.
341
342 Let's take the same example yet again,
343 and modify it to mutate the shared state:
344
345 ```
346 use std::sync::{Arc, Mutex};
347
348 fn main() {
349     let numbers = vec![1i, 2i, 3i];
350     let numbers_lock = Arc::new(Mutex::new(numbers));
351
352     for num in range(0u, 3) {
353         let (tx, rx)  = channel();
354         tx.send(numbers_lock.clone());
355
356         spawn(proc() {
357             let numbers_lock = rx.recv();
358
359             // Take the lock, along with exclusive access to the underlying array
360             let mut numbers = numbers_lock.lock();
361
362             // This is ugly for now because of the need for `get_mut`, but
363             // will be replaced by `numbers[num as uint] += 1`
364             // in the near future.
365             // See: https://github.com/rust-lang/rust/issues/6515
366             *numbers.get_mut(num as uint) += 1;
367
368             println!("{}", (*numbers)[num as uint]);
369
370             // When `numbers` goes out of scope the lock is dropped
371         })
372     }
373 }
374 ```
375
376 This example is starting to get more subtle,
377 but it hints at the powerful composability of Rust's concurrent types.
378 This time we've put our array of numbers inside a `Mutex` and then put *that* inside the `Arc`.
379 Like immutable data,
380 `Mutex`es are sharable,
381 but unlike immutable data,
382 data inside a `Mutex` may be mutated as long as the mutex is locked.
383
384 The `lock` method here returns not your original array or a pointer thereof,
385 but a `MutexGuard`,
386 a type that is responsible for releasing the lock when it goes out of scope.
387 This same `MutexGuard` can transparently be treated as if it were the value the `Mutex` contains,
388 as you can see in the subsequent indexing operation that performs the mutation.
389
390 OK, let's stop there before we get too deep.
391
392 # A footnote: unsafe
393
394 The Rust compiler and libraries are entirely written in Rust;
395 we say that Rust is "self-hosting".
396 If Rust makes it impossible to unsafely share data between threads,
397 and Rust is written in Rust,
398 then how does it implement concurrent types like `Arc` and `Mutex`?
399 The answer: `unsafe`.
400
401 You see, while the Rust compiler is very smart,
402 and saves you from making mistakes you might normally make,
403 it's not an artificial intelligence.
404 Because we're smarter than the compiler -
405 sometimes - we need to over-ride this safe behavior.
406 For this purpose, Rust has an `unsafe` keyword.
407 Within an `unsafe` block,
408 Rust turns off many of its safety checks.
409 If something bad happens to your program,
410 you only have to audit what you've done inside `unsafe`,
411 and not the entire program itself.
412
413 If one of the major goals of Rust was safety,
414 why allow that safety to be turned off?
415 Well, there are really only three main reasons to do it:
416 interfacing with external code,
417 such as doing FFI into a C library;
418 performance (in certain cases);
419 and to provide a safe abstraction around operations that normally would not be safe.
420 Our `Arc`s are an example of this last purpose.
421 We can safely hand out multiple pointers to the contents of the `Arc`,
422 because we are sure the data is safe to share.
423 But the Rust compiler can't know that we've made these choices,
424 so _inside_ the implementation of the Arcs,
425 we use `unsafe` blocks to do (normally) dangerous things.
426 But we expose a safe interface,
427 which means that the `Arc`s are impossible to use incorrectly.
428
429 This is how Rust's type system prevents you from making some of the mistakes that make concurrent programming difficult,
430 yet get the efficiency of languages such as C++.
431
432 # That's all, folks
433
434 I hope that this taste of Rust has given you an idea if Rust is the right language for you.
435 If that's true,
436 I encourage you to check out [the guide](guide.html) for a full,
437 in-depth exploration of Rust's syntax and concepts.