对于模块路径+类型名称,应该在宏示例中使用哪些片段说明符(元变量类型)?

2023-12-30

我发现自己写的代码是这样的:

// Of course I could add a use statement, but there's still undesireable duplication.
// in the real code theres anywhere from 3 to 10 items in the tuple,
// but that would just clog up this example
pub fn cols() -> (crate::foo::bar::A, crate::foo::bar::B) {
    (crate::foo::bar::A, crate::foo::bar::B)
}

很多次了。我尝试创建一个宏来为我吐出这个函数:

macro_rules! impl_cols {
    ( $namespace:path, $($col_name:ty,)*) => {
        pub fn cols() -> ( $( $namespace::$col_name, )* ) {
            ( $( $namespace::$col_name, )* )
        }
    }
}

但无论我为元变量选择什么片段说明符(path,ident,ty,tt,其组合)它会出错。让它发挥作用的神奇咒语是什么?

类型A and B看起来像这样:

// In "foo.rs"

pub mod bar {
    pub struct A;
    pub struct B;
}

.


这里当然存在 XY 问题:我正在使用diesel https://diesel.rs/板条箱并具有结构Queryable在它们上导出 impl,我希望能够.select()填充给定结构的正确列。虽然可能还有其他解决方案,但我仍然想了解为什么我编写的宏不起作用,以及如果有的话什么会起作用。


你可以通过 tt-munching 来做到这一点:

macro_rules! impl_cols {
    (@build-tuple
        ( $($types:path,)* )
        ( $($ns:ident)::* )
        ( $col_name:ident, $($rest:tt)* )
    ) => {
        impl_cols! { @build-tuple
            (
                $($types,)* 
                $($ns::)* $col_name,
            )
            ( $($ns)::* )
            ( $($rest)* )
        }
    };
    // Empty case
    (@build-tuple
        ( $($types:path,)* )
        ( $($ns:ident)::* )
        ( )
    ) => {
        ( $($types,)* )
    };
    (
        $($ns:ident)::*,
        $($col_name:ident,)*
    ) => {
        pub fn cols() -> impl_cols! { @build-tuple
            ( )
            ( $($ns)::* )
            ( $($col_name,)* )
        } {
            impl_cols! { @build-tuple
                ( )
                ( $($ns)::* )
                ( $($col_name,)* )
            }
        }
    };
}

.

Edit:

简单匹配不起作用的原因是您无法在 Rust 宏中连接路径(没有过程宏的帮助)。这是因为,引用参考文献 (https://doc.rust-lang.org/reference/macros-by-example.html#transcribing https://doc.rust-lang.org/reference/macros-by-example.html#transcribing):

当将匹配的片段转发到另一个宏时,第二个宏中的匹配器将看到片段类型的不透明 AST。第二个宏不能使用文字标记来匹配匹配器中的片段,只能使用相同类型的片段说明符。 ident、lifetime 和 tt 片段类型是一个例外,可以通过文字标记进行匹配。

不仅在转发到另一个宏时如此,而且在转发到编译器时也是如此。

您想要构建两个不同的 AST 片段:Type https://doc.rust-lang.org/reference/types.html#type-expressions返回类型(元组中的每种类型),以及Expression https://doc.rust-lang.org/reference/expressions.html为了身体。那是,crate::foo::bar::A(例如)履行两个角色:在返回类型中它是一个类型(具体来说TypePath https://doc.rust-lang.org/reference/paths.html#paths-in-types),在正文中它是一个表达式(具体来说PathExpression https://doc.rust-lang.org/reference/expressions/path-expr.html).

如果我们看一下两者的定义(TypePath and PathExpression),我们看到它们本质上等于以下内容(忽略不相关的部分,如泛型和函数路径):

Path :
  ::? PathIdentSegment (:: PathIdentSegment)*

路径识别段 :
  识别码 https://doc.rust-lang.org/reference/identifiers.html | super | self | Self | crate | $crate

如果您不熟悉 EBNF 表示法,这意味着标识符列表 (:ident in macro_rules!), 隔开::s.

所以,当你做类似的事情时:

macro_rules! concat_ns {
    ($ns:path, $type:ident) => {
        fn my_fn() -> $ns :: $type { todo!() }
    };
}
concat_ns!(crate::foo::bar, A)

您的宏调用会构建类似于以下内容的 AST:

MacroInvocation
  ...
    Path
      PathIdentSegment `crate`
      PathIdentSegment `foo`
      PathIdentSegment `bar`
    COMMA
    IDENTIFIER `A`

您的宏想要构建一个类似于以下内容的 AST:

Function
  ...
  FunctionReturnType
    Type
      Path
        <Insert metavariable $ns here>
        <Insert metavariable $type here>

这给你:

Function
  ...
  FunctionReturnType
    Type
      Path
        Path
          PathIdentSegment `crate`
          PathIdentSegment `foo`
          PathIdentSegment `bar`
        PathIdentSegment `A`

但这是一个无效的 AST,因为Path只能包含PathIdentSegment而不是其他Path! (注意:这不是确切的过程,但或多或​​少是相同的)。

现在你也明白了为什么 tt-munching 解决方案有效:在那里,我们从不创建一个Path节点,只保留原始标识符。我们can连接原始标识符并创建一个Path来自它们(这通常是使用 tt-munchers 的原因:当我们不需要使用 Rust 宏语法片段捕获功能时,因为我们想在之后恢复它们)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对于模块路径+类型名称,应该在宏示例中使用哪些片段说明符(元变量类型)? 的相关文章

随机推荐

  • Autofac 具有同一接口的多个实现

    我正在使用 Autofac 并且希望有一个接口的多个实现 如何配置 Autofac 以便根据当前类型解决依赖关系 更具体地说 我有一个接口和多个应该链接在一起的实现 让我解释一下 虚构的类 public interface IMessage
  • 在postgresql中创建表空间

    我正在尝试在 postgres 中创建表空间 但遇到所有权问题 我正在使用的命令是 CREATE TABLESPACE magdat OWNER maggie LOCATION home john BSTablespace 我收到错误 ER
  • .NET 4 中是否允许通用运算符重载?

    我假设 否 但我在谷歌上找不到确凿的证据来支持这个假设 使用关键字 vb net 通用运算符重载 仅产生 1 个结果 删除 重载 会产生更多结果 但没有直接说明该问题 我的想法是给定一个抽象类 如果能够实现派生类可以使用的通用运算符重载 在
  • 锁损坏了?魔法僵局?

    我正在处理多线程错误 现在我看到由于某种原因锁甚至没有执行一次而是被锁定了 我还有下一堂课 public sealed class Foo private readonly object lock new object private st
  • 使用 Python 关闭 AWS Lambda 执行上下文后进行清理

    来自使用 AWS Lambda 函数的最佳实践 https docs aws amazon com lambda latest dg best practices html 利用执行上下文重用来提高函数的性能 在函数处理程序之外初始化 SD
  • 使用 Maven 2 构建可运行的 jar

    我对 Maven 咒语相对较新 但我正在尝试使用 Maven 构建一个命令行可运行的 jar 我已经设置了我的依赖项 但是当我运行时mvn install并尝试运行罐子 发生了两件事 首先 没有找到主类 这是可以纠正的 当我更正这个问题后
  • 推送到 vercel 后无法连接到 websocket 服务器

    当我在本地运行我的服务器时 它工作得很好 但是 一旦我将其上传到 vercel 我就会收到类似 polling xhr js 202 GET 的错误400 与 wss giphy chat server vercel app socket
  • 如何使用 gimp 脚本 fu 保存(导出)所有图层?

    有了 gimp fu 我可以保存以下内容one层 至少 这就是我解释的定义gimp file save因为它需要参数drawable 现在 我有以下脚本 from gimpfu import def write text width 400
  • 将构建路径切换到 JDK 10 后,Eclipse 找不到 XML 相关类

    我正在开发一个Maven项目 https github com fcarsten portal core git 分支 platform b om brussels sr7 在 Eclipse 中 当我最近尝试将项目的 Java 构建路径切
  • 如何将Java编译为WASM(WebAssembly)?

    我想知道我可以使用Java并将其编译为WASM WebAssembly 吗 列表https web assembly org getting started developers guide https webassembly org ge
  • setup.py:限制python解释器允许的版本

    我有一个Python 库 不幸的是我还没有更新它以支持 Python 3 In its setup py 我补充说 install requires python lt 3 我的目的是不允许在 Python 3 下安装 使用这个包 因为我知
  • 找到具有给定总和的三元组

    一段时间以来 我一直在为这些问题苦苦挣扎 问题是这样的 我们有 n 2 个数字 我们需要找出是否存在一个三元组 a b c 使得 a b c 0 对于更一般的情况 a b c k k 已给出 存在复杂度为 O n 2log n 的解决方案
  • MAC OS X 10.6 上 MySQL C API 的编译问题

    我在使用 MySQL C API 和 Mac OS X 10 6 时遇到问题 当我想编译 SQL 客户端程序时 它会中止并显示此错误消息 ld 找不到 lmysqlclient 的库 Collect2 ld 返回 1 退出状态 我用这个命令
  • 为 macOS 构建,但链接到为独立构建的目标文件中

    我正在构建 ffmpeg 并且多个文件出现以下错误 我不是专门用独立式旗帜构建的 所以不明白为什么会发生这种情况 它到底意味着什么 它有问题吗 还是一个可忽略的警告 如果出现问题 有人知道如何解决吗 Building for macOS b
  • 对数据库表中的列进行排序

    当涉及数据库表中的列顺序时 是否有任何标准或至少是最佳实践 这是我遵循的手工约定 主键 即id 独特的列 即email ssn 外键 即article 保存用户生成的数据的列 即first name last name columns ho
  • 获取光标下的所有小部件

    The widgetAt函数以最高的 z 顺序为我提供了直接位于光标下方的小部件 pos QtGui QCursor pos widget QtGui qApp widgetAt pos 但我怎样才能得到all光标下的小部件 包括最上面那个
  • 获取活动模型序列化器生成的 JSON 中的 kaminari 分页链接

    我正在尝试使用 AdminSerializer 将 admins 转换为 JSON app serializers admin serializer rb class AdminSerializer lt ActiveModel Seria
  • 从 Xamarin 可移植类库发送 Http 请求

    我在这个非常简单的任务上遇到了相当大的问题并寻求帮助 问题是 从 Xamarin PCL 执行超时的 Http 请求的最佳和最可靠的方法是什么 有几种选择 使用旧的 HttpWebRequest 这个解决方案肯定应该有效 但是我对这个问题中
  • 微服务架构中的 ASP.NET 身份

    我正在尝试通过将主要组件分解为单独的 Web 服务器来使用微服务架构来实现 Web 应用程序 我正在使用 ASP NET Identity 仅电子邮件 用户名登录 没有 Facebook 等 和 主 应用程序服务器实现身份验证服务器 我当前
  • 对于模块路径+类型名称,应该在宏示例中使用哪些片段说明符(元变量类型)?

    我发现自己写的代码是这样的 Of course I could add a use statement but there s still undesireable duplication in the real code theres a