今回はスレッドのレースコンディションを検出する例です。
use std::thread; fn main() { let mut data = 100; thread::spawn(|| { data = 100; }); thread::spawn(|| { data = 100; }); println!("{}", data); }
これをcargo run
すると次のようにどどっとコンパイルエラーがでます。
PS C:\Users\led_l\rust_land\race> cargo run Compiling race v0.1.0 (C:\Users\led_l\rust_land\race) error[E0373]: closure may outlive the current function, but it borrows `data`, which is owned by the current function --> src\main.rs:4:19 | 4 | thread::spawn(|| { | ^^ may outlive borrowed value `data` 5 | data = 100; | ---- `data` is borrowed here | note: function requires argument type to outlive `'static` --> src\main.rs:4:5 | 4 | / thread::spawn(|| { 5 | | data = 100; 6 | | }); | |______^ help: to force the closure to take ownership of `data` (and any other referenced variables), use the `move` keyword | 4 | thread::spawn(move || { | ++++ error[E0499]: cannot borrow `data` as mutable more than once at a time --> src\main.rs:7:19 | 4 | thread::spawn(|| { | - -- first mutable borrow occurs here | _____| | | 5 | | data = 100; | | ---- first borrow occurs due to use of `data` in closure 6 | | }); | |______- argument requires that `data` is borrowed for `'static` 7 | thread::spawn(|| { | ^^ second mutable borrow occurs here 8 | data = 100; | ---- second borrow occurs due to use of `data` in closure error[E0373]: closure may outlive the current function, but it borrows `data`, which is owned by the current function --> src\main.rs:7:19 | 7 | thread::spawn(|| { | ^^ may outlive borrowed value `data` 8 | data = 100; | ---- `data` is borrowed here | note: function requires argument type to outlive `'static` --> src\main.rs:7:5 | 7 | / thread::spawn(|| { 8 | | data = 100; 9 | | }); | |______^ help: to force the closure to take ownership of `data` (and any other referenced variables), use the `move` keyword | 7 | thread::spawn(move || { | ++++ error[E0502]: cannot borrow `data` as immutable because it is also borrowed as mutable --> src\main.rs:10:20 | 4 | thread::spawn(|| { | - -- mutable borrow occurs here | _____| | | 5 | | data = 100; | | ---- first borrow occurs due to use of `data` in closure 6 | | }); | |______- argument requires that `data` is borrowed for `'static` ... 10 | println!("{}", data); | ^^^^ immutable borrow occurs here Some errors have detailed explanations: E0373, E0499, E0502. For more information about an error, try `rustc --explain E0373`. error: could not compile `race` due to 4 previous errors
エラー出過ぎなので、読んでいません。ですが・・・
レースコンディションそのものを静的解析でみつけるのは、ほぼ不可能です。 「スレッド間での値の共有をデフォルトで禁止する。共有可能な変数をスレッドに渡すこと自体をエラーとする」という作戦はRubyのractorと同じなので、馴染みがあります。
今回気になったのはthread::spawn
というメソッドです。
クラスメソッド風に見えます。
名前空間付きの静的メソッドみたいなのがあるのでしょうか?
|| { data = 100; })
この書き方も馴染みがありません。
エラーメッセージをみると||
の左にmoveを足してmove ||
みたいな書き方をするようです。
所有権移転の書き方なのでしょうか?
サンプルプログラムをちょっと変えてスレッド内ではdata = 100;
と実質的な値は変わらないようにしました。
エラーコードは変わらないようです。
やはりrustはソースコードの振る舞いをチェックしているわけではなく、文法をチェックしているようです。