Skip to content

Commit aeaeb46

Browse files
include note on variance and example
Fixes rust-lang#89150 Co-authored-by: Daniel Henry-Mantilla <[email protected]>
1 parent 61cc3e5 commit aeaeb46

File tree

1 file changed

+105
-2
lines changed

1 file changed

+105
-2
lines changed

library/core/src/any.rs

+105-2
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,110 @@ impl dyn Any + Send + Sync {
610610
/// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth
611611
/// noting that the hashes and ordering will vary between Rust releases. Beware
612612
/// of relying on them inside of your code!
613+
///
614+
/// # Danger of Improper Variance
615+
///
616+
/// You might think that subtyping is impossible between two static types,
617+
/// but this is false; there exists a static type with a static subtype.
618+
/// To wit, `fn(&str)`, which is short for `for<'any> fn(&'any str)`, and
619+
/// `fn(&'static str)`, are two distinct, static types, and yet,
620+
/// `fn(&str)` is a subtype of `fn(&'static str)`, since any value of type
621+
/// `fn(&str)` can be used where a value of type `fn(&'static str)` is needed.
622+
///
623+
/// This means that abstractions around `TypeId`, despite its
624+
/// `'static` bound on arguments, still need to worry about unnecessary
625+
/// and improper variance: it is advisable to strive for invariance
626+
/// first. The usability impact will be negligible, while the reduction
627+
/// in the risk of unsoundness will be most welcome.
628+
///
629+
/// ## Examples
630+
///
631+
/// Suppose `SubType` is a subtype of `SuperType`, that is,
632+
/// a value of type `SubType` can be used wherever
633+
/// a value of type `SuperType` is expected.
634+
/// Suppose also that `CoVar<T>` is a generic type, which is covariant over `T`
635+
/// (like many other types, including `PhantomData<T>` and `Vec<T>`).
636+
637+
/// Then, by covariance, `CoVar<SubType>` is a subtype of `CoVar<SuperType>`,
638+
/// that is, a value of type `CoVar<SubType>` can be used wherever
639+
/// a value of type `CoVar<SuperType>` is expected.
640+
///
641+
/// Then if `CoVar<SuperType>` relies on `TypeId::of::<SuperType>()` to uphold any invariants,
642+
/// those invariants may be broken because a value of type `CoVar<SuperType>` can be created
643+
/// without going through any of its methods, like so:
644+
/// ```
645+
/// type SubType = fn(&());
646+
/// type SuperType = fn(&'static ());
647+
/// type CoVar<T> = Vec<T>; // imagine something more complicated
648+
///
649+
/// let sub: CoVar<SubType> = CoVar::new();
650+
/// // we have a `CoVar<SuperType>` instance without
651+
/// // *ever* having called `CoVar::<SuperType>::new()`!
652+
/// let fake_super: CoVar<SuperType> = sub;
653+
/// ```
654+
///
655+
/// The following is an example program that tries to use `TypeId::of` to
656+
/// implement a generic type `Unique<T>` that guarantees unique instances for each `Unique<T>`,
657+
/// that is, and for each type `T` there can be at most one value of type `Unique<T>` at any time.
658+
///
659+
/// ```
660+
/// use std::any::TypeId;
661+
/// use std::collections::BTreeSet;
662+
/// use std::marker::PhantomData;
663+
/// use std::sync::Mutex;
664+
///
665+
/// use unique::Unique;
666+
///
667+
/// mod unique {
668+
/// use super::*;
669+
///
670+
/// static ID_SET: Mutex<BTreeSet<TypeId>> = Mutex::new(BTreeSet::new());
671+
///
672+
/// // Due to its private data member, outside this module,
673+
/// // this struct can only be created using `new`.
674+
/// #[derive(Debug, PartialEq)]
675+
/// pub struct Unique<TypeAsId: 'static>(
676+
/// // this usage of `TypeAsId` makes `Unique` covariant 🚨
677+
/// PhantomData<TypeAsId>,
678+
/// );
679+
///
680+
/// impl<TypeAsId: 'static> Unique<TypeAsId> {
681+
/// pub fn new() -> Option<Self> {
682+
/// let mut set = ID_SET.lock().unwrap();
683+
/// set.insert(TypeId::of::<TypeAsId>())
684+
/// .then(|| Self(PhantomData))
685+
/// }
686+
/// }
687+
///
688+
/// impl<TypeAsId: 'static> Drop for Unique<TypeAsId> {
689+
/// fn drop(&mut self) {
690+
/// let mut set = ID_SET.lock().unwrap();
691+
/// (!set.remove(&TypeId::of::<TypeAsId>())).then(|| panic!("duplicity detected"));
692+
/// }
693+
/// }
694+
/// }
695+
///
696+
/// // `FnRef` is a subtype of `FnStaticRef`.
697+
/// // Both are 'static, and thus have a TypeId.
698+
/// type FnRef = fn(&());
699+
/// type FnStaticRef = fn(&'static ());
700+
///
701+
/// fn main() {
702+
/// type TheOneRing = FnStaticRef;
703+
///
704+
/// let the_one_ring: Unique<TheOneRing> = Unique::new().unwrap();
705+
/// assert_eq!(Unique::<TheOneRing>::new(), None);
706+
///
707+
/// type OtherRing = FnRef;
708+
///
709+
/// let other_ring: Unique<OtherRing> = Unique::new().unwrap();
710+
/// // Use that `Unique<OtherRing>` is a subtype of `Unique<TheOneRing>` 🚨
711+
/// let fake_one_ring: Unique<TheOneRing> = other_ring;
712+
/// assert_eq!(fake_one_ring, the_one_ring);
713+
///
714+
/// std::mem::forget(fake_one_ring);
715+
/// }
716+
/// ```
613717
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
614718
#[stable(feature = "rust1", since = "1.0.0")]
615719
pub struct TypeId {
@@ -627,8 +731,7 @@ impl PartialEq for TypeId {
627731
}
628732

629733
impl TypeId {
630-
/// Returns the `TypeId` of the type this generic function has been
631-
/// instantiated with.
734+
/// Returns the `TypeId` of the generic type parameter.
632735
///
633736
/// # Examples
634737
///

0 commit comments

Comments
 (0)