Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why constrain the unit type by a generic trait bound in a `where` clause (as in `where () : Trait<…>`)?

Today I've encountered a somewhat weird syntax - where ():

fn hex_primary<Stream, Context>(stream: Stream) -> Parsed<u8, Stream, Context>
where
  (): IntRadixParse<Stream, Context, u8>,
{
  uint_radix(2, Radix::HEX).parse(stream)
}

To me it looks like "bound on unit type (aka empty tuple)", but I can't make sense of it. The () type doesn't implement every trait by default, does it? Alas, the official documentation is too vague and not complete enough (while being overly verbose), and I couldn't find anything related there.

The original RFC for where clause also mentions this syntax, but without proper explanation:

fn increment<T>(c: T) -> T
    where () : Add<int,T,T>
{
    1 + c
}

Besides, I also know that such bounds can be specified not only in trait generics.
So what is it, when is it used, why is it needed and which problems does it solve?

like image 787
cher-nov Avatar asked Dec 16 '25 13:12

cher-nov


1 Answers

In the code snippet, the where clause is used to narrow down the generic constraints. Instead of defining all the constraints, (): IntRadixParse<Stream, Context, u8> is used which means that whatever type Stream and Context have, they must follow the constraints of IntRadixParse.

The trait IntRadixParse is

pub trait IntRadixParse<Stream, Context, Token: 'static> = where
  Stream: Streaming,
  <Stream as Streaming>::Item: Into<u8>,
  Token: CheckedAdd + CheckedMul + CheckedSub + Zero + Copy + Debug,
  Context: Contexting<IntRadixAtom<Token>>,
  Context: Contexting<BaseAtom<u8>>,
  Context: Contexting<CoreAtom<Stream>>,
  Context: Contexting<UtilsAtom<Stream>>,
  u8: AsPrimitive<Token>;

Note: It uses the trait alias unstable feature

So to write the function without the weird unit constraint syntax, it would be something like

fn hex_primary<Stream, Context>(stream: Stream) -> Parsed<u8, Stream, Context>
where
  Stream: Streaming,
  <Stream as Streaming>::Item: Into<u8>,
  Context: Contexting<IntRadixAtom<u8>>,
  Context: Contexting<BaseAtom<u8>>,
  Context: Contexting<CoreAtom<Stream>>,
  Context: Contexting<UtilsAtom<Stream>>,
{
  uint_radix(2, Radix::HEX).parse(stream)
}

Note: In the function Token was replaced with u8

Another example:

#![feature(trait_alias)]

trait A<T> = where T: B; // Similar to `IntRadixParse` which
                         // shows that T should implement B

/// trait B with a method a
trait B {
    fn a(self);
}

/// implemented for unit
impl B for () {
    fn a(self) {}
}

fn a<T>(a: T) where (): A<T> {
    a.a() // T gets inferred to have implemented B and `a()` can be called
}

fn main() {}
like image 54
Timsib Adnap Avatar answered Dec 19 '25 07:12

Timsib Adnap



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!