What happens if you try to mutate a shared object from two threads, without using mutexes and locks, in languages without either Rust's compiler-enforced ownership or a global interpreter lock?
Java's collections don't (and can't, without high cost) make any guarantees that they will throw such an exception whenever they are modified concurrently... for one, data races can have weird consequences that aren't easy to detect. On that point, data races in Java are not undefined behaviour, they just have very weak guarantees about what happens (basically isolated loads and stores do reasonable things, and no fancier operations are available) which is another alternative to the Rust approach and the GIL approach.
That's hardly any better than undefined behavior. It's not the program you want to write, under any possible circumstance, and the language should tell you so.
It is significantly better than undefined behaviour, as it results in a noisy failure rather than silently incorrect results. Compile-time failures are best, sure, but I'll always take an exception over what is essentially data corruption.
Pedantically, it's defined behavior, and doesn't use an interpreter lock.
Granted, I'm completely head over heels for Rust, and I agree completely that ConcurrentModificationException is a crappy answer, but it is defined behavior (AFAIK).
??? Ask any C programmer if they could wave their magic wand and turn every single undefined behavior in their programs into a segfault how much better their life would be. It's not a small improvement it's a huge improvement.