我发现自己写的代码是这样的:
// 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
和B
如下所示:
// In "foo.rs"
pub mod bar {
pub struct A;
pub struct B;
}
铁锈操场上的一个(错误)例子。
当然这里有一个XY问题:我使用的是diesel板条箱,它的结构上有Queryable
派生impl,我希望能够。选择()。虽然可能还有其他解决方案,但我仍然想了解我编写的宏为什么不起作用,以及如果有任何问题,什么会起作用。
你可以用tt咀嚼来做到:
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,)* )
}
}
};
}
操场。
编辑:
简单匹配不起作用的原因是无法在Rust宏中连接路径(没有过程宏的帮助)。这是因为,引用参考文献(https://doc.rust-lang.org/reference/macros-by-example.html#transcribing):
通过示例将匹配的片段转发给另一个宏时,第二个宏中的匹配器将看到片段类型的不透明AST。第二个宏不能使用文字标记来匹配匹配器中的片段,只能使用相同类型的片段说明符。ident、life和tt片段类型是一个例外,可以通过文本标记进行匹配。
这不仅在转发到另一个宏时是正确的,而且在转发到编译器时也是正确的。
您想要构建两个不同的AST片段:返回类型(元组中的每个类型)的Type
和主体的Express
。也就是说,crate::foo::bar::A
(例如)履行两个角色:在返回类型中,它是一个类型(特别是TypePath
),在正文中,它是一个表达式(具体地说Path表达式
)。
如果我们看一下这两个的定义(TypePath
和Path表达式
),我们会发现它们基本上等于以下内容(忽略泛型和函数路径等不相关的部分):
路径:
PathIdentintSegment:
如果您不熟悉EBNF符号,这意味着标识符列表(: ident
inmacro_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
只能包含pathIdentitSegment
而不能包含其他Path
s!(注意:这不是确切的过程,但大致相同)。
现在您也了解了tt-munching解决方案的工作原理:在这里,我们从不创建路径
节点,只保留原始标识符。我们可以连接原始标识符并从中创建一个路径
(这通常是使用tt munchers的原因:当我们不需要使用Rust宏的语法片段捕获能力时,因为我们希望在之后恢复它们)。
通读ANSI C Yacc语法规范后,我注意到以下内容都是有效的: 这对我来说似乎很奇怪,因为我对类型的理解表明这些变量都没有类型。这些是什么意思?如何检查它们的类型?分配了多少内存?
本文向大家介绍Rust 片段说明符—模式的种类,包括了Rust 片段说明符—模式的种类的使用技巧和注意事项,需要的朋友参考一下 示例 在中$e:expr,expr称为片段说明符。它告诉解析器该参数$e期望什么样的令牌。Rust提供了多种片段说明符,使输入非常灵活。 说明符 描述 例子 ident 识别码 x, foo path 合格名称 std::collection::HashSet, Vec:
问题内容: 我在Go中找到了符文类型,并有一个简单的问题,但值得一提。 我发现它是int32的别名,目的是区分数字和字符值。 http://golang.org/pkg/builtin/#rune 但是我对“符文”一词感到困惑,实际上它代表什么?例如uint == unsigned int 问题答案: 但是我对“符文”一词感到困惑,实际上它代表什么?例如uint == unsigned int 符
我有那段代码(这是从一个更大的项目中复制的最小值)。 它在gcc 9.2中失败,并出现以下错误: 在“constexpr const auto poly_gcd,myint”的实例化中 静态配置的自动val=poly_gcd_reduce_helper::val; 显然,编译器试图实例化poly_gcd_reduce_helper,void 我必须承认我现在不知道该怎么办。
问题内容: 我正在编写一个小脚本,该脚本从目录获取文件名,并将其传递给另一个模块,然后该模块导入文件。 因此流程类似于1)获取模块名称(将其存储在变量中)2)将此变量名称传递给模块3)导入名称存储在变量名称中的模块 我的代码就像 问题在于,当它到达import语句时,它将modulename读取为真实的模块名称,而不是存储在此变量中的值。我不确定这在python中如何工作,解决这个问题的任何帮助都
本文向大家介绍Java变量类型与示例,包括了Java变量类型与示例的使用技巧和注意事项,需要的朋友参考一下 Java变量 变量是用户定义的存储块名称,它们的值可以在程序执行期间随时更改。它们在类/程序中起着重要的作用,因为它们有助于存储,检索数据值。 Java中变量的类型 有三种类型的Java变量, 实例变量 局部变量 类/静态变量 1)实例变量 实例变量在类中声明,但在方法,块或构造函数之外。