Finishing pass through `const`

case-study-borrow-checker
Bradlee Speice 2019-01-21 13:26:39 -05:00
parent 5148bf3fc8
commit 5d75e0c9fa
1 changed files with 27 additions and 19 deletions

View File

@ -127,15 +127,15 @@ Given this information, the compiler can efficiently lay out your memory usage s
that the program never needs to ask the kernel/allocator for memory! This example
was a bit silly though, so let's talk about the more interesting details.
## **static** and **const**: Program Allocations
## **const** and **static**: Program Allocations
The first memory type we'll look at is pretty special: when Rust can prove that
a *reference* is valid for the lifetime of the program (`static`, not specifically
`'static`), and when a *value* is the same for the lifetime of the program (`const`).
Understanding the distinction between reference and value is important for reasons
a *value* is fixed for the life of a program, and when a *reference* is valid for
the duration of the program (`static`, not specifically `'static`).
Understanding the distinction between value and reference is important for reasons
we'll go into below. The
[full specification](https://github.com/rust-lang/rfcs/blob/master/text/0246-const-vs-static.md)
for these two memory types is available, but I'd rather take a hands-on approach to the topic.
for these two memory types is available, but we'll take a hands-on approach to the topic.
### **const**
@ -143,13 +143,16 @@ The quick summary is this: `const` declares a read-only block of memory that is
as part of your program binary (during the call to [exec(3)](https://linux.die.net/man/3/exec)).
Any `const` value resulting from calling a `const fn` is guaranteed to be materialized
at compile-time (meaning that access at runtime will not invoke the `const fn`),
even though the function is available at run-time as well. The compiler can choose to
copy the constant value wherever it is deemed practical. Getting the address of a `const`
value is legal, but not guaranteed to be the same even when referring to the same
named identifier.
even though the `const fn` functions are available at run-time as well. The compiler
can choose to copy the constant value wherever it is deemed practical. Getting the address
of a `const` value is legal, but not guaranteed to be the same even when referring to the
same named identifier.
The first point is a bit strange - "read-only memory". *Typically* in Rust you can use
"inner mutability" to modify things that aren't declared `mut`.
The first point is a bit strange - "read-only memory".
[The Rust book](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants)
mentions in a couple places that using `mut` with constants is illegal,
but it's also important to demonstrate just how immutable they are. *Typically* in Rust
you can use "inner mutability" to modify things that aren't declared `mut`.
[`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) provides an API
to guarantee at runtime that some consistency rules are enforced:
@ -212,12 +215,15 @@ fn main() {
```
-- [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c3cc5979b5e5434eca0f9ec4a06ee0ed)
[Clippy](https://github.com/rust-lang/rust-clippy) will treat this behavior as an error if attempted,
When the [`const` specification](https://github.com/rust-lang/rfcs/blob/26197104b7bb9a5a35db243d639aee6e46d35d75/text/0246-const-vs-static.md)
refers to ["rvalues"](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3055.pdf), this is
what they mean. [Clippy](https://github.com/rust-lang/rust-clippy) will treat this as an error,
but it's still something to be aware of.
The next thing to mention is that `const` values are loaded into memory *as part of your program binary*.
Because of this, any `const` values declared in your program will be "realized" at compile-time;
accessing them may trigger a main-memory lookup, but that's it.
accessing them may trigger a main-memory lookup (with a fixed address, so your CPU may
be able to prefetch the value), but that's it.
```rust
use std::cell::RefCell;
@ -250,18 +256,19 @@ pub fn multiply_twice(value: u32) -> u32 {
In this example, the `FACTOR` value is turned into the `mov edi, 1000` instruction
in both the `multiply` and `multiply_twice` functions; the "1000" value is never
"stored" anywhere, as it's small enough to use directly.
"stored" anywhere, as it's small enough to inline into the assembly instructions.
Finally, getting the address of a `const` value is possible but not guaranteed
to be unique (given that the compiler can choose to copy values). In my testing
I was never able to get the compiler to copy a `const` value and get differing pointers,
but the specifications are clear enough: *don't rely on pointers to `const`
values being consistent*. To be frank, I have no idea why you'd ever care about
a pointer to `const`.
values being consistent*. To be frank, caring about locations for `const` values
is almost certainly a code smell.
### **static**
Final note: `thread_local!()` is always a heap allocation.
1. What's going on with `lazy_static!()`?
2. What's going on with `thread_local!()`?
## **push** and **pop**: Stack Allocations
@ -276,6 +283,8 @@ Questions:
5. How do Option<> or Result<> affect structs?
6. How are arrays allocated?
7. Legal to pass an array as an argument?
8. Can you force a heap allocation with arrays that are larger than stack size?
- Check `ulimit -s`
# Piling On - Rust and the Heap
@ -290,6 +299,7 @@ Questions:
- If it uses `dyn Trait`, it's on the heap.
4. Other pointer types? Do Rc<>/Arc<> force heap allocation?
- Maybe? Part of the alloc crate, but should use qadapt to check
5. How many allocations happen before `main()` is called?
# Compiler Optimizations Make Everything Complicated
@ -297,8 +307,6 @@ Example: Compiler stripping out allocations of Box<>, Vec::push()
# Appendix and Further Reading
[Embedonomicon]:
[embedonomicon]: https://docs.rust-embedded.org/embedonomicon/
[libc]: CRATES.IO LINK
[winapi]: CRATES.IO LINK