cam*_*024 5 macros metaprogramming traits rust
我正在创建一个宏来为某些基本类型生成瘦包装器,例如语法:
wrapper!(String => Email);
Run Code Online (Sandbox Code Playgroud)
会扩展到大致类似:
struct Email {
inner: String,
}
impl Email {
// some boilerplate-y stuff
}
Run Code Online (Sandbox Code Playgroud)
但是,我希望这些类型在适当的情况下实现一些 stdlib 特征(例如Eq、Clone、Copy等。特别是,Copy在这里引起问题。
我希望包装器类型是Copy内部类型Copy(类似于#[derive]工作方式),但是如果我在生成的特征上放置派生实现,如果不兼容,我会收到错误:
macro_rules! wrapper {
($inner:ty => $name:ident) => {
#[derive(Copy)]
struct $name {
inner: $inner
}
}
}
wrapper!(i64 => Version); // works fine
wrapper!(String => Email); // Error, String is not Copy
Run Code Online (Sandbox Code Playgroud)
我宁愿它只是“没有尝试实现Copy”,而不是使用非复制类型时出现编译时错误,但我不确定如何在宏中表达这一点。
另一种不成功的尝试如下:
// inside macro
impl Copy for $name where $inner: Copy {}
Run Code Online (Sandbox Code Playgroud)
但这给了我同样的String is not Copy错误。我本来希望,在扩展过程中,当它遇到:impl Copy for Email where String: Copy它会意识到:“String: Copy不满足,所以这个实现无效”并继续,但它只是错误。
有没有办法让宏根据特征实现扩展到不同的代码?
这称为平凡界限。它有一个 RFC,但只得到了部分应用。我不确定为什么(RFC、跟踪问题和实施 PR 都没有提到原因)。
在夜间,您可以通过打开该trivial_bounds功能(Playground)来做到这一点。
如果您被迫稳定,您可以使用此评论中的以下丑陋技巧:
#[derive(Clone, Copy)]
pub struct Wrapper<'a, T> {
pub inner: T,
_marker: std::marker::PhantomData<&'a ()>,
}
macro_rules! wrapper {
($inner:ty => $name:ident) => {
struct $name {
inner: Wrapper<'static, $inner>,
}
impl Copy for $name where for<'a> Wrapper<'a, $inner>: Copy {}
impl Clone for $name
where
for<'a> Wrapper<'a, $inner>: Clone,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
};
}
Run Code Online (Sandbox Code Playgroud)
游乐场。
生命周期欺骗 Rust 认为这是一个通用结构,它无法检查它是否实现了Copy。