Do you have a recursive function by any chance? It could be that in the case of panic=unwind, the cleanup code prevents tail-call optimization from being applied, causing O(1) stack usage to turn into O(n).
I think with panic=abort those large nested structs are constructed in-place after allocating the Box, while with panic=unwind they are first constructed on the stack and then moved into the Box in case memory allocation panics to preserve behavior when the memory allocation panics.
This is a common problem in Rust when using a lot of async fn + .await, without any Box allocation.
Whenever you use .await, Rust merges the state of the future being awaited with the future that does awaiting. If you write an async app this way, then all those states of all those futures get merged together, and snowball into one giant Future struct. The entire state of your entire application, for all the async calls it can make anywhere, ends up bundled together, and then it doesn't fit on the stack.
The problem would also happen with panic=abort, but you see it for unwind first, because unwinding happens to generate more code and preserve more state. With unwinding the giant whole-program-in-one Future is slightly larger and/or the code generating and passing it is less compact.
Wrap less frequently called functions call().await in a pinned Box:
Box::pin(call()).await
and use cargo clippy to find what large futures remain:
Adjust Clippy config to a lower threshold if necessary:
Today, I upgraded all dependencies + Rust from 1.83 to 1.87 + Edition from 2021 to 2024 and I don't have stack overflows anymore. And there is no single Box::pin in my code.
I thought I was doing something wrong so I wiped everything (.cargo, .rustup, target, Cargo.lock) and I re-set Cargo.toml to
and everything is just fine. I run cargo run -- ... and no stack overflow.
So I'm not sure if it's the Rust version, LLVM, or maybe Tokio, but it's all good now...
I guess there was a memory leak somewhere. I had a feeling that I'm not doing anything crazy and 1 GiB of RAM should be totally enough... And now 2 MiB (!!!) is enough