问题
The Rust book talks about having multiple readers and multiple mutable references to an object as a data race situation that may lead to issues.
For example, this code:
fn main() {
let mut x = 1;
let r1 = &mut x;
*r1 = 2;
let r2 = &mut x;
*r2 = 3;
println!("{}", r1);
println!("{}", r2);
}
will be rejected by Rust compiler because r1
and r2
scopes are intertwined.
But what is problem here? I mean, this is just one thread and there is no "reading and writing at the same time", so all these statements should be executed strictly sequentially and give deterministic reproducible result.
回答1:
From Niko Matsakis' blog:
I’ve often thought that while data-races in a technical sense can only occur in a parallel system, problems that feel a lot like data races crop up all the time in sequential systems. One example would be what C++ folk call iterator invalidation—basically, if you are iterating over a hashtable and you try to modify the hashtable during that iteration, you get undefined behavior. Sometimes your iteration skips keys or values, sometimes it shows you the new key, sometimes it doesn’t, etc.
But whatever the outcome, iterator invalidation feels very similar to a data race. The problem often arises because you have one piece of code iterating over a hashtable and then calling a subroutine defined over in some other module. This other module then writes to the same hashtable. Both modules look fine on their own, it’s only the combination of the two that causes the issue. And because of the undefined nature of the result, it often happens that the code works fine for a long time—until it doesn’t.
Rust’s type system prevents iterator invalidation.
Rust's type system disallows single-threaded programs like the one below to compile because they would result in Undefined Behavior and while that technically isn't a data race this particular error is in the same ballpark of "errors caused by two independent pieces of code mutating the same data in an interweaved fashion" so it's very similar to a data race and I believe that's what the Rust book was trying to communicate:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 1);
map.insert(2, 2);
map.insert(3, 3);
for _ in map.iter() {
map.insert(4, 4); // compile error!
}
}
playground
来源:https://stackoverflow.com/questions/61851916/how-do-intertwined-scopes-create-a-data-race