I have the following function:
fn get_person(type: PersonType, age: u8) -> Result<Box<dyn Person>> {
Ok(match type {
PersonType::Thin => Box::new(ThinPerson::new(age)),
PersonType::Fat => Box::new(FatPerson::new(age)),
})
}
Let's assume ThinPerson
and FatPerson
both implement Person
trait. And FatPerson
struct requires a much larger memory footprint.
I believe during Box::new(...)
, ThinPerson
/FatPerson
structs are first created on the stack and then pushed off into the heap once boxed.
Initially, I had only ThinPerson
as an arm to the match
. Once I added FatPerson
, I noticed the stack usage has increased even if the code never traverses the PersonType::Fat
arm.
Can someone please explain why? Does the match
somehow looks at all the arms and allocates stack space according to the largest struct it finds?
As a secondary question, how would I avoid this stack bloat? I understand "placement by return" RFC is still in the design phase and I cannot step outside safe Rust.
Does the
match
somehow looks at all the arms and allocates stack space according to the largest struct it finds?
It must; stack space is allocate statically. Theoretically it could allocate dynamically, but this is way too hard and AFAIK proper alignment is still not supported in LLVM.
As a secondary question, how would I avoid this stack bloat? I understand "placement by return" RFC is still in the design phase and I cannot step outside safe Rust.
As @Caesar suggested, make the constructor function return a Box
(not necessarily Box<<dyn Person>
, can be Box<Self>
). This way the stack space will be allocated only if it will be actually called. It is also possible that if the constructor function will be inlined LLVM will write in place. If not, you can make it take &mut Self
and write to that instead of returning Self
, and pass it a zeroed FatPerson
or something like that. The problem is the return of Self
, LLVM can optimize the write itself quite easily.