Skip to content

Commit 62aed6f

Browse files
committed
include note on variance and example
Fixes #89150
1 parent 61cc3e5 commit 62aed6f

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

library/core/src/any.rs

+79-2
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,84 @@ 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+
/// **Note on Variance**:
615+
/// Suppose `SubType` is a subtype of `SuperType`,
616+
/// that is, `SubType` can be used wherever `SuperType` can be used,
617+
/// and can be freely as-casted to it,
618+
/// and `CoVar`is a generic data type that has a covariant type parameter.
619+
/// Then by covariance, `CoVar<SubType>` is a subtype of `CoVar<SuperType>`,
620+
/// that is, `CoVar<SubType>` can be used wherever `CoVar<SuperType>` can be used,
621+
/// and can be freely as-casted to it.
622+
/// Then if `CoVar<SuperType>` relies on `TypeId::of::<SuperType>` to uphold any invariants,
623+
/// those invariants may be broken like so:
624+
/// ```
625+
/// type SubType = fn(&());
626+
/// type SuperType = fn(&'static ());
627+
/// type CoVar<T> = *const T; // imagine something more complicated
628+
///
629+
/// let sub = CoVar::<SubType>::new();
630+
/// // not created by CoVar::<SuperType>::new
631+
/// let fake_super = sub as CoVar<SuperType>;
632+
/// ```
633+
///
634+
/// A full example
635+
///
636+
/// ```
637+
/// use std::any::TypeId;
638+
/// use std::collections::HashSet;
639+
/// use std::marker::PhantomData;
640+
/// use std::sync::{LazyLock, Mutex};
641+
///
642+
/// use unique::Unique;
643+
///
644+
/// static ID_SET: LazyLock<Mutex<HashSet<TypeId>>> = LazyLock::new(|| Mutex::new(HashSet::new()));
645+
///
646+
/// mod unique {
647+
/// use super::*;
648+
///
649+
/// // Due to its private data member, outside this module,
650+
/// // this struct can only be created using `new`.
651+
/// #[derive(Debug, PartialEq)]
652+
/// pub struct Unique<TypeAsId: 'static>(PhantomData<TypeAsId>);
653+
///
654+
/// impl<TypeAsId: 'static> Unique<TypeAsId> {
655+
/// pub fn new() -> Option<Self> {
656+
/// let mut set = ID_SET.lock().unwrap();
657+
/// set.insert(TypeId::of::<TypeAsId>())
658+
/// .then(|| Self(PhantomData))
659+
/// }
660+
/// }
661+
///
662+
/// impl<TypeAsId: 'static> Drop for Unique<TypeAsId> {
663+
/// fn drop(&mut self) {
664+
/// let mut set = ID_SET.lock().unwrap();
665+
/// (!set.remove(&TypeId::of::<TypeAsId>())).then(|| panic!("duplicity detected"));
666+
/// }
667+
/// }
668+
/// }
669+
///
670+
/// // A FnRef can be used wherever a FnStaticRef can be used,
671+
/// // so FnRef is a subtype of FnStaticRef.
672+
/// // Both are 'static, and thus have a TypeId.
673+
/// type FnRef = fn(&());
674+
/// type FnStaticRef = fn(&'static ());
675+
///
676+
/// fn main() {
677+
/// type TheOneRing = FnStaticRef;
678+
///
679+
/// let the_one_ring = Unique::<TheOneRing>::new().unwrap();
680+
/// assert_eq!(Unique::<TheOneRing>::new(), None);
681+
///
682+
/// type OtherRing = FnRef;
683+
///
684+
/// let other_ring = Unique::<OtherRing>::new().unwrap();
685+
/// let fake_one_ring = other_ring as Unique<TheOneRing>;
686+
/// assert_eq!(fake_one_ring, the_one_ring);
687+
///
688+
/// std::mem::forget(fake_one_ring);
689+
/// }
690+
/// ```
613691
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
614692
#[stable(feature = "rust1", since = "1.0.0")]
615693
pub struct TypeId {
@@ -627,8 +705,7 @@ impl PartialEq for TypeId {
627705
}
628706

629707
impl TypeId {
630-
/// Returns the `TypeId` of the type this generic function has been
631-
/// instantiated with.
708+
/// Given a type (as a generic type argument), returns the `TypeId` of that type.
632709
///
633710
/// # Examples
634711
///

0 commit comments

Comments
 (0)