rust traits

rust - Referencias a rasgos en estructuras.



traits (2)

A tener en cuenta para futuras referencias: la sintaxis ha cambiado desde

struct Bar<''a> { foo: &''a Foo + ''a, }

a

struct Bar<''a> { foo: &''a (Foo + ''a), // with parens }

Por RFC 438

Tengo un rasgo Foo

pub trait Foo { fn do_something(&self) -> f64; }

y una estructura que hace referencia a ese rasgo.

pub struct Bar { foo: Foo, }

Tratando de compilar lo consigo

error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`

Cambiando la estructura a

struct Bar { foo: &Foo, }

Me dice error: missing lifetime specifier

Cambiando la definición a

struct Bar { foo: Box<Foo>, }

Compila - yay!

Sin embargo, cuando quiero una función para devolver foo en la bar , algo como:

impl Bar { fn get_foo(&self) -> Foo { self.foo } }

Bueno, obviamente, bar.foo es un Box<Foo> , así que, como es de esperar, recibo un error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo` error: reference to trait `Foo` where a type is expected; try `Box<Foo>` or `&Foo`

Cambiando la firma a

impl Bar { fn get_foo(&self) -> Box<Foo> { let this = *self; this.foo } }

Pero ahora me sale un error: cannot move out of dereference of `&`-pointer en intentar desreferirme a mí self .

Cambiando a

impl Bar { fn get_foo(self) -> Box<Foo> { self.foo } }

Todo está bien.

Asi que....

  1. ¿Por qué no funciona la estructura de bar ? Supongo que tengo que encajonar ya que las estructuras tienen un diseño de memoria establecido, así que tenemos que decir que es un puntero a un rasgo (ya que no podemos saber qué tan grande será), pero ¿por qué el compilador sugiere algo que no compila? ?
  2. ¿Por qué no puedo hacer referencia a self en get_foo() - Todos los ejemplos que he visto utilizan la sintaxis de self prestada?
  3. ¿Cuál es la implicación de eliminar el & y solo el uso de self ?

Aprender Rust es fascinante, pero la seguridad de la memoria es fascinante e intimidante.

Código completo que compila:

trait Foo { fn do_something(&self) -> f64; } struct Bar { foo: Box<Foo>, } impl Bar { fn get_foo(self) -> Box<Foo> { let foo = self.foo; foo.do_something(); foo } } fn main() {}


Este es el punto difícil de los objetos de rasgos, debe ser muy explícito acerca de quién es el propietario del objeto subyacente.

De hecho, cuando utiliza un rasgo como un tipo, el objeto subyacente debe almacenarse en algún lugar, ya que los objetos del rasgo son de hecho referencias a un objeto que implementa el rasgo dado. Esta es la razón por la que no puede tener un MyTrait como tipo, debe ser una referencia &MyTrait o una casilla Box<MyTrait> .

Con referencias

El primer método que probó fue con una referencia y el compilador se quejó de que faltaba un especificador de vida útil:

struct Bar { foo : &Foo, }

El problema es que una referencia no es propietaria del objeto subyacente y otro objeto o ámbito debe poseerlo en alguna parte: solo lo está tomando prestado. Y por lo tanto, el compilador necesita información acerca de cuánto tiempo será válida esta referencia: si el objeto subyacente fue destruido, su instancia de Bar tendría una referencia a la memoria liberada, ¡lo cual está prohibido!

La idea aquí es agregar tiempos de vida:

struct Bar<''a> { foo : &''a (Foo + ''a), }

Lo que le está diciendo aquí al compilador es: "El objeto de mi barra no puede sobrevivir a la referencia de Foo dentro de él". Debe especificar el tiempo de vida dos veces: una vez para el tiempo de vida de la referencia, y una vez para el objeto de rasgo en sí mismo, porque los rasgos se pueden implementar para referencias, y si el objeto subyacente es una referencia, también debe especificar su tiempo de vida.

En caso especial estaría escribiendo:

struct Bar<''a> { foo : &''a (Foo + ''static), }

En este caso, el ''static requiere que el objeto subyacente sea una estructura real o una referencia &''static , pero no se permitirán otras referencias.

Además, para construir su objeto, tendrá que darle una referencia a otro objeto que almacene usted mismo.

Terminas con algo como esto:

trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar<''a> { foo: &''a (Foo + ''a), } impl<''a> Bar<''a> { fn new(the_foo: &''a Foo) -> Bar<''a> { Bar { foo: the_foo } } fn get_foo(&''a self) -> &''a Foo { self.foo } } fn main() { let myfoo = MyFoo; let mybar = Bar::new(&myfoo as &Foo); }

Con cajas

Una Caja contrariamente posee su contenido, por lo que le permite otorgar la propiedad del objeto subyacente a su estructura de Bar. Sin embargo, como este objeto subyacente podría ser una referencia, también debe especificar una duración:

struct Bar<''a> { foo: Box<Foo + ''a> }

Si sabe que el objeto subyacente no puede ser una referencia, también puede escribir:

struct Bar { foo: Box<Foo + ''static> }

y el problema de la vida desaparece por completo.

La construcción del objeto es, por lo tanto, similar, pero más simple, ya que no es necesario que usted mismo almacene el objeto subyacente, el cuadro lo maneja:

trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar<''a> { foo: Box<Foo + ''a>, } impl<''a> Bar<''a> { fn new(the_foo: Box<Foo + ''a>) -> Bar<''a> { Bar { foo: the_foo } } fn get_foo(&''a self) -> &''a Foo { &*self.foo } } fn main() { let mybar = Bar::new(box MyFoo as Box<Foo>); }

En este caso, la ''static versión ''static sería:

trait Foo {} struct MyFoo; impl Foo for MyFoo {} struct Bar { foo: Box<Foo + ''static>, } impl Bar { fn new(the_foo: Box<Foo + ''static>) -> Bar { Bar { foo: the_foo } } fn get_foo<''a>(&''a self) -> &''a Foo { &*self.foo } } fn main() { let mybar = Bar::new(box MyFoo as Box<Foo>); let x = mybar.get_foo(); }

Con el valor desnudo

Para contestar su última pregunta:

¿Cuál es la implicación de eliminar el & y solo el uso de self?

Si un método tiene una definición como esta:

fn unwrap(self) {}

Significa que consumirá su objeto en el proceso, y después de llamar a bar.unwrap() , ya no podrá usar la bar .

Es un proceso utilizado generalmente para devolver la propiedad de los datos que posee su estructura. Conocerás muchas funciones de unwrap() en la biblioteca estándar.