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.