Как работает заимствование Box <Trait>?

У меня этот минимальный пример кода:

use std::borrow::BorrowMut;

trait Foo {}
struct Bar;
impl Foo for Bar {}

fn main() {
    let mut encryptor: Box<Foo> = Box::new(Bar);

    encrypt(encryptor.borrow_mut());
}

fn encrypt(encryptor: &mut Foo) { }

но с этой ошибкой не получается:

error: `encryptor` does not live long enough
  --> src/main.rs:11:1
   |
10 |     encrypt(encryptor.borrow_mut());
   |             --------- borrow occurs here
11 | }
   | ^ `encryptor` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

Добрые люди в #rustbeginners обнаружили, что мне нужно разыменовать поле, чтобы получить содержимое, а затем заимствовать содержимое. Как это:

trait Foo {}
struct Bar;
impl Foo for Bar {}

fn main() {
    let mut encryptor: Box<Foo> = Box::new(Bar);

    encrypt(&mut *encryptor);
}

fn encrypt(encryptor: &mut Foo) { }

Это работает, но я этого не понимаю.

Почему мне нужно сначала разыменовать? Какая ошибка пытается сказать? Обычно это не ошибка, когда значение отбрасывается в конце функции.


По-видимому, это не только я, кто не понимает, как это работает; была отправлена ​​.

Ответ 1

Начните с изменения, которое позволяет работать с кодом:

fn encrypt(encryptor: &mut (Foo + 'static)) { }

Важным отличием является добавление + 'static к объекту-признаку - для приоритета просто необходимы параны.

Важно отметить, что - это две жизни, присутствующие в &Foo:

  • время жизни для самой ссылки: &'a Foo
  • время жизни, которое представляет все ссылки внутри конкретного значения, которые абстрактные рефераты: &(Foo + 'b).

Если я правильно читаю RFC, это было введено RFC 192 и RFC 599 указаны разумные значения по умолчанию для сроков жизни. В этом случае времена жизни должны расширяться так:

fn encrypt(encryptor: &mut Foo) { }
fn encrypt<'a>(encryptor: &'a mut (Foo + 'a)) { }

На другом конце трубы мы имеем Box<Foo>. Расширяемый правилами RFC, это становится Box<Foo + 'static>. Когда мы берем его заимствование и пытаемся передать его функции, у нас есть уравнение для решения:

  • Время жизни внутри объекта-объекта 'static.
  • Функция принимает ссылку на объект-объект.
  • Время жизни ссылки равно времени жизни ссылок внутри объекта-объекта.
  • Следовательно, ссылка на объект-объект должна быть 'static. О, о!

Box будет опущен в конце блока, поэтому он, безусловно, не является статическим.

Исправление с явными временами жизни позволяет времени жизни ссылки на объект-образец отличаться от времени жизни ссылок внутри объекта-объекта.

Если вам нужно поддерживать объект-объект с внутренними ссылками, альтернативный вариант - сделать что-то вроде:

fn encrypt<'a>(encryptor: &mut (Foo + 'a)) { }

Истинный кредит для этого объяснения идет в nikomatsakis и его комментарий к GitHub, я немного расширил его.