¿Cómo obtener una referencia de estructura a partir de un rasgo encuadrado?

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

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

Este código devuelve un error:

 non-scalar cast: Box as Box 

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

     use std::any::Any; trait A { fn as_any(&self) -> &Any; } struct B; impl A for B { fn as_any(&self) -> &Any { self } } fn main() { let a: Box = Box::new(B); // The indirection through `as_any` is because using `downcast_ref` // on `Box` *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::() { Some(b) => b, None => panic!("&a isn't a B!") }; } 

    La otra forma es implementar un método para cada “objective” en el rasgo base (en este caso, A ), e implementar los moldes para cada tipo de objective deseado.


    Espera, ¿por qué necesitamos as_any ?

    Incluso si agrega Any como requisito para A , aún no va a funcionar correctamente. El primer problema es que A in Box también implementará Any … lo que significa que cuando llamas a downcast_ref , en realidad lo estarás llamando 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 bajar a &A que ya tenía.

    Pero hay una implementación de Any para el tipo subyacente en alguna parte , ¿verdad? Bueno, sí, pero no puedes lograrlo. Rust no te permite “lanzar de forma cruzada” de &A a &Any .

    Eso es lo que as_any es para; porque es algo implementado solo en nuestros tipos “concretos”, el comstackdor no se confunde con respecto a cuál se supone que debe invocar. Llamarlo a an &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 &Any utilizando la implementación de Any para B , que es lo que queremos.

    Tenga en cuenta que puede dejar de lado todo este problema simplemente sin utilizar A en absoluto . Específicamente, lo siguiente también funcionará:

     fn main() { let a: Box = Box::new(B); let _: &B = match a.downcast_ref::() { 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 interés potencial, la caja de mopa le permite combinar la funcionalidad de Any con un rasgo propio.

    Debe quedar claro que el elenco puede fallar si hay otro tipo C implementa A e intenta convertir el Box en un Box . No conozco su situación, pero a mí me parece que está trayendo técnicas de otros idiomas, como Java, a Rust. Nunca me he encontrado con este tipo de problema en Rust. Tal vez el diseño de tu código podría mejorarse para evitar este tipo de yeso.

    Si quieres, puedes “lanzar” prácticamente cualquier cosa con mem::transmute . Tristemente, tendremos un problema si solo queremos Box a Box o &A a &B porque un puntero a un trait es un puntero que en realidad consiste en dos punteros: uno para el objeto real, uno para el vptr. Si lo estamos lanzando a un tipo de struct , podemos simplemente ignorar el vptr. Recuerde que esta solución es altamente insegura y bastante hacky; no la usaría en un código “real”.

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

    EDITAR: Mierda, es aún más inseguro de lo que pensaba. Si quieres hacerlo correctamente de esta manera, deberías usar std::raw::TraitObject . Esto sigue siendo inestable sin embargo. 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