Skip to main content

rx_rust/utils/
types.rs

1use std::marker::PhantomData;
2
3/// Placeholder for a type parameter that a struct carries but never stores.
4///
5/// `PhantomData<fn(T) -> T>` is used instead of `PhantomData<T>` so the marker is `MaybeSend` /
6/// `MaybeSync` even when `T` is not, and stays invariant in `T`.
7/// For more detail: <https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns>
8///
9/// The marker still ties the struct's lifetime to `T`: `MarkerType<&'a U>` is not `'static`.
10/// There is no way to avoid that. `PhantomData<X>: 'r` holds only if `X: 'r`, and the check is
11/// structural over `X`, so every spelling that mentions `T` inherits `T`'s lifetime. The trait
12/// object trick suggested in
13/// <https://users.rust-lang.org/t/getting-phantomdata-to-have-a-static-lifetime/38505> does not
14/// work either: `PhantomData<Box<dyn Fn(T) -> T + 'static>>` and
15/// `PhantomData<*const (dyn Fn(T) -> T + 'static)>` are both rejected — a `dyn Trait + 'static`
16/// object bound does not erase the lifetimes of the trait's own arguments.
17///
18/// The only real escape is to not name the type parameter on the struct at all. Operators carry it
19/// because it would otherwise be an unconstrained impl parameter (E0207): in
20/// `impl<'or, T0, T, E, OE, F> Observable<'or, T, E> for Map<OE, F> where OE: Observable<'or, T0, E>`
21/// the source item type `T0` appears only in a where-clause trait bound, which does not constrain
22/// it. Making `Observable` carry associated `Item` / `Err` types instead of generic parameters
23/// would turn `T0` into the projection `OE::Item` and remove the marker from most adapters, the way
24/// `std::iter::Map<I, F>` needs no marker. That is a crate-wide breaking change, it does not help
25/// the operators whose parameter is chosen by the caller (`with_item_type::<T>`,
26/// `with_error_type::<E>`, `collect::<C>`), and it makes things worse for `Create`, whose `T` / `E`
27/// sit in the *input* type of its builder closure, which never constrains anything.
28///
29/// In practice the leak costs little: nearly every `T: 'static` bound in this crate comes from a
30/// scheduler owning a value across a task, not from this marker, and where a marker does add a
31/// lifetime the source observable already carries the same lifetime anyway.
32pub type MarkerType<T> = PhantomData<fn(T) -> T>;
33
34cfg_if::cfg_if! {
35    if #[cfg(feature = "single-threaded")] {
36        pub type Shared<T> = std::rc::Rc<T>;
37        pub type WeakShared<T> = std::rc::Weak<T>;
38
39        pub trait MaybeSend {}
40        impl<T> MaybeSend for T {}
41        pub trait MaybeSync {}
42        impl<T> MaybeSync for T {}
43    } else {
44        pub type Shared<T> = std::sync::Arc<T>;
45        pub type WeakShared<T> = std::sync::Weak<T>;
46
47        pub trait MaybeSend: Send {}
48        impl<T> MaybeSend for T where T: Send {}
49        pub trait MaybeSync: Sync {}
50        impl<T> MaybeSync for T where T: Sync {}
51    }
52}