Advertisement
Guest User

Untitled

a guest
May 25th, 2019
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.31 KB | None | 0 0
  1. #![feature(core_intrinsics)]
  2. use std::intrinsics::type_name;
  3.  
  4. /////////////////////////////////////////////////////////////////////
  5. // Suppose these are the real traits from Serde.
  6.  
  7. trait Querializer {}
  8.  
  9. trait Generic {
  10. // Not object safe because of this generic method.
  11. fn generic_fn<Q: Querializer>(&self, querializer: Q);
  12. }
  13.  
  14. impl<'a, T> Querializer for &'a T where T: Querializer + ?Sized {}
  15.  
  16. impl<'a, T> Generic for Box<T> where T: Generic + ?Sized {
  17. fn generic_fn<Q: Querializer>(&self, querializer: Q) {
  18. let name = unsafe { type_name::<T>() };
  19. println!("impl-1# T=({})#\t\t call to 'generic_fn()' for 'impl<'a, T> Generic for Box<T> where T: Generic + ?Sized'", name);
  20. (**self).generic_fn(querializer)
  21. }
  22. }
  23.  
  24. /////////////////////////////////////////////////////////////////////
  25. // This is an object-safe equivalent that interoperates seamlessly.
  26.  
  27. trait ErasedGeneric {
  28. fn erased_fn(&self, querializer: &Querializer);
  29. }
  30.  
  31. impl Generic for ErasedGeneric {
  32. // Depending on the trait method signatures and the upstream
  33. // impls, could also implement for:
  34. //
  35. // - &'a ErasedGeneric
  36. // - &'a (ErasedGeneric + Send)
  37. // - &'a (ErasedGeneric + Sync)
  38. // - &'a (ErasedGeneric + Send + Sync)
  39. // - Box<ErasedGeneric>
  40. // - Box<ErasedGeneric + Send>
  41. // - Box<ErasedGeneric + Sync>
  42. // - Box<ErasedGeneric + Send + Sync>
  43. fn generic_fn<Q: Querializer>(&self, querializer: Q) {
  44. println!("impl-2# T=([hardcoded]ErasedGeneric)#\t call to 'generic_fn()' for 'impl Generic for ErasedGeneric'");
  45. self.erased_fn(&querializer)
  46. }
  47. }
  48.  
  49. impl<T> ErasedGeneric for T where T: Generic {
  50. fn erased_fn(&self, querializer: &Querializer) {
  51. let name = unsafe { type_name::<T>() };
  52. println!("impl-3# T=({})#\t\t\t call to 'erased_fn()' for 'impl<T> ErasedGeneric for T where T: Generic'", name);
  53. self.generic_fn(querializer)
  54. }
  55. }
  56.  
  57. fn main() {
  58. struct T;
  59. impl Querializer for T {}
  60.  
  61. struct S;
  62. impl Generic for S {
  63. fn generic_fn<Q: Querializer>(&self, _querializer: Q) {
  64. println!("impl-4# T=([hard-coded]S)#\t\t call to 'generic_fn()' querying the real S");
  65. }
  66. }
  67.  
  68. // Construct a trait object.
  69. let trait_object: Box<ErasedGeneric> = Box::new(S{});
  70.  
  71. // Seamlessly invoke the generic method on the trait object.
  72. //
  73. // THIS LINE LOOKS LIKE MAGIC. We have a value of type trait
  74. // object and we are invoking a generic method on it.
  75. println!("---- call-1# trait_object.generic_fn(T); --\n");
  76. trait_object.generic_fn(T{});
  77.  
  78. println!("\n---- call-2# (*trait_object).generic_fn(T); --\n");
  79. (*trait_object).generic_fn(T{});
  80. }
  81.  
  82. /*
  83. Este código usa varios trucos:
  84.  
  85. Truco 1: cuando implementas Trait Generic para el trait ErasedGeneric
  86. (impl-2# en los println), da igual que el trait Generic sea un genérico
  87. y los traits genéricos no puedan ser teóricamente tratados como objetos
  88. (trait-objects), tienes acceso a self y self se trata como si fuese un
  89. objeto. Desde ahí se hace una llamada a 'self.erased_fn()'.
  90.  
  91. Truco 2: El 'self.erased_fn()' anterior llama a la función erased_fn de
  92. ErasedGeneric, pero no a un 'erased_fn()' base sino al 'erased_fn()'
  93. definido específicamente para tu struct, como si fuese una función
  94. virtual.
  95.  
  96. Truco 3: ¿Y quien ha definido un ErasedFn específico para tu struct
  97. 'S'? Aunque tú en tu código (función main) sólo implementas la clase
  98. Generic, para cada clase Generic automáticamente se implementa la
  99. clase ErasedGeneric (impl-3#).
  100.  
  101. Con lo anterior, tenemos implementado todo el andamiaje necesario para
  102. el call-2:
  103. - tú implementas el Generic para tu struct S (impl-4#)
  104. - Para cada tipo que implemente el trait Generic (como el tipo S) hay
  105. un ErasedGeneric específico y distinto (impl-3#, truco 3)
  106. - Si tienes un ErasedGeneric, puedes llamar a generic_fn porque hay
  107. una implementa Generic para el trait ErasedGeneric (impl-2#, truco 1)
  108.  
  109. En el call-2# se llaman a la inversa: primero al impl-2#, luego eso
  110. llama al impl-3# y luego eso al impl-4#.
  111.  
  112. En el call-1# simplemente se añade una indirección más porque se llama
  113. al generic_fn que se ha implementado específicamente para
  114. Box<trait-object-ErasedGeneric> (Box<dyn ErasedGeneric>) usando los
  115. trucos 1 y 3. Y eso llama al impl-2# y ya estamos en la misma cadena
  116. de llamadas que en el call-1#.
  117. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement