【问题标题】:How to match trait bounds in a macro?如何匹配宏中的特征边界?
【发布时间】:2018-07-29 11:04:17
【问题描述】:

我正在尝试匹配泛型类型的特征边界:

macro_rules! test {
    (
        where $(
            $bounded_type:ident: $( $bound:tt )++,
        )+
    ) => {
        // Dummy expansion for test:
        struct Foo<T, U>
        where $(
            $bounded_type : $( $bound )++,
        )+
        {
            t: T,
            u: U
        }
    }
}

test! {
    where
        T: PartialEq + Clone,
        U: PartialEq,
}

fn main() {}

不幸的是,如果我理解得很好,匹配 trait 的唯一方法是 tt 片段,但是这个片段几乎可以匹配任何东西,所以无论我做什么,都会出错:

error: local ambiguity: multiple parsing options: built-in NTs tt ('bound') or 1 other option.

如何匹配这段代码?

请注意,我不需要非常优雅的东西(我不需要公共用户),但当然,越优雅越好。

【问题讨论】:

  • 我在匹配和发射类型边界方面遇到了很多问题。那是不久前的事了,但我想我最终只是做了其他事情......
  • @PeterHall Rust 团队应该为此做点什么。 IMO,宏系统应该允许轻松匹配和发出语言语法。

标签: macros rust


【解决方案1】:

最好的办法是阅读source code for the parse-generics-shim crate;它有点旧,但希望仍然可以工作。这方式太复杂了,无法在 Stack Overflow 问题中解释,因为它基本上涉及复制 + 粘贴该板条箱的来源到答案中。

更简单的方法是解析实际的Rust语法,并使用宏解析器可以处理的东西,比如将约束包装在一个组中(比如{ ... } )。

【讨论】:

  • parse_where_shim!在这个文件github.com/DanielKeep/rust-parse-generics/blob/master/…中调用parse_where!,但是我找不到后者的定义。
  • @Boiethios 您会注意到下一个宏称为parse_where_shim!,并且具有精确的逆条件编译条件。 parse_where! 是被拒绝的编译器的提议添加;因此不使用它的替代实现。
【解决方案2】:

我能够通过将第一个绑定与其余绑定分开来使其匹配。

macro_rules! test {
    (
        where $(
            $bounded_type:ident: $bound:tt $(+ $others:tt )*,
        )+
    ) => {
        // Dummy expansion for test:
        struct Foo<T, U>
        where $(
            $bounded_type : $bound $(+ $others)*,
        )+

        {
            t: T,
            u: U
        }
    }
}

但是,如果特征有参数,这将不起作用。

【讨论】:

  • 不幸的是,我确实有通用的特征界限:(但你的想法很好!
  • 我最初预计 tt 实际上会匹配 Into&lt;u32&gt; 之类的东西。我认为您确实想使用ty,但是对于可以遵循的内容有太多限制-例如+ 不能。
  • 我尝试使用带有逗号分隔符和分号终止的ty,但是当我尝试扩展宏时,出现此错误:expected where, or {` 在结构名称后`
【解决方案3】:

自 2016 年左右this issue 关闭以来,您可以使用path 宏类型来匹配TypePaths。

以我自己的代码为例:

($name:ident<$($l:lifetime, )*$($x:ident : $xt:path),+>($s:ident$(, $a:ident: $t:ty)*) -> $ret:ty  => $body:block) => {
}

【讨论】:

  • 我对这个答案和the documentation on TypePaths 感到困惑。按照名称,我希望TypePathfoo::bar::MyType 中的foo::bar$type:ty 通常已经匹配类型,包括 foo::bar 部分,所以我不确定有什么区别。
  • 这也处理具有多个约束的边界,例如T: Clone + Eq?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-29
  • 1970-01-01
  • 2016-12-23
  • 2020-08-17
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
相关资源
最近更新 更多