为什么要通过 where 子句中的通用特征来约束单位类型(如 `where () : Trait<…>`)?

问题描述 投票:0回答:2

今天我在这里遇到了这种语法:
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
子句也提到了这个语法,但没有正确的解释:
https://rust-lang.github.io/rfcs/0135-where.html#alternatives

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

但除此之外,我知道这样的界限不仅可以在特征泛型中指定。
那么它是什么,什么时候使用,为什么需要它,它解决哪些问题?

generics rust syntax traits unit-type
2个回答
3
投票

在代码片段中,

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() {}

3
投票

(): IntRadixParse<Stream, Context, u8>
确实是单位类型的界限。它之所以有效,是因为特征别名1的定义如下:

pub trait IntRadixParse<Stream, Context, Token: 'static> = where
    …; // where bounds omitted for brevity

特别注意

where
之前缺失的特征,没有特征,所以在条件成立的情况下,这个别名是没有任何特征的别名。

每种类型都满足“实现一组空的特征”,因此编译器唯一需要证明的就是边界。简而言之,因为

Self
没有被引用,并且没有任何特征可以满足,所以您可以在那里编写任何类型,而不是单位
()
。当类型不重要时,Rust 程序员经常使用单位。

这使得特征别名对于缩写一堆边界非常有用,就像 Timsib Adnap 的答案

中所解释的那样
  1. 特质别名不稳定
© www.soinside.com 2019 - 2024. All rights reserved.