The Day const Betrayed Me
I love performance, so I love the const keyword. I wrote something like this in my real project:
mod static_data {
pub const DATA: &'static [u8] = include_bytes!("data.bin");
pub fn hash_bytes(bytes: &[u8]) -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
bytes.hash(&mut hasher);
hasher.finish()
}
pub fn data_hash() -> u64 {
hash_bytes(DATA)
}
}
fn main() {
const OUTPUT_PATH: &str = "output.bin";
if !std::fs::read(OUTPUT_PATH)
.is_ok_and(|data| static_data::hash_bytes(&data) == static_data::data_hash())
{
std::fs::write(OUTPUT_PATH, static_data::DATA).unwrap()
}
}
DATA is a reference with a 'static lifetime, so the content should be embedded only once. Right? That's what I thought, until one day I changed some unrelated code, and the output binary size suddenly dropped. I used AI to find the reason. It turned out that DATA had been embedded twice before. Huh? After some research, I found the reason:
Constants are essentially inlined wherever they are used, meaning that they are copied directly into the relevant context when used. This includes use of constants from external crates, and non-Copy types. References to the same constant are not necessarily guaranteed to refer to the same memory address.
It ruined my perfect dream about const. The solution is simple: use static instead. Lesson learned.
Note: To reproduce this with the example above, you need to explicitly set codegen-units to a value greater than 1 (e.g. codegen-units = 2), because rustc may merge small CGUs by default. Setting lto = "fat" will also optimize the duplicate data away.