A typical example of a race condition could be for example a missed update, e. g. if you implement an increment of a number by doing it in multiple steps
- read current value
- calculate value plus one
- write back result
Such increments can be missed if two threads do them simultaneously. (Both threads read 42, then both threads write back 43, even though the correct result after two increments should be 44).
You can implement this "badly" (but not unsoundly, so no "data race" and no UB) in Rust, too. E. g. you could use atomics, but without using the atomic increment but just atomic read followed by atomic write. Or you could use Mutex<i32>
but lock the mutex twice, once for the read (then unlock and calculate) then again for the write.
Of course "good" implementations on the other hand would do this either with atomics which can turn the 3 steps into a single one, or with a mutex that's kept locked throughout all 3 steps (or with some kind of loop that checks the value hasn't changed before doing the write operation, e. g. with compare-and-swap operation of atomics, and retries otherwise).
Many "memory safe" programming languages that aren't Rust avoid the UB from data races by turning them all into "just race conditions". Of course these race conditions are capable of leaving your not properly synchronized data structures in very invalid states (in case they care about invariants between multiple parts of the data structures beind upheld) so it let's you quickly get into "almost arbitrary program mi's-behavior but not UB" land. But it's probably the best they can achieve if they don't have Rust's Send/Sync system and aren't purely functional ("no mutation allowed") or avoid shared-memory ("only message passing").
Of course this is not to say that message passing prevents all race conditions; it just prevents data races, and makes bad cases of race conditions less easy to run into.
Anyways, as for 'what is a race condition' I would classify this example as "program behavior that depends on thread interleaving in ways it isn't supposed to producing results it isn't supposed to". In general, race conditions don't have to be about threads though, they could be about processes that interact, or even different servers that communicate, or a user interacting with a gui mutating data while some background thread is also mutating it, etc... usually "race condition" would refer to something that's a bug from some sort of - let's say "concurrent interaction"? - and often to bugs that are a bit more unlikely to actually occur. (Which makes them harder to observe, reproduce, etc.)
Edit: The Wikipedia linked above claims it doesn't have to refer to a bug. I guess that's a fair usage of the term, too, if you want to make it have that meaning. Cases where you don't care about different outcomes tbst depend on thread interleavings or the like, certainly exist.