Calling the Any trait's type_id() on a mutable reference causes a weird compiler error

The first question is the same as being asked e.g. in this thread opened 1 day ago. Any requires 'static because Any allows conversions based on the obtained TypeId’s, and lifetime information (is “erased” at compiletime and thus) not part of the TypeIds, so a carelessly designed Any API without the 'static requirements might, unsoundly, allow users to convert e.g. StructWithRefs<'a> into StructWithRefs<'b> of some different (longer) lifetime.

The second question is answered by looking at how method call resolution works. The “algorithm” described in the linked reference page shows that in ex2, the first receiver type being considered is the type of s itself, i.e. &'a StructWithoutRefs, and that directly matches the &self receiver type of the type_id method for the StructWithoutRefs: Any implementation.

Now in contrast whilst one might expect s.type_id() in ex3 to be able to (implicitly) convert the &'a mut StructWithoutRefs to &StructWithoutRefs, too, in order to match the &self type, the actual order of types being considered is

  • &'a mut StructWithoutRefs
  • & &'a mut StructWithoutRefs
  • &mut &'a mut StructWithoutRefs
  • StructWithoutRefs
  • &StructWithoutRefs
  • &mut StructWithoutRefs

You’ll see that &'a StructWithoutRefs does appear, but only fairly late. This is commonly not a problem, so you can for many other traits or inherent methods call a &self method directly on a &mut Self reference without any problems. But in this case, the & &'a mut StructWithoutRefs that comes earlier prevents that, and the compiler decides to go with this receiver type as it matches &self for the instance &'a mut StructWithoutRefs: Any. Yes, even a type like &'a mut StructWithoutRefs implements Any, just with a where clause that (effectively) restricts the lifetime to 'a: 'static. The compiler doesn’t (and frankly cannot) use such a lifetime bound being failed to be met in order to reject this “candidate” in method resolution, it merely says: yep, the type & &'a mut StructWithoutRefs matches the &self type for the generic impl<T: 'static> Any for T implementation, and after this “successful” method resulution, the method call gives rise to the 'a: 'static bound. Subsequently / down the line the borrow checker will of course catch this bound as problematic in this function and reports the “borrowed data escapes outside of functionargument requires that `'a` must outlive `'static` ” error. Well, unless you add the 'a: 'static bound of course, which makes for a weird function signature (with a generic lifetime being effectively restricted to be equal to 'static), but successfully compiling nonetheless.

With the above observation of how method resolution is relevant in mind, you can actually “fix” ex3 (so that it gets the type id of the StructWithoutRefs type successfully) by manually creating the &StructWithoutRefs as in (&*s).type_id(), or even just by inserting a dereference step and letting the compiler doing the auto-ref (going from *s to &*s, i.e. from StructWithoutRefs to &StructWithoutRefs) by writing (*s).type_id(); crucially, this makes sure that the & &'a mut StructWithoutRefs candidate type never comes up in method resolution, and the first matching type is &StructWithoutRefs.

2 Likes