You can interoperate via C ABI and just not use the C++ standard types across modules - which is the sane thing to do. Every other language that supports FFI via C linkage does this, only C++ insists on this craziness.
Also I wouldn't start by rewriting the thing that calls do_something, I'd start by rewriting do_something. Calling into rust from c++ using something like zngur lets you define rust types in c++ and then call idiomatic rust. You can't do it in the opposite direction because you cannot safely represent all c++ types in rust, because some of them aren't safe.
I have millions of lines of C++. do_something exists and is used but a lot of those lines and works well. I have a new feature that needs to call do_something. I'm not rewriting any code. My current code base was a rewrite of previous code into C++ started before rust existed), and it costs a nearly a billion dollars! I cannot go to my bosses and say that expensive rewrite that is only now starting to pay off because of how much better our code is needs to be scrapped. Maybe in 20 years we can ask for another billion (adjust for inflation) to rewrite again, but today either I write C++, or I interoperate with existing C++ with minimal effort.
I'm working on interoperation with existing C++. It is a hard problem and so far every answer I've found means all of our new features still needs to be written in C++ but now I'm putting in a framework where that code could be used by non-C++. I hope in 5 years that framework is in place by enough that early adopters can write something other than C++ - only time will tell though.
Yeah that use case is harder, but I'm involved in a similar one. Our approach is to split off new work as a separate process when possible and do it entirely in rust. You can call into c++ from rust, it just means more unsafe code in rust wrapping the c++ that has to change when you or your great grandchild finally do get around to writing do_something in rust. I am super aware of how daunting it is, especially if your customer base isn't advocating for the switch. Which most don't care until they get pwned and then they come with lawyers. Autocxx has proven a painful way to go. The chrome team has had some input to stuff and seem to be making it better.
Sure I can do that - but my example C++ function is fully memory safe (other than don't go off the end of the vector which static rules can enforce by banning []). If I make a C wrapper I just lost all the memory safety and now I'm at higher risk. Plus the effort to build that wrapper is not zero (though there are some generators that help)
How about going off the end of the vector with an iterator, or modifying the vector while iterating it, or adding to the vector from two different threads or reading from one thread while another is modifying it or [...].
There is nothing memory safe whatsoever about std::vector<something> and std::string. Sure, they give you access to their allocated length, so they're better than something[] and char* (which often also know the size of their allocations, but refuse to tell you).
> going off the end of the vector with an iterator,
The point of an iterator is to make it hard to do that. You can, but it is easy to not do that.
> modifying the vector while iterating it
Annoying, but in practice I've not found it hard to avoid.
> adding to the vector from two different threads or reading from one thread while another is modifying it
Rust doesn't help here - they stop you from doing this, but if threads are your answer rust will just say no (or force you into unsafe). Threads are hard, generally it is best to avoid this in the first place, but in the places where you need to modify data from threads Rust won't help.
> rust will just say no
This is just not accurate, you can use atomic data types, Mutex<> or RwLock<> to ensure thread-safe access. (Or write your own concurrent data structures, and mark them safe for access from a different thread.) C++ has equivalent solutions but doesn't check that you're doing the right thing.