Correct name for a trait used to select implementations

In Rust you cannot have specialization or disjoint trait-based implementations, but you can use a special trait and the types implementing it to implement a trait differently. E.g.,

This is not technically a marker trait—more a selector trait. Is there a standard name for this technique?

You normally wouldn't have the Selector trait at all. It does nothing here.

True, but in this way IDEs can help choosing the right type, since they know it must implement Selector.

How would you call the technique, then, in general? Type-dependent implementation? Type-selected implementation?

I have used this technique and called the types representatives or sentinels or whatnot, but am unaware of a standard name for the pattern.

I've also used this technique and curiously, I called it a FooSelector.

A and B function as tag types (or type tags) here, so the trait could be called a tag trait ("a trait for type tags"). If the trait is nonempty and encodes some behavior that the tagged type utilizes, then it’s a case of the policy pattern (the term "policy" comes from C++, essentially the strategy pattern but with static dispatch) and the trait could be called a policy trait.

Yes, at this time I think "selector trait" is the best name. I'm really stunned that this does not have a name—it seems to be this is a quite important technique, given that the type system has no specialization, no negative types, and even selecting implementations using associated types is very complicated.

1 Like

For the record, in the documentation of dsi-bitstream we ended up using this nomenclature:

  • A selector type A is a type (usually empty) used to implement parametrically on A traits on a struct T<S>, where S is a type parameter, by implementing the trait for T<A>. A different selector type B will be associated with a different implementation (usually of the same traits) on T<B>.

  • A marker trait for selector types M is a conveniency trait that helps in implementing the pattern above by definining T<S: M> rather than T<S>, which helps with IDE, etc.

In particular, if one wants to be sure that only a finite, predefined set of selector types are available one can seal the marker trait, as in

mod private {
    pub trait M {}
}

trait M: private::M {}

impl<T: private::M> M for T {}

and then implement the private marker trait for the relevant selector types.

Perhaps worth noting that this is (especially if the trait is sealed) the type-level equivalent to enums, where the selector types correspond to enum variants. (Cf. type-level numbers, eg. the typenum crate). But this may not help much when it comes to naming :frowning:

The place where I used them is in this experiment, which was exploring the use of generics in a (specific) implementation of the Init Struct Pattern – X Blog – A blog for Xaeroxe, Software Engineer It gets used there with associated types, default type parameters.