Понимание проблемы на всю жизнь - PullRequest
3 голосов
/ 04 апреля 2020

Я получаю пожизненную ошибку при компиляции изменения, которое я сделал для Firecracker (на aarch64, но я сомневаюсь, что проблема зависит от архитектуры):

error[E0716]: temporary value dropped while borrowed
   --> src/vmm/src/device_manager/mmio.rs:174:24
    |
174 |           let int_evt = &serial
    |  ________________________^
175 | |             .lock()
176 | |             .expect("Poisoned legacy serial lock")
    | |__________________________________________________^ creates a temporary which is freed while still in use
177 |               .interrupt_evt();
    |                               - temporary value is freed at the end of this statement
178 |           vm.register_irqfd(int_evt, self.irq)
    |                             ------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value

Исходный код (который прекрасно компилируется):

vm.register_irqfd(&serial
        .lock()
        .expect("Poisoned legacy serial lock")
        .interrupt_evt(), self.irq)
    .map_err(Error::RegisterIrqFd)?;

Я не понимаю разницы. Похоже, в сообщении об ошибке указано, что expect() возвращает временный объект и что я использую константную ссылку на него, в C ++ это продлит срок действия временного файла, не так ли в Rust? В любом случае, почему он работает в исходном коде, но не после того, как я связался с l-значением (на языке C ++, я не уверен, что оно одинаково для Rust)?

Я попытался создать SSCE здесь, но это сработало, как и ожидалось!

1 Ответ

3 голосов
/ 04 апреля 2020

Простой, воспроизводимый пример проблемы ( детская площадка ):

// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);

// get reference to item inside array
let item: &i32 = mutex.lock().unwrap().get(0).unwrap();

// use reference for something
println!("item = {:?}", item);

mutex.lock().unwrap() возвращает MutexGuard<'_, Option<i32>>, который заимствует данные внутри мьютекса. Он также владеет блокировкой данных, которая освобождается при снятии защиты, что означает, что никто не может заимствовать данные одновременно.

Когда вы вызываете метод внутреннего типа для этого предохранителя (например, .get в приведенном выше примере или .interrupt_evt в вашем коде), он будет заимствован из времени жизни охранника, поскольку вы можете безопасно получать доступ к данным только при наличии защиты. Но защита не хранится ни в одной переменной, поэтому она существует только временно для этого оператора и сразу же удаляется в конце. Поэтому вы не можете получить ссылку на данные вне оператора.

Решить эту проблему очень просто: сначала сохраните охрану в переменной, а затем позаимствуйте у нее. Это гарантирует, что охранник живет дольше, чем рекомендации, которые вы получаете от него ( детская площадка ):

// create array inside mutex
let mutex = Mutex::new([ 0i32 ]);

// get reference to item inside array
let guard = mutex.lock().unwrap();
let item: &i32 = guard.get(0).unwrap();

// use reference for something
println!("item = {:?}", item);

// guard is now destroyed at end of scope
// and mutex lock is released here
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...