|
| 1 | +- Start Date: 2014-08-08 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- Rust Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Divide global declarations into two categories: |
| 8 | + |
| 9 | +- **constants** declare *constant values*. These represent a value, |
| 10 | + not a memory address. This is the most common thing one would reach |
| 11 | + for and would replace `static` as we know it today in almost all |
| 12 | + cases. |
| 13 | +- **statics** declare *global variables*. These represent a memory |
| 14 | + address. They would be rarely used: the primary use cases are |
| 15 | + global locks, global atomic counters, and interfacing with legacy C |
| 16 | + libraries. |
| 17 | + |
| 18 | +# Motivation |
| 19 | + |
| 20 | +We have been wrestling with the best way to represent globals for some |
| 21 | +times. There are number of interrelated issues: |
| 22 | + |
| 23 | +- *Significant addresses and inlining:* For optimization purposes, it |
| 24 | + is useful to be able to inline constant values directly into the |
| 25 | + program. It is even more useful if those constant values do not have |
| 26 | + a known address, because that means the compiler is free to replicate |
| 27 | + them as it wishes. Moreover, if a constant is inlined into downstream |
| 28 | + crates, than they must be recompiled whenever that constant changes. |
| 29 | +- *Read-only memory:* Whenever possible, we'd like to place large |
| 30 | + constants into read-only memory. But this means that the data must |
| 31 | + be truly immutable, or else a segfault will result. |
| 32 | +- *Global atomic counters and the like:* We'd like to make it possible |
| 33 | + for people to create global locks or atomic counters that can be |
| 34 | + used without resorting to unsafe code. |
| 35 | +- *Interfacing with C code:* some C libraries require the use of |
| 36 | + global, mutable data. Other times it's just convenient and threading |
| 37 | + is not a concern. |
| 38 | +- *Initializer constants:* there must be a way to have initializer |
| 39 | + constants for things like locks and atomic counters, so that people |
| 40 | + can write `static MY_COUNTER: AtomicUint = INIT_ZERO` or some |
| 41 | + such. It should not be possible to modify these initializer |
| 42 | + constants. |
| 43 | + |
| 44 | +The current design is that we have only one keyword, `static`, which |
| 45 | +declares a global variable. By default, global variables do not have a |
| 46 | +significant address and can be inlined into the program. You can make |
| 47 | +a global variable have a *significant* address by marking it |
| 48 | +`#[inline(never)]`. Furthermore, you can declare a mutable global |
| 49 | +using `static mut`: all accesses to `static mut` variables are |
| 50 | +considered unsafe. Because we wish to allow `static` values to be |
| 51 | +placed in read-only memory, they are forbidden from having a type that |
| 52 | +includes interior mutable data (that is, an appearance of `UnsafeCell` |
| 53 | +type). |
| 54 | + |
| 55 | +Some concrete problems with this design are: |
| 56 | + |
| 57 | +- There is no way to have a safe global counter or lock. Those must be |
| 58 | + placed in `static mut` variables, which means that access to them is |
| 59 | + illegal. To resolve this, there is an alternative proposal which |
| 60 | + makes access to `static mut` be considered safe if the type of the |
| 61 | + static mut meets the `Sync` trait. |
| 62 | +- The signifiance (no pun intended) of the `#[inline(never)]` annotation |
| 63 | + is not intuitive. |
| 64 | +- There is no way to have a generic type constant. |
| 65 | + |
| 66 | +Other less practical and more aesthetic concerns are: |
| 67 | + |
| 68 | +- Although `static` and `let` look and feel analogous, the two behave |
| 69 | + quite differently. Generally speaking, `static` declarations do not |
| 70 | + declare variables but rather values, which can be inlined and which |
| 71 | + do not have a fixed address. You cannot have interior mutability in |
| 72 | + a `static` variable, but you can in a `let`. So that `static` |
| 73 | + variables can appear in patterns, it is illegal to shadow a `static` |
| 74 | + variable -- but `let` variables cannot appear in patterns. Etc. |
| 75 | +- There are other constructs in the language, such as nullary enum |
| 76 | + variants and nullary structs, which look like global data but in |
| 77 | + fact act quite differently. They are actual values which do not have |
| 78 | + a address. They are categorized as rvalues and so forth. |
| 79 | + |
| 80 | +# Detailed design |
| 81 | + |
| 82 | +## Constants |
| 83 | + |
| 84 | +Reintroduce a `const` declaration which declares a *constant*: |
| 85 | + |
| 86 | + const name: type = value; |
| 87 | + |
| 88 | +Constants may be declared in any scope. They cannot be shadowed. |
| 89 | +Constants are considered rvalues. Therefore, taking the address of a |
| 90 | +constant actually creates a spot on the local stack -- they by |
| 91 | +definition have no significant address. Constants are intended to |
| 92 | +behave exactly like a nullary enum variant. |
| 93 | + |
| 94 | +### Possible extension: Generic constants |
| 95 | + |
| 96 | +As a possible extension, it is perfectly reasonable for constants to |
| 97 | +have generic parameters. For example, the following constant is legal: |
| 98 | + |
| 99 | + struct WrappedOption<T> { value: Option<T> } |
| 100 | + const NONE<T> = WrappedOption { value: None } |
| 101 | + |
| 102 | +Note that this makes no sense for a `static` variable, which represents |
| 103 | +a memory location and hence must have a concrete type. |
| 104 | + |
| 105 | +### Possible extension: constant functions |
| 106 | + |
| 107 | +It is possible to imagine constant functions as well. This could help |
| 108 | +to address the problem of encapsulating initialization. To avoid the |
| 109 | +need to specify what kinds of code can execute in a constant function, |
| 110 | +we can limit them syntactically to a single constant expression that |
| 111 | +can be expanded at compilation time (no recursion). |
| 112 | + |
| 113 | + struct LockedData<T:Send> { lock: Lock, value: T } |
| 114 | + |
| 115 | + const LOCKED<T:Send>(t: T) -> LockedData<T> { |
| 116 | + LockedData { lock: INIT_LOCK, value: t } |
| 117 | + } |
| 118 | + |
| 119 | +This would allow us to make the `value` field on `UnsafeCell` private, |
| 120 | +among other things. |
| 121 | + |
| 122 | +## Static variables |
| 123 | + |
| 124 | +Repurpose the `static` declaration to declare static variables |
| 125 | +only. Static variables always have a single address. `static` |
| 126 | +variables can optionally be declared as `mut`. The lifetime of a |
| 127 | +`static` variable is `'static`. It is not legal to move from a static. |
| 128 | +Accesses to a static variable generate actual reads and writes: the |
| 129 | +value is not inlined (but see "Unresolved Questions" below). |
| 130 | + |
| 131 | +Non-`mut` statics must have a type that meets the `Sync` bound. All |
| 132 | +access to the static is considered safe (that is, reading the variable |
| 133 | +and taking its address). If the type of the static does not contain |
| 134 | +an `UnsafeCell` in its interior, the compiler may place it in |
| 135 | +read-only memory, but otherwise it must be placed in mutable memory. |
| 136 | + |
| 137 | +`mut` statics may have any type. All access is considered unsafe. |
| 138 | +They may not be placed in read-only memory and their values may |
| 139 | + |
| 140 | +# Drawbacks |
| 141 | + |
| 142 | +This RFC introduces two keywords for global data. Global data is kind |
| 143 | +of an edge feature so this feels like overkill. (On the other hand, |
| 144 | +the only keyword that most Rust programmers should need to know is |
| 145 | +`const` -- I imagine `static` variables will be used quite rarely.) |
| 146 | + |
| 147 | +# Alternatives |
| 148 | + |
| 149 | +The other design under consideration is to keep the current split but |
| 150 | +make access to `static mut` be considered safe if the type of the |
| 151 | +static mut is `Sync`. For the details of this discussion, please see |
| 152 | +[RFC 177](https://github.com/rust-lang/rfcs/pull/177). |
| 153 | + |
| 154 | +One serious concern is with regard to timing. Adding more things to |
| 155 | +the Rust 1.0 schedule is inadvisable. Therefore, it would be possible |
| 156 | +to take a hybrid approach: keep the current `static` rules, or perhaps |
| 157 | +the variation where access to `static mut` is safe, for the time |
| 158 | +being, and create `const` declarations after Rust 1.0 is released. |
| 159 | + |
| 160 | +# Unresolved questions |
| 161 | + |
| 162 | +- Should the compiler be allowed to inline the values of `static` |
| 163 | + variables which are deeply immutable (and thus force recompilation)? |
| 164 | + |
| 165 | +- Should we permit `static` variables whose type is not `Sync`, but |
| 166 | + simply make access to them unsafe? |
| 167 | + |
| 168 | +- How hard are the envisioned extensions to implement? If easy, they |
| 169 | + would be nice to have. If hard, they can wait. |
0 commit comments