今天我在这里遇到了这种语法:
https://github.com/binator/self/tree/80ba2ade?tab=readme-ov-file#example
fn hex_primary<Stream, Context>(stream: Stream) -> Parsed<u8, Stream, Context>
where
(): IntRadixParse<Stream, Context, u8>,
{
uint_radix(2, Radix::HEX).parse(stream)
}
对我来说,它看起来像“绑定在单位类型(又名空元组)”,但我无法理解它。默认情况下,单一类型不会实现每个特征,不是吗?不幸的是,官方文档太模糊且不够完整(同时过于冗长),我找不到任何相关内容。
原始 RFC for
where
子句也提到了这个语法,但没有正确的解释:fn increment<T>(c: T) -> T
where () : Add<int,T,T>
{
1 + c
}
但除此之外,我知道这样的界限不仅可以在特征泛型中指定。
那么它是什么,什么时候使用,为什么需要它,它解决哪些问题?
在代码片段中,
where
子句用于缩小通用约束范围。不是定义所有约束,而是使用 (): IntRadixParse<Stream, Context, u8>
,这意味着无论 Stream
和 Context
具有什么类型,它们都必须遵循 IntRadixParse
的约束。
特质
IntRadixParse
是
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>;
注意:它使用 trait 别名不稳定功能
因此,要编写没有奇怪的单位约束语法的函数,它会是这样的
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)
}
注意:函数中
Token
替换为u8
另一个示例:
#![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() {}
(): IntRadixParse<Stream, Context, u8>
确实是单位类型的界限。它之所以有效,是因为特征别名1的定义如下:
pub trait IntRadixParse<Stream, Context, Token: 'static> = where
…; // where bounds omitted for brevity
特别注意
where
之前缺失的特征,没有特征,所以在条件成立的情况下,这个别名是没有任何特征的别名。
每种类型都满足“实现一组空的特征”,因此编译器唯一需要证明的就是边界。简而言之,因为
Self
没有被引用,并且没有任何特征可以满足,所以您可以在那里编写任何类型,而不是单位 ()
。当类型不重要时,Rust 程序员经常使用单位。
这使得特征别名对于缩写一堆边界非常有用,就像 Timsib Adnap 的答案
中所解释的那样