84136


以下の内容をお聞きしたいです。

## 背景

マルチスレッドについて少し触ったので配列を分割して和を、各スレッドで取得できるようにしようと考えました。

そこで以下のコードを作成しました。

  * main.rs([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b2eb0c31cbc92a2c3413aed9c138ebd9))

[code]

    use rand::prelude::*;
    use std::thread;
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    
    fn main() {
        const N: usize = 10;
        const SIZE: usize = 10000000;
        let mut tasks = vec![];
    
        println!("[make array]");
        let arr = create_rand_array(SIZE);
        let result = Arc::new(AtomicUsize::new(0));
        for i in 0..N - 1 {
            let picked_arr = pick_array(i * N, SIZE / N, &arr);
            let result = Arc::clone(&result);
            let handle = thread::spawn(move || {
                result.fetch_add(sum(picked_arr), Ordering::SeqCst);
            });
            tasks.push(handle);
        }
    
        for task in tasks {
            task.join().unwrap();
        }
    
        println!("sum: {:?}", result);
    }
    
    fn sum(arr: &[usize]) -> usize {
        let mut result: usize = 0;
        arr.iter().for_each(|num| {
            result += num
        });
        result
    }
    
    fn create_rand_array(num: usize) -> Vec<usize> {
        let mut arr: Vec<usize> = Vec::with_capacity(num);
        let mut rng = rand::thread_rng();
        (1..num).for_each(|_| {
            arr.push(rng.gen::<usize>() % 1000);
        });
        arr
    }
    
    fn pick_array(start: usize, num: usize, arr: &[usize]) -> &[usize] {
        if start >= 0 && start + num < arr.len() {
            &arr[start..start + num - 1]
        } else {
            panic!("Invalid Args: start -> {}, num -> {}, arr.len() -> {}", start, num, arr.len());
        }
    }
    
[/code]

このコードを実行したところ

[code]

       Compiling playground v0.0.1 (/playground)
    error[E0597]: `arr` does not live long enough
      --> src/main.rs:15:54
       |
    15 |         let picked_arr = pick_array(i * N, SIZE / N, &arr);
       |                          ----------------------------^^^^-
       |                          |                           |
       |                          |                           borrowed value does not live long enough
       |                          argument requires that `arr` is borrowed for `'static`
    ...
    28 | }
       | - `arr` dropped here while still borrowed
    
[/code]

となりました。

自分としては

[code]

    for task in tasks {
        task.join().unwrap();
    }
    
[/code]

があるから`arr`の参照である`picked_arr`がdropされるまで(`thread::spawn`のクロージャを抜けるまで)、`arr`は存在できるのではないかと考えています。

## お聞きしたいこと

  * このコンパイルエラーを解消する方法をお聞きしたいです


CC BY-SA 4.0
2021-12-14T13:44:48.523
84134
2022-08-31T00:35:57.997
24870
question
rust
マルチスレッドで配列の和を取りたい


`thread::spawn`で生成したスレッドに変数をムーブ（所有権を移動）するには、その変数が `'static`
ライフタイムを持っている必要があります。生成されたスレッドの処理は、おおもとのプロセスが終了する（メインスレッドが終了する）まではどこまでも続く可能性があるからです。

`pick_array`関数のライフタイムを明示的に書くと次のようになり、質問にある通り`picked_arr` と`&arr`
のライフタイムは等しくなります。

[code]

    fn pick_array<'a>(start: usize, num: usize, arr: &'a [usize]) -> 
     &'a [usize] {
    
[/code]

ここでは`picked_arr`がスレッドにムーブされているので `'static`
が必要になり、`pick_array`の定義によりライフタイムが伝搬されたため `&arr` にも `'static`
が要求されている、ということになります。

とりあえずコンパイルが通るようにするには、`pick_array`から`Vec<usize>`を返すようにする方法があります（[Playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2021&gist=d7c43ce9c8f401d09eb14fc020013538)）。効率の良い方法ではありませんが…

[code]

    fn pick_array(start: usize, num: usize, arr: &[usize]) -> Vec<usize> {
        if start >= 0 && start + num < arr.len() {
            arr[start..start + num - 1].into()
    
[/code]

また、`pick_array`のようにスライスの要素をまとめて取ってくるには、[`chunks`](https://doc.rust-
lang.org/std/primitive.slice.html#method.chunks)が便利です。他にも[`fold`](https://doc.rust-
lang.org/std/iter/trait.Iterator.html#method.fold)や[`map`](https://doc.rust-
lang.org/std/iter/trait.Iterator.html#method.map)を使って書き換えると次のようになります。こちらでは[`Box::leak`](https://doc.rust-
lang.org/std/boxed/struct.Box.html#method.leak)を使って `'static`
ライフタイムを持つ参照を生成しています。

[code]

    use rand::prelude::*;
    use std::thread::{self, JoinHandle};
    use std::sync::Arc;
    use std::sync::atomic::{AtomicUsize, Ordering};
    
    fn main() {
        const N: usize = 10;
        const SIZE: usize = 1000;
    
        println!("[make array]");
        let result = Arc::new(AtomicUsize::new(0));
        let arr = Box::new(create_rand_array(SIZE));
        let arr = Box::leak(arr);
        arr.chunks(N).map(|chunk| {
            let result = Arc::clone(&result);
            thread::spawn(move || {
                let sum = chunk.iter().fold(0, |sum, num| sum + *num);
                result.fetch_add(sum, Ordering::SeqCst);
            })
        })
    　　 .collect::<Vec<JoinHandle<()>>>().into_iter()
        .for_each(|handle| handle.join().unwrap());
    
        println!("sum: {:?}", result);
    }
    
    fn create_rand_array(num: usize) -> Vec<usize> {
        let mut rng = rand::thread_rng();
        (1..num).map(|_| rng.gen::<usize>() % 1000).collect()
    }
    
[/code]

コメントの指摘にある通り、`collect`を呼ばないとイテレータが[遅延評価](https://doc.rust-
lang.org/std/iter/index.html#laziness)されるため、それぞれのスレッドが`spawn`された後にすぐ`join`されてしまいます（比較:
[Playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2021&gist=cd43e7f351ce4dd460b8dff9f0590a8b)）。

ちなみに、以前は
`thread::scoped`という「スコープ付きスレッド」のようなものがあり、`'static`ではない参照を使えたのですが、いろいろとややこしい問題があり廃止されました（下記リンクを参照）。現時点では[crossbeam](https://docs.rs/crossbeam/0.8.1/crossbeam/fn.scope.html),
[thread-scoped](https://github.com/arcnmx/thread-scoped-rs)クレートから同等の機能を使えます。
_2022/8/31更新：Rust 1.63.0でスコープ付きスレッド[`std::thread::scope`](https://doc.rust-
lang.org/stable/std/thread/fn.scope.html) が復活しました。_

参考リンク

  * [std::thread::JoinGuard (and scoped) are unsound because of reference cycles · Issue #24292 · rust-lang/rust](https://github.com/rust-lang/rust/issues/24292)
  * [Threads and Lifetimes : rust](https://www.reddit.com/r/rust/comments/8epwbe/threads_and_lifetimes/)
  * [Lifetime elision - The Rust Reference](https://doc.rust-lang.org/reference/lifetime-elision.html) (`substr2` の例に該当）


CC BY-SA 4.0
2021-12-14T17:15:56.277
84136
2022-08-31T00:35:57.997
2022-08-31T00:35:57.997
7500
7500
84134
answer