2017-01-16 7 views
11

У меня есть this minimal example code:Как работает заимствование 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 обнаружили, что у меня есть разыменовать поле, чтобы получить содержимое, а затем заимствовать содержание. Like this:

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) { } 

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

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


По-видимому, не только я не понимаю, как это работает; a issue has been filed.

+3

Обратите внимание, что вам даже не нужно вызывать 'encrypt' для получения этой ошибки; просто попытка создать отдельную изменчивую ссылку через 'loan_mut' потерпит неудачу. – ljedrz

+1

[кодовая версия комментария ljedrz] (http://play.integer32.com/?gist=c36fba752d8b9500d3b1356c4f33dcd1&version=stable). – Shepmaster

+2

Существует некоторая дискуссия на https://users.rust-lang.org/t/trait-objects-and-borrowmut/8520 –

ответ

4

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

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

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

Важно признать, что есть are two lifetimes present in &Foo:

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

Если я правильно читать РЛК, это было введено 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)) { } 

Истинного кредит для этого объяснения goes to nikomatsakis and his comment on GitHub, я просто расширил его немного.