Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #![feature(core_intrinsics)]
- use std::intrinsics::type_name;
- /////////////////////////////////////////////////////////////////////
- // Suppose these are the real traits from Serde.
- trait Querializer {}
- trait Generic {
- // Not object safe because of this generic method.
- fn generic_fn<Q: Querializer>(&self, querializer: Q);
- }
- impl<'a, T> Querializer for &'a T where T: Querializer + ?Sized {}
- impl<'a, T> Generic for Box<T> where T: Generic + ?Sized {
- fn generic_fn<Q: Querializer>(&self, querializer: Q) {
- let name = unsafe { type_name::<T>() };
- println!("impl-1# T=({})#\t\t call to 'generic_fn()' for 'impl<'a, T> Generic for Box<T> where T: Generic + ?Sized'", name);
- (**self).generic_fn(querializer)
- }
- }
- /////////////////////////////////////////////////////////////////////
- // This is an object-safe equivalent that interoperates seamlessly.
- trait ErasedGeneric {
- fn erased_fn(&self, querializer: &Querializer);
- }
- impl Generic for ErasedGeneric {
- // Depending on the trait method signatures and the upstream
- // impls, could also implement for:
- //
- // - &'a ErasedGeneric
- // - &'a (ErasedGeneric + Send)
- // - &'a (ErasedGeneric + Sync)
- // - &'a (ErasedGeneric + Send + Sync)
- // - Box<ErasedGeneric>
- // - Box<ErasedGeneric + Send>
- // - Box<ErasedGeneric + Sync>
- // - Box<ErasedGeneric + Send + Sync>
- fn generic_fn<Q: Querializer>(&self, querializer: Q) {
- println!("impl-2# T=([hardcoded]ErasedGeneric)#\t call to 'generic_fn()' for 'impl Generic for ErasedGeneric'");
- self.erased_fn(&querializer)
- }
- }
- impl<T> ErasedGeneric for T where T: Generic {
- fn erased_fn(&self, querializer: &Querializer) {
- let name = unsafe { type_name::<T>() };
- println!("impl-3# T=({})#\t\t\t call to 'erased_fn()' for 'impl<T> ErasedGeneric for T where T: Generic'", name);
- self.generic_fn(querializer)
- }
- }
- fn main() {
- struct T;
- impl Querializer for T {}
- struct S;
- impl Generic for S {
- fn generic_fn<Q: Querializer>(&self, _querializer: Q) {
- println!("impl-4# T=([hard-coded]S)#\t\t call to 'generic_fn()' querying the real S");
- }
- }
- // Construct a trait object.
- let trait_object: Box<ErasedGeneric> = Box::new(S{});
- // Seamlessly invoke the generic method on the trait object.
- //
- // THIS LINE LOOKS LIKE MAGIC. We have a value of type trait
- // object and we are invoking a generic method on it.
- println!("---- call-1# trait_object.generic_fn(T); --\n");
- trait_object.generic_fn(T{});
- println!("\n---- call-2# (*trait_object).generic_fn(T); --\n");
- (*trait_object).generic_fn(T{});
- }
- /*
- Este código usa varios trucos:
- Truco 1: cuando implementas Trait Generic para el trait ErasedGeneric
- (impl-2# en los println), da igual que el trait Generic sea un genérico
- y los traits genéricos no puedan ser teóricamente tratados como objetos
- (trait-objects), tienes acceso a self y self se trata como si fuese un
- objeto. Desde ahí se hace una llamada a 'self.erased_fn()'.
- Truco 2: El 'self.erased_fn()' anterior llama a la función erased_fn de
- ErasedGeneric, pero no a un 'erased_fn()' base sino al 'erased_fn()'
- definido específicamente para tu struct, como si fuese una función
- virtual.
- Truco 3: ¿Y quien ha definido un ErasedFn específico para tu struct
- 'S'? Aunque tú en tu código (función main) sólo implementas la clase
- Generic, para cada clase Generic automáticamente se implementa la
- clase ErasedGeneric (impl-3#).
- Con lo anterior, tenemos implementado todo el andamiaje necesario para
- el call-2:
- - tú implementas el Generic para tu struct S (impl-4#)
- - Para cada tipo que implemente el trait Generic (como el tipo S) hay
- un ErasedGeneric específico y distinto (impl-3#, truco 3)
- - Si tienes un ErasedGeneric, puedes llamar a generic_fn porque hay
- una implementa Generic para el trait ErasedGeneric (impl-2#, truco 1)
- En el call-2# se llaman a la inversa: primero al impl-2#, luego eso
- llama al impl-3# y luego eso al impl-4#.
- En el call-1# simplemente se añade una indirección más porque se llama
- al generic_fn que se ha implementado específicamente para
- Box<trait-object-ErasedGeneric> (Box<dyn ErasedGeneric>) usando los
- trucos 1 y 3. Y eso llama al impl-2# y ya estamos en la misma cadena
- de llamadas que en el call-1#.
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement