为什么 serde 的 Serialize/Deserialize 派生会在 const _: () 块中生成代码?

Qua*_*Dot 4 compile-time rust rust-proc-macros

注意:这个问题并不是专门关于serde:它是关于过程宏和 const 函数。

我正在研究serde的派生宏为Serialize和生成的代码Deserialize,但我还没有弄清楚为什么它的所有代码都在const _: ().

例如,

#[derive(Deserialize)]
struct MyNewType(i32);
Run Code Online (Sandbox Code Playgroud)

扩展为(使用cargo expand):

#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
    #[allow(unused_extern_crates, clippy::useless_attribute)]
    extern crate serde as _serde;
    #[automatically_derived]
    impl<'de> _serde::Deserialize<'de> for MyNewType {
        fn deserialize<__D>(
            __deserializer: __D,
        ) -> _serde::__private::Result<Self, __D::Error>
        where
            __D: _serde::Deserializer<'de>,
        {
         // generated implementation omitted for brevity
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

我发现,如果我仔细复制块内的内容const _: () = {...},并在删除标签后将其粘贴回源代码中#[derive(Deserialize)],那么一切似乎都会编译并且反序列化继续正常工作。const _: ()那么,在我的程序中将所有内容放入块中有何目的呢?

Sil*_*olo 5

它防止将新名称引入外部范围。当您复制粘贴到自己的范围时,您也复制了这一行。

#[allow(unused_extern_crates, clippy::useless_attribute)]
extern crate serde as _serde;
Run Code Online (Sandbox Code Playgroud)

_serde这引入了一个被称为当前作用域的名称。如果您不知道查找它,这可能会与您自己的代码发生冲突。更糟糕的是,如果多个Deserialize宏在同一范围内扩展,那么它们都会引入相同的名称,这是一个问题。

const _: () = {
  ...
}
Run Code Online (Sandbox Code Playgroud)

因此,我们运行一个块来获取一个不会干扰任何内容的新本地范围。该块返回(),我们忽略了绑定。我不完全确定为什么他们需要一个变量(而不仅仅是大括号),尽管我怀疑它可能是为了抑制未使用的变量警告或防止死代码消除。

  • 你不能在顶层放置一个块。唯一合理的东西是函数或模块,但它们都引入了名称。常量可以是未命名的。 (3认同)