diff --git a/library/core/src/any.rs b/library/core/src/any.rs
index 17d9455592787..f90de1f5ced40 100644
--- a/library/core/src/any.rs
+++ b/library/core/src/any.rs
@@ -610,6 +610,101 @@ impl dyn Any + Send + Sync {
 /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth
 /// noting that the hashes and ordering will vary between Rust releases. Beware
 /// of relying on them inside of your code!
+///
+/// # Danger of Improper Variance
+///
+/// You might think that subtyping is impossible between two static types,
+/// but this is false; there exists a static type with a static subtype.
+/// To wit, `fn(&str)`, which is short for `for<'any> fn(&'any str)`, and
+/// `fn(&'static str)`, are two distinct, static types, and yet,
+/// `fn(&str)` is a subtype of `fn(&'static str)`, since any value of type
+/// `fn(&str)` can be used where a value of type `fn(&'static str)` is needed.
+///
+/// This means that abstractions around `TypeId`, despite its
+/// `'static` bound on arguments, still need to worry about unnecessary
+/// and improper variance: it is advisable to strive for invariance
+/// first. The usability impact will be negligible, while the reduction
+/// in the risk of unsoundness will be most welcome.
+///
+/// ## Examples
+///
+/// Suppose `SubType` is a subtype of `SuperType`, that is,
+/// a value of type `SubType` can be used wherever
+/// a value of type `SuperType` is expected.
+/// Suppose also that `CoVar<T>` is a generic type, which is covariant over `T`
+/// (like many other types, including `PhantomData<T>` and `Vec<T>`).
+///
+/// Then, by covariance, `CoVar<SubType>` is a subtype of `CoVar<SuperType>`,
+/// that is, a value of type `CoVar<SubType>` can be used wherever
+/// a value of type `CoVar<SuperType>` is expected.
+///
+/// Then if `CoVar<SuperType>` relies on `TypeId::of::<SuperType>()` to uphold any invariants,
+/// those invariants may be broken because a value of type `CoVar<SuperType>` can be created
+/// without going through any of its methods, like so:
+/// ```
+/// type SubType = fn(&());
+/// type SuperType = fn(&'static ());
+/// type CoVar<T> = Vec<T>; // imagine something more complicated
+///
+/// let sub: CoVar<SubType> = CoVar::new();
+/// // we have a `CoVar<SuperType>` instance without
+/// // *ever* having called `CoVar::<SuperType>::new()`!
+/// let fake_super: CoVar<SuperType> = sub;
+/// ```
+///
+/// The following is an example program that tries to use `TypeId::of` to
+/// implement a generic type `Unique<T>` that guarantees unique instances for each `Unique<T>`,
+/// that is, and for each type `T` there can be at most one value of type `Unique<T>` at any time.
+///
+/// ```
+/// mod unique {
+///     use std::any::TypeId;
+///     use std::collections::BTreeSet;
+///     use std::marker::PhantomData;
+///     use std::sync::Mutex;
+///
+///     static ID_SET: Mutex<BTreeSet<TypeId>> = Mutex::new(BTreeSet::new());
+///
+///     // TypeId has only covariant uses, which makes Unique covariant over TypeAsId 🚨
+///     #[derive(Debug, PartialEq)]
+///     pub struct Unique<TypeAsId: 'static>(
+///         // private field prevents creation without `new` outside this module
+///         PhantomData<TypeAsId>,
+///     );
+///
+///     impl<TypeAsId: 'static> Unique<TypeAsId> {
+///         pub fn new() -> Option<Self> {
+///             let mut set = ID_SET.lock().unwrap();
+///             (set.insert(TypeId::of::<TypeAsId>())).then(|| Self(PhantomData))
+///         }
+///     }
+///
+///     impl<TypeAsId: 'static> Drop for Unique<TypeAsId> {
+///         fn drop(&mut self) {
+///             let mut set = ID_SET.lock().unwrap();
+///             (!set.remove(&TypeId::of::<TypeAsId>())).then(|| panic!("duplicity detected"));
+///         }
+///     }
+/// }
+///
+/// use unique::Unique;
+///
+/// // `OtherRing` is a subtype of `TheOneRing`. Both are 'static, and thus have a TypeId.
+/// type TheOneRing = fn(&'static ());
+/// type OtherRing = fn(&());
+///
+/// fn main() {
+///     let the_one_ring: Unique<TheOneRing> = Unique::new().unwrap();
+///     assert_eq!(Unique::<TheOneRing>::new(), None);
+///
+///     let other_ring: Unique<OtherRing> = Unique::new().unwrap();
+///     // Use that `Unique<OtherRing>` is a subtype of `Unique<TheOneRing>` 🚨
+///     let fake_one_ring: Unique<TheOneRing> = other_ring;
+///     assert_eq!(fake_one_ring, the_one_ring);
+///
+///     std::mem::forget(fake_one_ring);
+/// }
+/// ```
 #[derive(Clone, Copy, Eq, PartialOrd, Ord)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct TypeId {
@@ -627,8 +722,7 @@ impl PartialEq for TypeId {
 }
 
 impl TypeId {
-    /// Returns the `TypeId` of the type this generic function has been
-    /// instantiated with.
+    /// Returns the `TypeId` of the generic type parameter.
     ///
     /// # Examples
     ///