有条件地在宏调用中实现特征

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 特征(例如EqCloneCopy等。特别是,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不满足,所以这个实现无效”并继续,但它只是错误。

有没有办法让宏根据特征实现扩展到不同的代码?

Cha*_*man 0

这称为平凡界限。它有一个 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