Почему не сбрасывается этот MutexGuard?

В главе 20 The Rust Book приведен пример реализации пул потоков построен. Задания передаются рабочим через канал с одним производителем и несколькими потребителями: у каждого рабочего есть Arc<Mutex<Receiver>> для выбора заданий из очереди.

Первый пример тела рабочего потока выглядит так:

loop {
    let job = receiver.lock().unwrap().recv().unwrap();

    println!("Worker {} got a job; executing.", id);

    job();
}

Когда я увидел это, моей первой мыслью было «но мьютекс удерживается, пока job запущен» (т.е. я не ожидал, что мьютекс будет выпущен до тех пор, пока возвращаемое значение lock не выйдет из области видимости в конце цикла).

Однако в книге приводится второй пример:

while let Ok(job) = receiver.lock().unwrap().recv() {
    println!("Worker {} got a job; executing.", id);

    job();
}

В книге говорится, что этот пример демонстрирует описанную мною проблему, то есть "блокировка остается в силе на время вызова job()". Далее говорится, что в предыдущем примере проблема не возникает, потому что «MutexGuard, возвращенный из метода lock, отбрасывается, как только оператор let job завершается».

Эта последняя часть заставляет звучать так, как будто, поскольку MutexGuard на самом деле никогда не присваивается переменной, его время жизни заканчивается, как только выражение завершает вычисление. Это имело бы смысл. Но разве это не относится ко второму примеру? Почему нахождение в while выражении изменяет время жизни значения MutexGuard?


person Brendan    schedule 21.11.2019    source источник


Ответы (2)


Ответ Майкла в основном правильный. Более подробную информацию можно найти в справочнике Rust, посвященном выражениям мест, выражениям значений и временные сроки службы:

В первом примере (let job = ...) MutexGuard является временным. Временное время жизни заканчивается в конце инструкции. Следовательно, MutexGuard удаляется после оператора let job = ....

Во втором примере while let ..., MutexGuard является частью проверяемого while-выражения. Это делает MutexGuard частью выражения значения в контексте выражения места. Поскольку повышение до 'static не может произойти, время жизни всего scrutinee - это включающий блок, а не включающий оператор. То есть MutexGuard сохраняется для всего while let-блока.

person user2722968    schedule 21.11.2019
comment
Эта первая ссылка содержит контрпример моему упрощенному взгляду на происходящее. while foo(&temp()) {bar();}. Временный объект, содержащий значение, возвращаемое из вызова temp (), создается в условном выражении цикла. Следовательно, он будет освобожден в конце условного выражения цикла; в этом примере перед вызовом bar, если выполняется тело цикла. В моем объяснении было бы &temp() дожить до конца цикла - person Michael Anderson; 22.11.2019
comment
Выражение foo(&temp()) не привязано, это while x, а не while let x = y. Поэтому &temp() можно освободить в конце условного выражения цикла. Это привязка через while let x = y, которая требует продления временного времени жизни y на весь блок, поэтому можно фактически использовать x. - person user2722968; 22.11.2019
comment
Для меня не очевидно, почему Rust работает именно так. Это функция, которую используют люди, или просто случайность? - person Brendan; 29.11.2019

Временные объекты очищаются в конце оператора, а не в конце выражения.

В этом случае весь блок while является оператором, а не только выражением в let сопоставлении с шаблоном.

person Michael Anderson    schedule 21.11.2019
comment
Этот способ мышления о поведении чрезмерно упрощен, ответ пользователя 2722968 более полный (но требует более глубокого понимания того, как сочетается весь синтаксис rusts). - person Michael Anderson; 22.11.2019