2017-01-16 33 views
11

mam this minimal example code:Jak działa wypożyczanie treści 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) { } 

ale nie jest on z tego błędu:

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 

Rodzaju ludzie w #rustbeginners okazało się, że muszę dereference polu, aby uzyskać zawartość, a następnie pożyczyć zawartość. 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) { } 

To działa, ale ja tego nie rozumiem.

Dlaczego muszę najpierw usunąć dereferencję? Co mówi błąd? Zwykle nie jest błędem, że wartość jest zrzucana na końcu funkcji.

Najwyraźniej to nie tylko ja nie rozumiem, jak to działa; issue has been filed.

+3

Pamiętaj, że nie musisz nawet wywoływać "szyfrowania", aby uzyskać ten błąd; próba utworzenia samodzielnego odwołania do zmiennej za pomocą 'borrow_mut' może się nie powieść. – ljedrz

+1

A [wersja kodu komentarza ljedrza] (http://play.integer32.com/?gist=c36fba752d8b9500d3b1356c4f33dcd1&version=stable). – Shepmaster

+2

Jest dyskusja na https://users.rust-lang.org/t/trait-objects-and-borrowmut/8520 –

Odpowiedz

4

Zacznijmy od zmiany kodu, który pozwala pracować:

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

Ważną różnicą jest dodanie + 'static do obiektu cecha - parens są po prostu potrzebne do pierwszeństwa.

Ważną rzeczą jest, aby uznać, że istnieje are two lifetimes present in &Foo:

  • całe życie na odniesienie się do: &'a Foo
  • całe życie, który reprezentuje wszystkie wartości odniesienia wewnątrz betonowego że cecha Abstracts: &(Foo + 'b).

Jeśli poprawnie czytam dokument RFC, został on wprowadzony przez RFC 192, a RFC 599 określił rozsądne wartości domyślne dla wszystkich okresów życia. W tym przypadku, wcieleń powinny poszerzyć jak:

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

na drugim końcu rury, mamy Box<Foo>. Rozszerzony regułami RFC, staje się Box<Foo + 'static>. Gdy weźmiemy go pożyczyć, i starają się przekazać go do funkcji, mamy równanie do rozwiązania:

  • Żywotność wewnątrz obiektu cecha jest 'static.
  • Funkcja pobiera odniesienie do obiektu cechy.
  • Żywotność odniesienia jest równa długości życia referencji wewnątrz obiektu cechy.
  • Dlatego odniesienie do obiektu cechy musi być 'static. O o!

Na końcu bloku zostanie usunięty Box, więc z pewnością nie jest statyczny.

Rozwiązaniem wyraźnych wcieleń pozwala żywotność odniesienia do wówczas Obiekt cecha różnią się od trwania odniesienia wewnątrz obiektu cechy.

Jeśli konieczna do utrzymania obiektu cechę z odniesieniami wewnętrznych, alternatywny jest zrobić coś takiego:

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

Prawdziwego kredytu dla tego wyjaśnienia goes to nikomatsakis and his comment on GitHub, właśnie rozszerzył go trochę.