vehiculos titulo sobre propiedad prestamos por personales con carros rust traits borrow-checker

rust - titulo - prestamos por carros



¿Cómo funciona el préstamo de los elementos de Box<Trait>? (1)

Tengo este código de ejemplo mínimo :

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

pero falla con este error:

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

La gente amable de #rustbeginners descubrió que tenía que quitar la referencia del cuadro para obtener los contenidos y luego tomar prestado el contenido. De esta manera :

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

Funciona, pero no lo entiendo.

¿Por qué necesito desreferencia primero? ¿Cuál es el error tratando de decir? Normalmente no es un error que un valor se descarta al final de la función.

Aparentemente no soy solo yo quien no entiende cómo funciona esto; un problema ha sido archivado


Comencemos con un cambio que permita que el código funcione:

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

La diferencia importante es la adición de + ''static al objeto de rasgo - los paréntesis solo son necesarios para la precedencia.

Lo importante de reconocer es que hay dos vidas presentes en &Foo :

  • toda una vida para la referencia en sí: &''a Foo
  • una vida que representa todas las referencias dentro del valor concreto que abstrae el rasgo: &(Foo + ''b) .

Si estoy leyendo las RFC correctamente, esto fue introducido por RFC 192 , y RFC 599 especificó valores predeterminados razonables para toda la vida útil. En este caso, las vidas deberían expandirse como:

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

En el otro extremo de la tubería, tenemos un Box<Foo> . Ampliado por las reglas de la RFC, esto se convierte en Box<Foo + ''static> . Cuando tomamos un préstamo de él e intentamos pasarlo a la función, tenemos una ecuación para resolver:

  • La vida útil dentro del objeto de rasgo es ''static .
  • La función toma una referencia a un objeto de rasgo.
  • La duración de la referencia es igual a la duración de las referencias dentro del objeto de rasgo.
  • Por lo tanto, la referencia al objeto de rasgo debe ser ''static . ¡UH oh!

El Box se eliminará al final del bloque, por lo que ciertamente no es estático.

La solución con tiempos de vida explícitos permite que la duración de la referencia al objeto de rasgo difiera de la duración de las referencias dentro del objeto de rasgo.

Si necesita admitir un objeto de rasgo con referencias internas, una alternativa es hacer algo como:

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

El verdadero mérito de esta explicación es para nikomatsakis y su comentario sobre GitHub , simplemente lo amplié un poco.