|
1 | 1 | % Meet Safe and Unsafe
|
2 | 2 |
|
3 |
| -Safe and Unsafe are Rust's chief engineers. |
4 |
| - |
5 |
| -TODO: ADORABLE PICTURES OMG |
6 |
| - |
7 |
| -Unsafe handles all the dangerous internal stuff. They build the foundations |
8 |
| -and handle all the dangerous materials. By all accounts, Unsafe is really a bit |
9 |
| -unproductive, because the nature of their work means that they have to spend a |
10 |
| -lot of time checking and double-checking everything. What if there's an earthquake |
11 |
| -on a leap year? Are we ready for that? Unsafe better be, because if they get |
12 |
| -*anything* wrong, everything will blow up! What Unsafe brings to the table is |
13 |
| -*quality*, not quantity. Still, nothing would ever get done if everything was |
14 |
| -built to Unsafe's standards! |
15 |
| - |
16 |
| -That's where Safe comes in. Safe has to handle *everything else*. Since Safe needs |
17 |
| -to *get work done*, they've grown to be fairly careless and clumsy! Safe doesn't worry |
18 |
| -about all the crazy eventualities that Unsafe does, because life is too short to deal |
19 |
| -with leap-year-earthquakes. Of course, this means there's some jobs that Safe just |
20 |
| -can't handle. Safe is all about quantity over quality. |
21 |
| - |
22 |
| -Unsafe loves Safe to bits, but knows that they *can never trust them to do the |
23 |
| -right thing*. Still, Unsafe acknowledges that not every problem needs quite the |
24 |
| -attention to detail that they apply. Indeed, Unsafe would *love* if Safe could do |
25 |
| -*everything* for them. To accomplish this, Unsafe spends most of their time |
26 |
| -building *safe abstractions*. These abstractions handle all the nitty-gritty |
27 |
| -details for Safe, and choose good defaults so that the simplest solution (which |
28 |
| -Safe will inevitably use) is usually the *right* one. Once a safe abstraction is |
29 |
| -built, Unsafe ideally needs to never work on it again, and Safe can blindly use |
30 |
| -it in all their work. |
31 |
| - |
32 |
| -Unsafe's attention to detail means that all the things that they mark as ok for |
33 |
| -Safe to use can be combined in arbitrarily ridiculous ways, and all the rules |
34 |
| -that Unsafe is forced to uphold will never be violated. If they *can* be violated |
35 |
| -by Safe, that means *Unsafe*'s the one in the wrong. Safe can work carelessly, |
36 |
| -knowing that if anything blows up, it's not *their* fault. Safe can also call in |
37 |
| -Unsafe at any time if there's a hard problem they can't quite work out, or if they |
38 |
| -can't meet the client's quality demands. Of course, Unsafe will beg and plead Safe |
39 |
| -to try their latest safe abstraction first! |
40 |
| - |
41 |
| -In addition to being adorable, Safe and Unsafe are what makes Rust possible. |
42 |
| -Rust can be thought of as two different languages: Safe Rust, and Unsafe Rust. |
43 |
| -Any time someone opines the guarantees of Rust, they are almost surely talking about |
44 |
| -Safe. However Safe is not sufficient to write every program. For that, |
45 |
| -we need the Unsafe superset. |
46 |
| - |
47 |
| -Most fundamentally, writing bindings to other languages |
48 |
| -(such as the C exposed by your operating system) is never going to be safe. Rust |
49 |
| -can't control what other languages do to program execution! However Unsafe is |
50 |
| -also necessary to construct fundamental abstractions where the type system is not |
51 |
| -sufficient to automatically prove what you're doing is sound. |
52 |
| - |
53 |
| -Indeed, the Rust standard library is implemented in Rust, and it makes substantial |
54 |
| -use of Unsafe for implementing IO, memory allocation, collections, |
55 |
| -synchronization, and other low-level computational primitives. |
56 |
| - |
57 |
| -Upon hearing this, many wonder why they would not simply just use C or C++ in place of |
58 |
| -Rust (or just use a "real" safe language). If we're going to do unsafe things, why not |
59 |
| -lean on these much more established languages? |
60 |
| - |
61 |
| -The most important difference between C++ and Rust is a matter of defaults: |
62 |
| -Rust is 100% safe by default. Even when you *opt out* of safety in Rust, it is a modular |
63 |
| -action. In deciding to work with unchecked uninitialized memory, this does not |
64 |
| -suddenly make dangling or null pointers a problem. When using unchecked indexing on `x`, |
65 |
| -one does not have to suddenly worry about indexing out of bounds on `y`. |
66 |
| -C and C++, by contrast, have pervasive unsafety baked into the language. Even the |
67 |
| -modern best practices like `unique_ptr` have various safety pitfalls. |
68 |
| - |
69 |
| -It cannot be emphasized enough that Unsafe should be regarded as an exceptional |
70 |
| -thing, not a normal one. Unsafe is often the domain of *fundamental libraries*: anything that needs |
71 |
| -to make FFI bindings or define core abstractions. These fundamental libraries then expose |
72 |
| -a safe interface for intermediate libraries and applications to build upon. And these |
73 |
| -safe interfaces make an important promise: if your application segfaults, it's not your |
74 |
| -fault. *They* have a bug. |
75 |
| - |
76 |
| -And really, how is that different from *any* safe language? Python, Ruby, and Java libraries |
77 |
| -can internally do all sorts of nasty things. The languages themselves are no |
78 |
| -different. Safe languages *regularly* have bugs that cause critical vulnerabilities. |
79 |
| -The fact that Rust is written with a healthy spoonful of Unsafe is no different. |
80 |
| -However it *does* mean that Rust doesn't need to fall back to the pervasive unsafety of |
81 |
| -C to do the nasty things that need to get done. |
| 3 | +Programmers in safe "high-level" languages face a fundamental dilemma. On one |
| 4 | +hand, it would be *really* great to just say what you want and not worry about |
| 5 | +how it's done. On the other hand, that can lead to some *really* poor |
| 6 | +performance. It may be necessary to drop down to less clear or idiomatic |
| 7 | +practices to get the performance characteristics you want. Or maybe you just |
| 8 | +throw up your hands in disgust and decide to shell out to an implementation in |
| 9 | +a less sugary-wonderful *unsafe* language. |
82 | 10 |
|
| 11 | +Worse, when you want to talk directly to the operating system, you *have* to |
| 12 | +talk to an unsafe language: *C*. C is ever-present and unavoidable. It's the |
| 13 | +lingua-franca of the programming world. |
| 14 | +Even other safe languages generally expose C interfaces for the world at large! |
| 15 | +Regardless of *why* you're doing it, as soon as your program starts talking to |
| 16 | +C it stops being safe. |
| 17 | + |
| 18 | +With that said, Rust is *totally* a safe programming language. |
| 19 | + |
| 20 | +Well, Rust *has* a safe programming language. Let's step back a bit. |
| 21 | + |
| 22 | +Rust can be thought of as being composed of two |
| 23 | +programming languages: *Safe* and *Unsafe*. Safe is For Reals Totally Safe. |
| 24 | +Unsafe, unsurprisingly, is *not* For Reals Totally Safe. In fact, Unsafe lets |
| 25 | +you do some really crazy unsafe things. |
| 26 | + |
| 27 | +Safe is *the* Rust programming language. If all you do is write Safe Rust, |
| 28 | +you will never have to worry about type-safety or memory-safety. You will never |
| 29 | +endure a null or dangling pointer, or any of that Undefined Behaviour nonsense. |
| 30 | + |
| 31 | +*That's totally awesome*. |
| 32 | + |
| 33 | +The standard library also gives you enough utilities out-of-the-box that you'll |
| 34 | +be able to write awesome high-performance applications and libraries in pure |
| 35 | +idiomatic Safe Rust. |
| 36 | + |
| 37 | +But maybe you want to talk to another language. Maybe you're writing a |
| 38 | +low-level abstraction not exposed by the standard library. Maybe you're |
| 39 | +*writing* the standard library (which is written entirely in Rust). Maybe you |
| 40 | +need to do something the type-system doesn't understand and just *frob some dang |
| 41 | +bits*. Maybe you need Unsafe Rust. |
| 42 | + |
| 43 | +Unsafe Rust is exactly like Safe Rust with *all* the same rules and semantics. |
| 44 | +However Unsafe Rust lets you do some *extra* things that are Definitely Not Safe. |
| 45 | + |
| 46 | +The only things that are different in Unsafe Rust are that you can: |
| 47 | + |
| 48 | +* Dereference raw pointers |
| 49 | +* Call `unsafe` functions (including C functions, intrinsics, and the raw allocator) |
| 50 | +* Implement `unsafe` traits |
| 51 | +* Mutate statics |
| 52 | + |
| 53 | +That's it. The reason these operations are relegated to Unsafe is that misusing |
| 54 | +any of these things will cause the ever dreaded Undefined Behaviour. Invoking |
| 55 | +Undefined Behaviour gives the compiler full rights to do arbitrarily bad things |
| 56 | +to your program. You definitely *should not* invoke Undefined Behaviour. |
| 57 | + |
| 58 | +Unlike C, Undefined Behaviour is pretty limited in scope in Rust. All the core |
| 59 | +language cares about is preventing the following things: |
| 60 | + |
| 61 | +* Dereferencing null or dangling pointers |
| 62 | +* Reading [uninitialized memory][] |
| 63 | +* Breaking the [pointer aliasing rules][] |
| 64 | +* Producing invalid primitive values: |
| 65 | + * dangling/null references |
| 66 | + * a `bool` that isn't 0 or 1 |
| 67 | + * an undefined `enum` discriminant |
| 68 | + * a `char` outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF] |
| 69 | + * A non-utf8 `str` |
| 70 | +* Unwinding into another language |
| 71 | +* Causing a [data race][race] |
| 72 | +* Double-dropping a value |
| 73 | + |
| 74 | +That's it. That's all the Undefined Behaviour baked into Rust. Of course, unsafe |
| 75 | +functions and traits are free to declare arbitrary other constraints that a |
| 76 | +program must maintain to avoid Undefined Behaviour. However these are generally |
| 77 | +just things that will transitively lead to one of the above problems. Some |
| 78 | +additional constraints may also derive from compiler intrinsics that make special |
| 79 | +assumptions about how code can be optimized. |
| 80 | + |
| 81 | +Rust is otherwise quite permissive with respect to other dubious operations. Rust |
| 82 | +considers it "safe" to: |
| 83 | + |
| 84 | +* Deadlock |
| 85 | +* Have a [race condition][race] |
| 86 | +* Leak memory |
| 87 | +* Fail to call destructors |
| 88 | +* Overflow integers |
| 89 | +* Abort the program |
| 90 | +* Delete the production database |
| 91 | + |
| 92 | +However any program that actually manages to do such a thing is *probably* |
| 93 | +incorrect. Rust provides lots of tools to make these things rare, but |
| 94 | +these problems are considered impractical to categorically prevent. |
| 95 | + |
| 96 | +[pointer aliasing rules]: references.html |
| 97 | +[uninitialized memory]: uninitialized.html |
| 98 | +[race]: races.html |
0 commit comments