The const can easily dropped later in production from both entities, potentionally creating two return values.
This is analougous to good use of privacy in C++ classes.
hero = spin(hero, key);
Becomes:
hero.spin(key);
Where external objects dictate the private state of the hero object.
Pointer aliasing is another thing. Inlining is easier with fewer pointers. A fun C++ experiment is to count your instruction and data cache points with valgrind's cachegrind with pod types both passed by value and by reference (&).
You might be surprised. For small structs, copying is very fast and may give you a result with better locality. Passing around structs by pointer means more pointer dereferencing, which has its own overhead and can cause cache misses.
You are correct. On a much larger project with a similar software engine, if I change all structs I pass by value to pass by reference my instruction count shoots up by 40% according to cachegrind (flto + ofast).
Amazing how powerful pass by value can be when it comes to pod structs.
static Hero spin(Hero hero, const uint8_t* key)
https://github.com/glouw/littlewolf/blob/339cb5624462d4f2301...