> If data structures require unsafe operations, doesn't this make Rust's safety a 90/10 solution, and leave a small but credible threat of what are effectively buffer overflow attacks?Somewhat gibly, every solution is a 90/10 solution (with different exact numbers of course): there has to be some assumptions/assertions buried in the system somewhere. For instance, correctness of the runtime/built-in types in "safe" languages like Python or Java or Haskell, behaviour of the operating system when doing syscalls (or behaviour of specific machine instructions), or even bug-free-ness of a theorem prover used. Obviously different classes have different rates of errors, but it is still very useful to reduce and focus the places where incorrect assumptions can lead to "critical failures" (memory safety ones) even if one doesn't/can't sink the effort/money into reducing it to (essentially) zero. All of these systems are a tradeoff in some sense, between guaranteed-correctness and things like performance, "productivity", and cost of development.
Rust's power is its ability to significantly narrow the places where memory unsafety is a risk without imposing a cost ("zero cost abstractions"), but still giving all the convention control and power needed for systems programming. There is of course the risk of those places having bugs, but they're explicitly marked and exhaustively testing/auditing the 100 lines of code that build safe abstraction is easier than a whole 1000 or 10000 application that uses it.
> that you can DDOS a web server by sending requests with just the right query parameters
The HashDOS problem is not a memory safety one. In some sense, it isn't even a bug in the implementation but rather the design. The problem arises from using a poor/predictible hash function that allows an attacker to construct many values with the same hash, even if the hash table and the hash function itself are implemented 100% to spec. It is... difficult for a programming language to protect against spec bugs, especially because what is a bug/not helpful sometimes might be desirable at other times.
(Incidentally, Rust's HashMap actually defends against this by default, using SipHash with random keys, which is why it lags behind, say, C++ in some benchmarks that use the default data structures.)