The Rust Reference recommends statics over consts for large amounts of data[1]. In addition, there is a Clippy lint warning when large const arrays are used[2]. However, I've been using const arrays fairly often and they have not been "inlined upon use" as the Clippy documentation states.
When compiling and linking this code into an ELF file, the array ends up in the .rodata section whether it's defined as const or static. The function does not store the array on the stack.
I was wondering whether this is just some sort of optimization and I should still declare my arrays static to guarantee this behavior or whether const is the way to go? If const is the way to go, does this mean the first bullet point from [1] should be removed?
Without optimizations, the array constant data will be copied onto the stack. With optimizations, the array will probably not be, but this is not actually guaranteed.
So, if the array size is in “high chance of stack overflow” territory, make sure to use a static. If it’s small enough to be merely a performance issue, it probably isn’t worth using a static.
This is an interesting topic. During the last 18 months I was never really able to understand exactly in which cases statics should be used instead of large constants. Of course I understand the difference, and possible optimizations. My private conclusion was then, that statics might be useful for FFI, when we have to pass larger constant data blocks to C functions. Related to this topic is, how Rust processes multiple identical string constants, e.g. &str literals. Most C and C++ compilers have an optimization for the case that identical string literals are used often, e.g. "Error: ". They have a special name for an optimization, which avoids that identical string literals occur multiple times in the final binary. But I can not remember the term.
Thanks for the confirmation. I also just realized I had opt-level=1 enabled in my godbolt setup. After taking it out the compiler generated code memcpying the array onto the stack in each function it was used.
So, if the array size is in “high chance of stack overflow” territory, make sure to use a static. If it’s small enough to be merely a performance issue, it probably isn’t worth using a static.
Although I guess if you still want decent performance on dev builds it makes sense to declare anything that's more than a few machine words wide static.
I kinda don't know of any situation where both static and const both work and yet const is better – except for readability. Note also that as time goes on const becomes less and less useful. E.g. starting from Rust 1.79 one can use static in the example above like this:
pub fn main() {
static X: usize = 42;
static Y: [usize; const { X }] = [const { X }; const { X }];
println!("{Y:?}")
}
But, of course, at some point repeating these endless const { X } becomes tedious… and using const here is Ok, because X is so small so that's fine.
I am not quite sure if Rust or the LLVM backend does interning for &str and arrays -- of course only for immutable data with static lifetime, e.g. literals.
Somewhere I saw
// Immutable static: lives for the program duration at a fixed address.
static APP_VERSION: &str = "1.0.2";
Maybe because they want to pass that address somewhere? String deduplication is not guaranteed in Rust (especially when different translation units are involved) thus if you want to rely on there being just one, single, &str it's better to use static.
Are you sure? That's mostly used for a specific case in dynamic languages: when you add new symbol the name of that symbol is placed in a hash table and thereafter can be used with address comparison (like above). I've not seen word “interning” used in C/C++ outside of such runtimes. Daniel Lemire's doesn't use that term, too.
Sorry, not really. I think where I read about the topic was in the Nim forum maybe 8 years ago, and as Nim typical compiles to C, someone wrote that the C backend would optimize identical string literals.
[Edit]
String pooling might be the correct term for C/C++, see