こんにちは。これはRustのライフタイムが絡むところですね。
今回の対象になるのはクロージャ内だけなのでそこを抜き出します。
rust
1{
2 let mut v = history.lock().unwrap();
3 (*v).push(&i.to_string());
4}
&i.to_string()
と一気に書かれていますが、これは分けて書くとこう書けます。
rust
1{
2 let mut v = history.lock().unwrap();
3 let tmp = i.to_string();
4 (*v).push(&tmp);
5}
この tmp
はブロック内で作られているのでライフタイムがブロックの末尾で終わります。
rust
1{
2 let mut v = history.lock().unwrap();
3 let tmp = i.to_string();
4 (*v).push(&tmp);
5 // tmpはここで終わり
6}
一方 .push(&tmp)
では tmp
への参照をブロックの外側へ持ち出そうとしています。
rust
1// 外側にあるhistory
2let history = ...
3{
4 let mut v = history.lock().unwrap();
5 let tmp = i.to_string();
6 (*v).push(&tmp);
7 // tmpはここで終わり
8}
9// historyにpushしたtmpへの参照はここまで有効
ブロックを抜けたときにはライフタイムが終わっている値への参照は無効なのでコンパイラがエラーを出しているのです。
参照ではなく値そのものを push
するようにすれば問題が解決します。このとき、型が変わるので history
の方も修正する必要がでてきます。
結果、コードはこうなります。
rust
1use std::sync::{Arc, Mutex};
2use std::thread;
3
4fn main() {
5 // Arc<Mutex<&str>> ではなく `Arc<Mutex<String>>` に
6 let history = Arc::new(Mutex::new(vec!["main thread".to_string()]));
7 for i in 1..=10 {
8 let history = Arc::clone(&history);
9 thread::spawn(move || {
10 let mut v = history.lock().unwrap();
11 // 参照をとらずに値をそのままpushする
12 v.push(i.to_string());
13
14 // 本筋から逸れますが、 `(*v).push` ではなく `v.push` で十分です。
15 // コンパイラが裏で `(*v)` に変換してくれます。
16 });
17 }
18
19 thread::sleep(std::time::Duration::from_secs(2));
20 println!("{:?}", history);
21}
22
もしよく分からないなら、公式ドキュメントの参照と借用やスライスが助けになるかもしれません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/22 11:03