Default allocation?

Primitive type - stack,
Array(data) - stack?
Slice(data) - stack?
Struct - heap?
Vector(data) - heap?

1 Like

Rust actually makes it very clear where different data types are allocated. By default, all bare types are allocated on the stack.

For example, struct goes on the stack unless placed in a Box, Rc, Vec, etc. Same with arrays.

Slices can refer to heap or stack allocated data; they only contain a pointer to somewhere else and a count of available values. In fact, it is safe to transmute &T to &[T; 1] or &[T] with a length of 1, because the layout of the data it points to is the same.

4 Likes

Is there a way to figure out what else might fall under the "etc" category? (Other than reading the source)

The source. Or the documentation.

Things which are Copy generally don't own heap values; things which are Drop might.

But the distinction between stack and heap doesn't matter so much in Rust. Copy and ownership are more important, and thinking in terms of these is easier.

Generally, anything where the size of the data it contains cannot be determined at compile time, or when a consistent location in memory is required and a static too accessible or long-lived.

Containers:

  • Box
  • Rc
  • Arc

Collections types:

  • Vec, VecDeque
  • String
  • HashMap, HashSet
  • BTreeMap, BTreeSet

Concurrency:

  • thread::spawn() : the closure is boxed so it can be passed as a pointer into the new thread's environment (required by the C-based thread APIs)
  • JoinHandle: the return value of the closure passed to thread::spawn() is returned as a pointer (same)
  • Sender/SyncSender & Receiver

I/O:

  • BufReader
  • The TCP streams are buffered at the OS level, I don't know how much Rust has a say in those buffer sizes
  • stdin, stdout, and stderr use global heap-allocated buffers (which you interact with whenever you invoke println!() or panic!())

FFI:

  • CString
  • OsString

I'm probably missing a lot but those are some of the more common examples of heap allocation done in the stdlib.

Another way of thinking about this is that Rust will stack allocate everything, unless you specifically use alloc::heap::allocate() or the box keyword. Types may use this internally. For example, when you create a new Arc<T>, it uses box internally: https://github.com/rust-lang/rust/blob/master/src/liballoc/arc.rs#L188

That's why it's on the heap. It's not that Rust itself chooses to default to allocate, it's that you create an Arc<T> struct on the stack, and its new() function creates a space on the heap, and then sets a pointer to that heap-allocated memory from inside your stack-allocated Arc<T> struct. We conversationally say that Arc<T> is "on the heap", but it's really the contents that are.

Does that help?

5 Likes

Speaking of the box keyword, any news on when that goes into stable?