Skip to content

Commit 97daa02

Browse files
hkBstdanielhenrymantilla
authored and
gitbot
committed
include note on variance and example
Fixes rust-lang#89150 Co-authored-by: Daniel Henry-Mantilla <[email protected]>
1 parent 4a0d0fc commit 97daa02

File tree

1 file changed

+96
-2
lines changed

1 file changed

+96
-2
lines changed

core/src/any.rs

+96-2
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,101 @@ 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+
/// mod unique {
661+
/// use std::any::TypeId;
662+
/// use std::collections::BTreeSet;
663+
/// use std::marker::PhantomData;
664+
/// use std::sync::Mutex;
665+
///
666+
/// static ID_SET: Mutex<BTreeSet<TypeId>> = Mutex::new(BTreeSet::new());
667+
///
668+
/// // TypeId has only covariant uses, which makes Unique covariant over TypeAsId 🚨
669+
/// #[derive(Debug, PartialEq)]
670+
/// pub struct Unique<TypeAsId: 'static>(
671+
/// // private field prevents creation without `new` outside this module
672+
/// PhantomData<TypeAsId>,
673+
/// );
674+
///
675+
/// impl<TypeAsId: 'static> Unique<TypeAsId> {
676+
/// pub fn new() -> Option<Self> {
677+
/// let mut set = ID_SET.lock().unwrap();
678+
/// (set.insert(TypeId::of::<TypeAsId>())).then(|| Self(PhantomData))
679+
/// }
680+
/// }
681+
///
682+
/// impl<TypeAsId: 'static> Drop for Unique<TypeAsId> {
683+
/// fn drop(&mut self) {
684+
/// let mut set = ID_SET.lock().unwrap();
685+
/// (!set.remove(&TypeId::of::<TypeAsId>())).then(|| panic!("duplicity detected"));
686+
/// }
687+
/// }
688+
/// }
689+
///
690+
/// use unique::Unique;
691+
///
692+
/// // `OtherRing` is a subtype of `TheOneRing`. Both are 'static, and thus have a TypeId.
693+
/// type TheOneRing = fn(&'static ());
694+
/// type OtherRing = fn(&());
695+
///
696+
/// fn main() {
697+
/// let the_one_ring: Unique<TheOneRing> = Unique::new().unwrap();
698+
/// assert_eq!(Unique::<TheOneRing>::new(), None);
699+
///
700+
/// let other_ring: Unique<OtherRing> = Unique::new().unwrap();
701+
/// // Use that `Unique<OtherRing>` is a subtype of `Unique<TheOneRing>` 🚨
702+
/// let fake_one_ring: Unique<TheOneRing> = other_ring;
703+
/// assert_eq!(fake_one_ring, the_one_ring);
704+
///
705+
/// std::mem::forget(fake_one_ring);
706+
/// }
707+
/// ```
613708
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
614709
#[stable(feature = "rust1", since = "1.0.0")]
615710
pub struct TypeId {
@@ -627,8 +722,7 @@ impl PartialEq for TypeId {
627722
}
628723

629724
impl TypeId {
630-
/// Returns the `TypeId` of the type this generic function has been
631-
/// instantiated with.
725+
/// Returns the `TypeId` of the generic type parameter.
632726
///
633727
/// # Examples
634728
///

0 commit comments

Comments
 (0)