> Generally the Rust community as well don't seem to have an answer on how to do this incrementally.
You can very much translate C to Rust on a function-by-function basis, the only issue is at the boundary where you're either left with unsafe interfaces or a "safe" but slow interop. But this is inherent since soundness is a global property, even a tiny bit of wrong unsafe code can spoil it all unless you do things like placing your untrusted code in a separate sandbox. So you can do the work incrementally, but much of the advantage accrues at the end.
Surely if you do this, you just end up expressing your C design in different syntax?
Doing the right thing means writing different functions with different signatures. Incrementalism here is very hard, and the smallest feasible bottom up replacement for existing functionality may be uncomfortably large. Top down is easier but it tends to lock in the incumbent design.
> You can very much translate C to Rust on a function-by-function basis, the only issue is at the boundary
Absolutely not. There are many restrictions of Rust that will prevent that. Lifetimes, global state come to mind first. Think about returning pointer to some owned by the caller - this can require massive cascading changes all over the codebase to be fixed.