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 in it.
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
}
But besides that, I 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?
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() {}