Search code examples
memoryrusttraits

How do boxed traits interact with memory when using match in Rust?


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.


Solution

  • 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.