diff --git a/_drafts/a-heaping-helping.md b/_drafts/a-heaping-helping.md index ba1149d..e11546b 100644 --- a/_drafts/a-heaping-helping.md +++ b/_drafts/a-heaping-helping.md @@ -75,16 +75,20 @@ we'll follow this guide: - Boxed closures (FnBox, others?) are heap allocated - "Move" semantics don't trigger new allocation; just a change of ownership, so are incredibly fast + - In examples, is address of data before and after the same? + - Can `Copy` trigger allocation? - Stack-based alternatives to standard library types should be preferred (spin, parking_lot) -## Smart pointers +# Smart pointers The first thing to note are the "smart pointer" types. When you have data that must outlive the scope in which it is declared, or your data is of unknown or dynamic size, you'll make use of these types. The term [smart pointer](https://en.wikipedia.org/wiki/Smart_pointer) -comes from C++, and is used to describe objects that are responsible for managing +comes from C++, and while it's closely linked to a general design pattern of +["Resource Acquisition Is Initialization"](https://en.cppreference.com/w/cpp/language/raii), +we'll use it here specifically to describe objects that are responsible for managing ownership of data allocated on the heap. The smart pointers available in the `alloc` crate should look mostly familiar: - [`Box`](https://doc.rust-lang.org/alloc/boxed/struct.Box.html) @@ -97,10 +101,10 @@ though more than can be covered in this article. Some examples: - [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html) - [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) -Finally, there is one [gotcha](https://www.merriam-webster.com/dictionary/gotcha): +Finally, there is one ["gotcha"](https://www.merriam-webster.com/dictionary/gotcha): cell types (like [`RefCell`](https://doc.rust-lang.org/stable/core/cell/struct.RefCell.html)) -look and behave like smart pointers, but don't actually require heap allocation. -Check out the [`core::cell` docs](https://doc.rust-lang.org/stable/core/cell/index.html) +follow the RAII pattern, but don't involve heap allocation. Check out the +[`core::cell` docs](https://doc.rust-lang.org/stable/core/cell/index.html) for more information. When a smart pointer is created, the data it is given is placed in heap memory and @@ -138,7 +142,7 @@ pub fn my_cow() { ``` -- [Compiler Explorer](https://godbolt.org/z/SaDpWg) -## Collections +# Collections Collections types use heap memory because they have dynamic size; they will request more memory [when needed](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.reserve), diff --git a/_drafts/compiler-optimizations.md b/_drafts/compiler-optimizations.md index 5cfe710..13e6120 100644 --- a/_drafts/compiler-optimizations.md +++ b/_drafts/compiler-optimizations.md @@ -30,7 +30,7 @@ we're just focused on interesting things the Rust language can do. The guiding principal as we move forward is this: *optimizing compilers won't produce worse assembly than we started with.* There won't be any situations where stack allocations get moved to heap allocations. -There will, however, be an opera of optimization. +There will, however, be an opera of optimization. # The Case of the Disappearing Box diff --git a/_drafts/stacking-up.md b/_drafts/stacking-up.md index 91feeb8..42b3806 100644 --- a/_drafts/stacking-up.md +++ b/_drafts/stacking-up.md @@ -99,7 +99,7 @@ With all that in mind, let's talk about situations in which we're guaranteed to - Closures capture their arguments on the stack - Generics will use stack allocation, even with dynamic dispatch. -## Structs +# Structs The simplest case comes first. When creating vanilla `struct` objects, we use stack memory to hold their contents: @@ -132,7 +132,7 @@ pub fn make_line() { Note that while some extra-fancy instructions are used for memory manipulation in the assembly, the `sub rsp, 64` instruction indicates we're still working with the stack. -## Function arguments +# Function arguments Have you ever wondered how functions communicate with each other? Like, once the variables are given to you, everything's fine. But how do you "give" those variables to another function? @@ -237,7 +237,7 @@ and passing by reference (either moving ownership or passing a pointer) may have [slightly different layouts in assembly](https://godbolt.org/z/sKi_kl), but will still use either stack memory or CPU registers. -## Enums +# Enums If you've ever worried that wrapping your types in [`Option`](https://doc.rust-lang.org/stable/core/option/enum.Option.html) or @@ -275,7 +275,7 @@ in assembly, so I'll instead point you to the [`core::mem::size_of`](https://doc.rust-lang.org/stable/core/mem/fn.size_of.html#size-of-enums) documentation. -## Arrays +# Arrays The array type is guaranteed to be stack allocated, which is why the array size must be declared. Interestingly enough, this can be used to cause safe Rust programs to crash: @@ -320,7 +320,7 @@ There aren't any security implications of this (no memory corruption occurs), but it's good to note that the Rust compiler won't move arrays into heap memory even if they can be reasonably expected to overflow the stack. -## Closures +# Closures Rules for how anonymous functions capture their arguments are typically language-specific. In Java, [Lambda Expressions](https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html) @@ -387,7 +387,7 @@ pub fn complex() { In every circumstance though, the compiler ensured that no heap allocations were necessary. -## Generics +# Generics Traits in Rust come in two broad forms: static dispatch (monomorphization, `impl Trait`) and dynamic dispatch (trait objects, `dyn Trait`). While dynamic dispatch is often @@ -444,5 +444,5 @@ pub fn do_call() { ``` -- [Compiler Explorer](https://godbolt.org/z/u_yguS) -It's hard to imagine practical situations where dynamic dispatch -would be used for objects that aren't heap allocated, but it can be done. \ No newline at end of file +It's hard to imagine practical situations where dynamic dispatch would be +used for objects that aren't heap allocated, but it technically can be done. \ No newline at end of file diff --git a/_drafts/the-whole-world.md b/_drafts/the-whole-world.md index 18eecce..c7031f3 100644 --- a/_drafts/the-whole-world.md +++ b/_drafts/the-whole-world.md @@ -16,7 +16,7 @@ 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 we'll take a hands-on approach to the topic. -## **const** +# **const** The quick summary is this: `const` declares a read-only block of memory that is loaded as part of your program binary (during the call to [exec(3)](https://linux.die.net/man/3/exec)). @@ -144,7 +144,7 @@ but the specifications are clear enough: *don't rely on pointers to `const` values being consistent*. To be frank, caring about locations for `const` values is almost certainly a code smell. -## **static** +# **static** Static variables are related to `const` variables, but take a slightly different approach. When the compiler can guarantee that a *reference* is fixed for the life of a program,