hyperion/20-dev/00-rust/30-fearless/Untitled.md

70 lines
No EOL
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Отличный и глубокий вопрос! На него часто дают неполный ответ ("перемещается замыкание"), но давайте разберем **конкретно по байтам**, что именно физически перемещается в новый поток в вашем коде.
### Короткий ответ
Когда вы пишете `move ||`, в новый поток перемещается **сама структура замыкания** (closure struct).
В вашем случае эта структура имеет размер всего 8 байт (на 64-битной системе) и содержит внутри **только один `Arc`**.
### Подробный разбор ("под капот")
#### 1. Что такое замыкание в Rust?
Компилятор Rust превращает ваше замыкание в анонимную структуру (struct). Поля этой структуры — это все переменные из внешнего окружения, которые вы захватили.
В вашем коде внутри `thread::spawn`:
```rust
move || {
// Используются:
// 1. i (копия, примитив)
// 2. chunk_size (копия, примитив)
// 3. data_ref (это Arc<Vec<i32>>)
// ... логика
}
```
Компилятор генерирует примерно такую структуру:
```rust
// Псевдокод сгенерированной структуры
struct ClosureEnvironment {
i: usize, // 8 байт
chunk_size: usize, // 8 байт
data_ref: Arc<Vec<i32>>, // 8 байт (указатель на кучу)
}
```
#### 2. Что делает `move`?
Ключевое слово `move` приказывает компилятору: "Забери владение всеми переменными, которые используются внутри `{...}`, и положи их в эту структуру `ClosureEnvironment`".
Без `move` компилятор попытался бы положить в структуру **ссылки** (`&i`, `&data_ref`), что вызвало бы ошибку компиляции, так как ссылки не могут пережить функцию `sum_squares_static_arc`.
#### 3. Что физически "перемещается"?
Когда вы вызываете `thread::spawn(move || ...)`:
1. Создается экземпляр этой структуры `ClosureEnvironment` на стеке текущего потока.
2. Этот экземпляр **перемещается** (memcpy) из стека главного потока в стек нового потока (или в кучу, если `Box::new`).
* `i` и `chunk_size` просто копируются (как `Copy` типы).
* `data_ref` (`Arc`) тоже копируется (бинарно это просто указатель `0x...`). **Сами данные массива `[i32]` никуда не копируются**, они лежат в куче и остаются на месте. Копируется только "умный указатель" на них.
### Итоговая картина
В новый поток "улетает" маленький пакет данных (размером 24 байта: `usize` + `usize` + `ptr`), который содержит:
1. Число `i` (индекс потока).
2. Число `chunk_size`.
3. Указатель `Arc` (который увеличил счетчик ссылок на данные, но сами данные не трогал).
В этом и есть суть эффективности: вы передаете гигабайты данных в другой поток, физически перемещая всего 8 байт указателя `Arc`.
### Важное уточнение про `Arc`
Обратите внимание, что перед циклом вы делаете:
```rust
let data_ref = Arc::clone(&data_arc);
```
Именно этот **новый** `data_ref` перемещается в замыкание. Оригинальный `data_arc` остается в `main` потоке нетронутым.
Каждая итерация цикла создает свой собственный "билетик" (`data_ref`), и именно этот билетик `move` забирает в новый поток.
[1](https://doc.rust-lang.org/std/sync/struct.Arc.html)
[2](https://stackoverflow.com/questions/72251938/implement-thread-safe-function-object-in-rust)
[3](http://rust-unofficial.github.io/too-many-lists/third-arc.html)
[4](https://users.rust-lang.org/t/implementing-thread-safe-structs-for-tokio/21658)
[5](https://onesignal.com/blog/thread-safety-rust/)
[6](https://www.rustfinity.com/blog/what-is-thread-safety)
[7](https://rust-training.ferrous-systems.com/latest/book/thread-safety)
[8](https://forums.swift.org/t/arc-automatic-reference-counting-is-thread-safe/63899)
[9](https://www.ardanlabs.com/blog/2024/11/fearless-concurrency-ep6-understanding-rust-arc-for-efficient-multithreading.html)