Unique

Trait Unique 

Source
pub trait Unique:
    Sealed
    + Sized
    + Debug { }
Expand description

A unique type.

The Initial unique type for a scope may be constructed using with.

Further unique types may be obtained via split.

§Invariant

Unique has one invariant: more than one instance of an impl Unique type must not exist within a scope.

The crate is designed in such a way that this may never be violated in safe code.

§Guarantees

As impl Unique types are opaque, they have a few properties that are not possible to expose.

§Types are zero-sized

All impl Unique types are zero-sized.

§Types do nothing on drop

All impl Unique types can be forgotten without worry as they lack an implementation of Drop.

§Safeguards

A number of safeguards are in place to prevent Unique from potential misuse. The following is a list of these including examples of what could go wrong if they were not in place.

§The trait is not object-safe

dyn Unique trait objects are forbidden from existence for a reason. See the following example:

fn assert_type_eq<T>(_: T, _: T) {}

with(|initial| {
    let (a, b) = split(initial);
    let a: Box<dyn Unique> = Box::new(a);
    let b: Box<dyn Unique> = Box::new(b);
    // In this hypothetical world, unique types aren't so unique.
    assert_type_eq(a, b);
});

If Unique were object-safe, trait objects of it could be arbitrarily created. As they would implement Unique while being the same type, the trait’s invariant would be violated.

§The trait is sealed

Unique cannot be implemented for external types because it is sealed. See the following example:

fn assert_type_eq<T>(_: T, _: T) {}

#[derive(Debug)]
struct MyUnique;

impl Unique for MyUnique {}

let a = MyUnique;
let b = MyUnique;
// The invariant is trivially violated once again.
assert_type_eq(a, b);

If the trait were not sealed, its invariant could easily be violated.

§Values are tied to a scoped invariant lifetime

with and split both have very particular function signatures:

fn with<R, F>(scope: F) -> R
where
    F: for<'scope> FnOnce(Initial<'scope>) -> R,
{ /* ... */ }

fn split<'scope, T>(unique: T) -> (impl 'scope + Unique, impl 'scope + Unique)
where
    T: 'scope + Unique,
{ /* ... */ }

'scope can be thought of as the “scope” of the closure passed to with. No type bound by it can escape (or be returned from) the closure.

with(|initial| {
    // error: lifetime may not live long enough
    split(initial)
});

Furthermore, the lifetime is invariant in order to guarantee that impl Unique types are truly unique. If the lifetime were absent, such types would be 'static, allowing them to be downcast between each other since they would all share the same TypeId. Consider the following example:

// Because all `impl Unique` values are backed by the same concrete type,
// they share a `TypeId`. This allows them to be downcast between each other
// thanks to `Box<dyn Any>`'s `downcast` methods.
fn unify<T, U>(x: T, y: U) -> Option<(T, T)>
where
    T: 'static + Unique,
    U: 'static + Unique,
{
    let y: Box<dyn Any> = Box::new(y);
    match y.downcast::<T>() {
       Ok(y) => Some((x, *y)),
       Err(_) => None,
    }
}

Thankfully, this is forbidden from occurring since, due to the design of the crate, it is impossible to create an impl Unique type that is 'static.

§Values cannot be duplicated

No type that implements Unique also implements Copy or Clone as to forbid its instance from being trivially duplicated.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<'scope> Unique for Initial<'scope>