todos orden los listado lista etiquetas estructura elementos documento cuerpo rust traits

rust - orden - ¿Cómo obtener una referencia a un tipo concreto de un objeto de rasgo?



lista de etiquetas html (2)

Debe quedar claro que la conversión puede fallar si hay otro tipo C implementa A e intenta convertir la Box<C> en una Box<B> . No sé su situación, pero para mí parece que está trayendo técnicas de otros lenguajes, como Java, a Rust. Nunca me he encontrado con este tipo de problema en Rust, tal vez el diseño de su código podría mejorarse para evitar este tipo de lanzamiento.

Si lo desea, puede "emitir" prácticamente cualquier cosa con mem::transmute . Lamentablemente, tendremos un problema si solo queremos lanzar el Box<A> al Box<B> o &A a &B porque un puntero a un trait es un indicador gordo que en realidad consiste en dos punteros: uno al objeto real, uno a la vptr. Si lo estamos convirtiendo en un tipo de struct , podemos ignorar el vptr. Por favor recuerde que esta solución es altamente insegura y bastante obscena, no la usaría en código "real".

let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };

EDITAR: Al diablo con eso, es aún más inseguro de lo que pensaba. Si desea hacerlo correctamente de esta manera, tendría que usar std::raw::TraitObject . Sin embargo, esto todavía es inestable. No creo que esto sea útil para OP; no lo uses!

Hay mejores alternativas en esta pregunta muy similar: cómo hacer coincidir los implementadores de rasgos

¿Cómo obtengo Box<B> o &B o &Box<B> de la variable en este código:

trait A {} struct B; impl A for B {} fn main() { let mut a: Box<dyn A> = Box::new(B); let b = a as Box<B>; }

Este código devuelve un error:

error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>` --> src/main.rs:8:13 | 8 | let b = a as Box<B>; | ^^^^^^^^^^^ | = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait


Hay dos formas de hacer downcasting en Rust. El primero es usar Any . Tenga en cuenta que esto solo le permite bajar al tipo de concreto original exacto. Al igual que:

use std::any::Any; trait A { fn as_any(&self) -> &dyn Any; } struct B; impl A for B { fn as_any(&self) -> &dyn Any { self } } fn main() { let a: Box<dyn A> = Box::new(B); // The indirection through `as_any` is because using `downcast_ref` // on `Box<A>` *directly* only lets us downcast back to `&A` again. // The method ensures we get an `Any` vtable that lets us downcast // back to the original, concrete type. let b: &B = match a.as_any().downcast_ref::<B>() { Some(b) => b, None => panic!("&a isn''t a B!"), }; }

La otra forma es implementar un método para cada "objetivo" en el rasgo base (en este caso, A ) e implementar los lanzamientos para cada tipo de objetivo deseado.

Espera, ¿por qué necesitamos as_any ?

Incluso si agrega Any como requisito para A , todavía no funcionará correctamente. El primer problema es que la A en la Box<dyn A> también implementará Any ... lo que significa que cuando llame a downcast_ref , en realidad lo llamará en el tipo de objeto A Any solo puede bajar al tipo en el que se invocó, que en este caso es A , por lo que solo podrá volver a lanzar a &dyn A que ya tenía.

Pero hay una implementación de Any para el tipo subyacente en algún lugar , ¿verdad? Bueno, sí, pero no puedes llegar a eso. Rust no te permite "realizar una conversión cruzada" de &dyn A a &dyn Any .

Para eso es para lo que sea; como es algo que solo se implementa en nuestros tipos "concretos", el compilador no se confunde con respecto a cuál se supone que debe invocar. Invocarlo en un &dyn A hace que se B::as_any dinámicamente a la implementación concreta (de nuevo, en este caso, B::as_any ), que devuelve un &dyn Any utilizando la implementación de Any for B , que es lo que queremos.

Tenga en cuenta que puede evadir todo este problema simplemente no utilizando A en absoluto . Específicamente, lo siguiente también funcionará:

fn main() { let a: Box<dyn Any> = Box::new(B); let _: &B = match a.downcast_ref::<B>() { Some(b) => b, None => panic!("&a isn''t a B!") }; }

Sin embargo, esto le impide tener otros métodos; todo lo que puedes hacer aquí es abatido a un tipo concreto.

Como nota final de potencial interés, la caja de mopa permite combinar la funcionalidad de Any con un rasgo propio.