gpt4 book ai didi

generics - Rust 宏接受具有泛型参数的类型

转载 作者:行者123 更新时间:2023-11-29 07:47:52 25 4
gpt4 key购买 nike

我有一个实现特征的宏,impl_Trait!()。现在,它适用于没有泛型参数的类型,但我不确定如何将类型参数添加到 impl 关键字。

macro_rules! impl_FooTrait {
($name:ty) => {
impl $crate::FooTrait for $name { ... }
};
}

struct Bar(i32);
impl_FooTrait!(Bar);
// All OK

struct Baz<'a>(&'a i32);
impl_FooTrait!(Baz<'a>);
// use of undeclared lifetime name `'a`

最佳答案

首先使用 macro_rules! 解析泛型以万无一失的方式非常困难(可能是不可能的),因为模式不支持混合重复(例如 $( $( $lt:lifetime ) | $( $gen:ident )* )* ,它将匹配生命周期 ( 'a ) 或通用参数 ( T ))。

如果需要,您应该考虑使用 proc-macro (您甚至可以使用 proc-macro-hack 将它们放在表达式位置)。

简单地将代码放在这里而不作任何解释对任何人都没有好处,因此下面将介绍理解最终声明性宏所需的所有步骤:)


Hello<'a, 'b> 的形式解析输入或 Hello比较简单:

macro_rules! simple_match {
(
// name of the struct/enum
$name:ident
// only one or none `<>`
$(<
// match one or more lifetimes separated by a comma
$( $lt:lifetime ),+
>)?
) => {}
}

simple_match!( Hello<'a, 'b, 'static> );

一个人可能也有受限的生命周期(例如 Hello<'a, 'b: 'a, 'static> ),不能用上面的方法解析。

为了也解析它,必须将以下模式添加到 $lt:lifetime 的末尾:

// optional constraint: 'a: 'b
$( : $clt:lifetime )?
macro_rules! better_match {
(
// name of the struct/enum
$name:ident
// only one or none `<>`
$(<
// match one or more lifetimes separated by a comma
$(
$lt:lifetime
// optional constraint: 'a: 'b
$( : $clt:lifetime )?
),+
>)?
) => {}
}

better_match!( Hello<'a, 'b: 'static> );

以上仅限于单个受限生命周期(Hello<'a: 'b + 'c> 将无法解析)。为了支持多个受限生命周期,必须将模式更改为:

$(
: $clt:lifetime
// allow `'z: 'a + 'b + 'c`
$(+ $dlt:lifetime )*
)?

这就是解析通用生命周期所需的一切。也可以尝试解析排名更高的生命周期,但这会使模式更加复杂。

所以解析生命周期的最终宏看起来像这样

macro_rules! lifetimes {
( $name:ident $(< $( $lt:lifetime $( : $clt:lifetime $(+ $dlt:lifetime )* )? ),+ >)? ) => {}
}

lifetimes!( Hello<'b, 'a: 'b, 'static, 'c: 'a + 'b> );

上面的宏只允许生命周期,可以通过替换 lifetime 来修复。与 tt在模式中(生命周期和通用参数都可以解析为 tt ):

macro_rules! generic {
( $name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? ) => {}
}

generic!( Hello<'b, 'a: 'b, 'static, 'c: 'a + 'b> );
generic!( Hello<T: Display, D: Debug + 'static + Display, 'c: 'a + 'b> );

正如我上面提到的,我认为目前无法区分生命周期和特征界限。如果需要,可以使用 ( $(+ $lt:lifetime )* $(+ $param:ident )* ) 部分完成, 但这不适用于未排序的边界,如 Hello<'a, T, 'b>T: 'a + Debug + 'c .


impl_trait -宏会这样写:

use std::fmt::{Debug, Display};

trait ExampleTrait {}

struct Alpha;
struct Beta<'b>(&'b usize);
struct Gamma<T>(T);
struct Delta<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a> {
hello: &'a T,
what: &'b D,
}

macro_rules! impl_trait {
( $name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? ) => {
// I split this over multiple lines to make it more readable...
// this is essentially just a copy of the above match without the
// type annotations
impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
ExampleTrait
for $name
// the bounds are not required here
$(< $( $lt ),+ >)?
{}
}
}

impl_trait!(Alpha);
impl_trait!(Beta<'b>);
impl_trait!(Gamma<T>);
impl_trait!(Delta<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a>);

注意:不支持路径(例如 impl_trait!(Hello<D: std::fmt::Display>)


下面的宏在一次调用中使用多个结构:

macro_rules! impl_trait_all {
( $( $name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? ),+ ) => {
$(
// I split this over multiple lines to make it more readable...
// this is essentially just a copy of the above match without the
// type annotations
impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?
ExampleTrait
for $name
// the bounds are not required here
$(< $( $lt ),+ >)?
{}
)+
}
}

impl_trait_all!(
Alpha,
Beta<'b>,
Gamma<T>,
Delta<'b, 'a: 'static + 'b, T: 'a, D: Debug + Display + 'a>
);

Link to playground with all the code

关于generics - Rust 宏接受具有泛型参数的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41603424/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com