Почему я получаю ошибку заимствования при использовании Option :: map, а не при сопоставлении вручную? - PullRequest
0 голосов
/ 28 апреля 2019

У меня есть эта рабочая функция:

fn clear_job(state: State<MyState>) -> Option<String> {
    let mut guard = state.job.lock().unwrap();
    let maybe_job: &mut Option<Job> = &mut *guard;
    // Job is not Copy nor Clone

    match maybe_job {
        None => None,
        Some(job) => match job {
            Job::InProgress { .. } => Some("Can't clear in progress job".into()),
            _ => {
                *maybe_job = None;
                Some("Job cleared".into())
            },
        },
    }
}

Я возвращаю None, если у меня есть None и Some, если у меня есть Some, что ... именно то, что Option::map() есть!

Я пытался переписать эту функцию:

fn clear_job(state: State<MyState>) -> Option<String> {
    let mut guard = state.job.lock().unwrap();
    let maybe_job: &mut Option<Job> = &mut *guard;

    maybe_job.as_ref().map(|job| match job {
        Job::InProgress { .. } => "Can't clear in progress job".into(),
        _ => {
            *maybe_job = None;
            "Job cleared".into()
        },
    })
}

Это дает мне эту ошибку:

error[E0500]: closure requires unique access to `maybe_job` but it is already borrowed
   --> src/main.rs:113:28
    |
113 |     maybe_job.as_ref().map(|job| match job {
    |     ---------          --- ^^^^^ closure construction occurs here
    |     |                  |
    |     |                  first borrow later used by call
    |     borrow occurs here
...
116 |             *maybe_job = None;
    |              --------- second borrow occurs due to use of `maybe_job` in closure

Я вроде понимаю, почему есть ошибка; что я не понимаю, так это то, как я не получаю ошибку в версии match, потому что семантически я делаю то же самое: читая в maybe_job, получая заем на job (так, предположительно на maybe_job тоже), но все еще пишу в *maybe_job.

Почему мне разрешено в первой версии, а не во второй? Есть ли способ использовать Option::map в этой ситуации?

1 Ответ

1 голос
/ 14 мая 2019

Я вроде понимаю, почему есть ошибка;что я не понимаю, так это то, как я не получаю сообщение об ошибке в версии match, потому что семантически я делаю одно и то же: чтение в maybe_job, получение заимствования на job (так, предположительно, на maybe_job тоже), но все еще записываю в *maybe_job.

В случае совпадения компилятор помогает вам, отбрасывая заимствование на job перед тем, как писать в *maybe_job.Вы можете проверить это сами - используйте job после выполнения *maybe_job = None, и вы явно заимствуете job слишком долго, и компилятор выдаст ошибку.

В случае mapзайм, который является аргументом к закрытию |job|, является неизменным займом на maybe_job.Доступ к переменной из замыкания также является заимствованием (переменной в стеке), поэтому попытка присвоить *maybe_job требует получения изменяемого заимствования для уже заимствованного значения.

Причина, по которой компилятор допускает одно иа не другой вопрос просто в том, насколько сложным может быть анализ - чтобы автоматически отбросить заем в |job|, компилятор должен был бы обнаружить и убедиться, что maybe_job.as_ref() - это единственный заем в maybe_job - что очень сложно,Что произойдет, если вы сделаете maybe_job.as_ref().my_function().map(...) - и my_function() скопируете неизменную ссылку?

...