Rust in Everything
基于 serde 1.0.229

泛型与 bound

主线位置:两条主线的起点之前。derive 在写出 impl Serialize for Post 和 impl<'de> Deserialize<'de> for Post 的函数体之前,先要定下 impl 头:有哪些泛型参数、where 里要求什么。这一页讲这一步:bound.rs 怎么从字段类型里找出类型参数,de.rs 的 build_generics 怎么再加上 'de 和借用的生命周期,以及 Self 为什么要先换成具体的类型名。

flowchart TB
  input["原始 Generics"] --> nodef[without_defaults]
  nodef --> fieldb["字段 / 变体的 bound 属性"]
  fieldb --> cont{"容器写了 bound?"}
  cont -->|写了| use[原样用它]
  cont -->|没写| selfb["default 时加 Self: Default"]
  selfb --> withb["with_bound 推断 T: Trait"]
  withb --> defb["skip 字段加 T: Default"]
  use --> de["前面加 'de"]
  defb --> de

函数体怎么生成在生成的 Serialize 和生成的 Deserialize;bound、borrow 这些属性怎么从 #[serde(...)] 里解析出来在属性。本页只讲 impl 头。bound.rs 431 行,一半是遍历语法树的样板。实验在 M2 Max、macOS、rustc 1.99.0 上跑,依赖 serde = "=1.0.229"(derive)、serde_json = "=1.0.154",展开用 cargo expand。

两个 build_generics

ser.rs 和 de.rs 各有一个 build_generics,结构相同。序列化一侧:

fn build_generics(cont: &Container) -> syn::Generics {
    let generics = bound::without_defaults(cont.generics);

    let generics =
        bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);

    let generics =
        bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound);

    match cont.attrs.ser_bound() {
        Some(predicates) => bound::with_where_predicates(&generics, predicates),
        None => bound::with_bound(
            cont,
            &generics,
            needs_serialize_bound,
            &parse_quote!(_serde::Serialize),
        ),
    }
}

四步:

  1. without_defaults 去掉类型参数的默认值。struct WithDefault<T = u8> 展开成 impl<T> _serde::Serialize for WithDefault<T>,注释说留着默认值会报「associated type bindings are not allowed here」。
  2. 字段上的 #[serde(bound = "...")] 和变体上的 #[serde(bound = "...")] 原样追加到 where 里(with_where_predicates_from_fields、with_where_predicates_from_variants)。
  3. 容器上写了 bound,就把它原样放进 where(with_where_predicates),不再推断。
  4. 没写,就调 with_bound 推断,过滤条件是 needs_serialize_bound:
fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
    !field.skip_serializing()
        && field.serialize_with().is_none()
        && field.ser_bound().is_none()
        && variant.map_or(true, |variant| {
            !variant.skip_serializing()
                && variant.serialize_with().is_none()
                && variant.ser_bound().is_none()
        })
}

不由 derive 自己序列化的字段(跳过的、交给 serialize_with 的)不参与推断;自己写了 bound 的字段也不参与,免得和手写的那条重复或冲突。

反序列化一侧在第 4 步多了两件事:

    match cont.attrs.de_bound() {
        Some(predicates) => bound::with_where_predicates(&generics, predicates),
        None => {
            let generics = match *cont.attrs.default() {
                attr::Default::Default => bound::with_self_bound(
                    cont,
                    &generics,
                    &parse_quote!(_serde::#private::Default),
                ),
                attr::Default::None | attr::Default::Path(_) => generics,
            };

            let delife = borrowed.de_lifetime();
            let generics = bound::with_bound(
                cont,
                &generics,
                needs_deserialize_bound,
                &parse_quote!(_serde::Deserialize<#delife>),
            );

            bound::with_bound(
                cont,
                &generics,
                requires_default,
                &parse_quote!(_serde::#private::Default),
            )
        }
    }

推断的 trait 是 Deserialize<'de>(或 Deserialize<'static>,见后面的 'de 一节);容器写了 #[serde(default)] 时加一条 Self: Default;字段要用 Default::default() 填的,给其中的类型参数加 T: Default。容器上的 bound 一旦写了,这三条都不加。

with_bound:在语法树里找类型参数

with_bound 的注释用一个例子说清了它要做什么:

// Puts the given bound on any generic type parameters that are used in fields
// for which filter returns true.
//
// For example, the following struct needs the bound `A: Serialize, B:
// Serialize`.
//
//     struct S<'b, A, B: 'b, C> {
//         a: A,
//         b: Option<&'b B>
//         #[serde(skip_serializing)]
//         c: C,
//     }

它在函数里定义了一个只用一次的访问器 FindTyParams,三个字段:

    struct FindTyParams<'ast> {
        // Set of all generic type parameters on the current struct (A, B, C in
        // the example). Initialized up front.
        all_type_params: HashSet<syn::Ident>,

        // Set of generic type parameters used in fields for which filter
        // returns true (A and B in the example). Filled in as the visitor sees
        // them.
        relevant_type_params: HashSet<syn::Ident>,

        // Fields whose type is an associated type of one of the generic type
        // parameters.
        associated_type_usage: Vec<&'ast syn::TypePath>,
    }

对每个通过 filter 的字段调 visit_field,它再沿着类型的语法树往下走。真正做判断的只有两个方法:

        fn visit_field(&mut self, field: &'ast syn::Field) {
            if let syn::Type::Path(ty) = ungroup(&field.ty) {
                if let Some(Pair::Punctuated(t, _)) = ty.path.segments.pairs().next() {
                    if self.all_type_params.contains(&t.ident) {
                        self.associated_type_usage.push(ty);
                    }
                }
            }
            self.visit_type(&field.ty);
        }

        fn visit_path(&mut self, path: &'ast syn::Path) {
            if let Some(seg) = path.segments.last() {
                if seg.ident == "PhantomData" {
                    // Hardcoded exception, because PhantomData<T> implements
                    // Serialize and Deserialize whether or not T implements it.
                    return;
                }
            }
            if path.leading_colon.is_none() && path.segments.len() == 1 {
                let id = &path.segments[0].ident;
                if self.all_type_params.contains(id) {
                    self.relevant_type_params.insert(id.clone());
                }
            }
            for segment in &path.segments {
                self.visit_path_segment(segment);
            }
        }
  • 一段的路径才算类型参数。 T 算,T::Item、::T、a::T 都不算。
  • PhantomData 整个跳过。 只看最后一段的名字,std::marker::PhantomData<T> 和 PhantomData<T> 一样,T 不会被记下。PhantomData<T> 对任何 T 都实现了这两个 trait(见标准库类型的实现)。
  • 关联类型只认字段类型的最外层。 visit_field 只看字段类型本身是不是 T::Xxx 这样「第一段是类型参数、后面还有段」的路径,是就把整个路径记进 associated_type_usage。它不往里递归,Vec<T::Item> 里的 T::Item 就漏掉了。
  • 其余的 visit_type、visit_path_arguments 等只是遍历:数组、引用、元组、尖括号里的类型参数、fn(A) -> B 的参数和返回值都会走到。宏类型 T!() 不往里看(visit_macro 是空的),否则名叫 T 的宏会被当成类型参数。

最后按 generics.type_params() 的顺序,给 relevant_type_params 里的每个参数加一条 T: Bound,再给 associated_type_usage 里的每个路径加一条 T::Assoc: Bound。bound 加在类型参数上,不加在字段类型上。

实验:各种字段生成什么 where

src/bin/bounds.rs 里一组结构体,cargo expand 后看 impl 头:

#[derive(Serialize, Deserialize)]
struct Plain<T> {
  x: T,
}

#[derive(Serialize, Deserialize)]
struct Marker<T> {
  id: u64,
  marker: PhantomData<T>,
}

#[derive(Serialize, Deserialize)]
struct Skipped<T> {
  id: u64,
  #[serde(skip)]
  cache: T,
}

#[derive(Serialize, Deserialize)]
struct Boxed<T> {
  x: Box<T>,
}

#[derive(Serialize)]
struct Assoc<I: Iterator> {
  item: I::Item,
}

#[derive(Serialize)]
struct WithDefault<T = u8> {
  x: T,
}
类型Serialize 的 impl 头Deserialize 的 impl 头
Plain<T>impl<T> ... where T: _serde::Serializeimpl<'de, T> ... where T: _serde::Deserialize<'de>
Marker<T>impl<T> _serde::Serialize for Marker<T>,没有 whereimpl<'de, T> _serde::Deserialize<'de> for Marker<T>,没有 where
Skipped<T>没有 wherewhere T: _serde::__private229::Default
Boxed<T>where T: _serde::Serializewhere T: _serde::Deserialize<'de>
Assoc<I>impl<I: Iterator> ... where I::Item: _serde::Serialize—
WithDefault<T = u8>impl<T> _serde::Serialize for WithDefault<T> where T: _serde::Serialize—

几处要看的:

  • Skipped 的 cache 在序列化一侧不参与推断;反序列化一侧也不要 T: Deserialize,但换来了 T: Default。原因在 attr.rs 的这个 if:skip_deserializing 的字段如果容器和字段都没指定 default = "...",就把字段的 default 设成 Default::Default,requires_default 正是挑出这类字段。
  • Boxed 得到的是 T: Serialize,不是 Box<T>: Serialize。
  • I: Iterator 留在了 impl<I: Iterator> 里:原来的 bound 照抄,推断出来的写进 where。
  • Assoc 只有 I::Item: Serialize,没有 I: Serialize:I::Item 是两段的路径,visit_path 不会把 I 记进 relevant_type_params。
📝NOTE
没有蠢问题

问: 既然 Boxed<T> 真正要的是 Box<T>: Serialize,为什么不直接给每个字段类型加 bound?那样 PhantomData、关联类型这些特例都不用管了。

答: 递归类型会让 trait 求解绕不出来。src/bin/field_type_bound.rs 手写了一个「给字段类型加 bound」的 impl:struct List<T> { head: T, tail: Option<Box<List<T>>> },where T: Serialize, Option<Box<List<T>>>: Serialize。impl 本身能编译,一用就报:

error[E0275]: overflow evaluating the requirement `List<i32>: Serialize`
    --> src/bin/field_type_bound.rs:24:18
     |
  24 |   println!("{}", serde_json::to_string(&l).unwrap());
     |                  ^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = note: required for `Box<List<i32>>` to implement `Serialize`
     = note: 2 redundant requirements hidden
     = note: required for `List<i32>` to implement `Serialize`
note: required by a bound in `serde_json::to_string`
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.154/src/ser.rs:2247:17
     |
2245 | pub fn to_string<T>(value: &T) -> Result<String>
     |        --------- required by a bound in this function
2246 | where
2247 |     T: ?Sized + Serialize,
     |                 ^^^^^^^^^ required by this bound in `to_string`

For more information about this error, try `rustc --explain E0275`.
error: could not compile `exp-serde-enum1` (bin "field_type_bound") due to 1 previous error

要证明 List<i32>: Serialize,先要证明 Option<Box<List<i32>>>: Serialize,它又要 List<i32>: Serialize,绕回了起点。给类型参数加 bound 就没有这个问题:T: Serialize 不提到 List 自己。代价是推断按语法走,有时多、有时少,下一节就是两个例子。

推断错的两种情况

少了:Vec<I::Item>

#[derive(Serialize)]
struct Batch<I: Iterator> {
  items: Vec<I::Item>,
}

items 的类型最外层是 Vec,不是 I::...,associated_type_usage 里没有它;I::Item 是两段的路径,I 也不算用到。于是什么 bound 都不加,生成的函数体里 serialize_field("items", &self.items) 要求 Vec<I::Item>: Serialize,rustc 在 derive 处报错(src/bin/assoc_vec_err.rs):

error[E0277]: the trait bound `<I as Iterator>::Item: serde::Serialize` is not satisfied
    --> src/bin/assoc_vec_err.rs:3:10
     |
   3 | #[derive(Serialize)]
     |          ^^^^^^^^^ the trait `Serialize` is not implemented for `<I as Iterator>::Item`
   4 | struct Batch<I: Iterator> {
   5 |   items: Vec<I::Item>,
     |   ----- required by a bound introduced by this call
     |
     = note: for local types consider adding `#[derive(serde::Serialize)]` to your `<I as Iterator>::Item` type
     = note: for types from other crates check whether the crate offers a `serde` feature flag
     = note: required for `Vec<<I as Iterator>::Item>` to implement `Serialize`
note: required by a bound in `_serde::ser::SerializeStruct::serialize_field`
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.229/src/ser/mod.rs:1917:21
     |
1915 |     fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     |        --------------- required by a bound in this associated function
1916 |     where
1917 |         T: ?Sized + Serialize;
     |                     ^^^^^^^^^ required by this bound in `SerializeStruct::serialize_field`
     = note: this error originates in the derive macro `Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `exp-serde-enum1` (bin "assoc_vec_err") due to 1 previous error

容器上写 #[serde(bound = "I::Item: Serialize")],程序输出 {"items":[1,2]}。

📝NOTE
冷知识:关联类型的推断是 1.0.38 才加的

issue #1208(「Infer correct trait bounds for use of a type parameter associated type」)报告 struct Struct<T: Trait> { assoc: T::Assoc } 展开成 impl<T: Trait> Serialize for Struct<T>,「which fails to compile」:那时 T::Assoc 既不算用到 T,也没有别的规则,什么 bound 都不加。issue 还对比了标准库的 #[derive(Debug)],说它给出 T::Assoc: Debug 是「partially right」,同时多加了 T: Debug 又是「wrong」。PR #1213(「Support #[derive(Serialize, Deserialize)] when using associated types」)加上了上面 visit_field 里的那段和 associated_type_usage,1.0.38 的发布说明是「Infer trait bounds for use of a type parameter associated type (#1213)」。它只认字段类型本身就是 T::Assoc 的情况,Vec<T::Assoc> 至今仍要手写 bound。

多了:Id<T>

反过来的情况更常见:类型参数出现在字段里,但字段类型并不需要它实现 trait。

// A typed id: serializes as a bare number, whatever T is.
#[derive(Serialize)]
#[serde(transparent)]
struct Id<T> {
  raw: u64,
  marker: PhantomData<T>,
}

struct User; // not Serialize

#[derive(Serialize)]
struct Post<T> {
  author: Id<T>,
  title: String,
}

Id<T> 的 impl 没有 where(PhantomData 特例),对任何 T 都能序列化。可 Post<T> 的 author: Id<T> 里出现了一段的路径 T,于是得到 where T: Serialize。Post<User> 用不了(src/bin/too_strict.rs):

error[E0277]: the trait bound `User: serde::Serialize` is not satisfied
    --> src/bin/too_strict.rs:23:40
     |
  23 |   println!("{}", serde_json::to_string(&p).unwrap());
     |                  --------------------- ^^ unsatisfied trait bound
     |                  |
     |                  required by a bound introduced by this call
     |
help: the trait `Serialize` is not implemented for `User`
    --> src/bin/too_strict.rs:13:1
     |
  13 | struct User; // not Serialize
     | ^^^^^^^^^^^
     = note: for local types consider adding `#[derive(serde::Serialize)]` to your `User` type
     = note: for types from other crates check whether the crate offers a `serde` feature flag
     = help: the following other types implement trait `Serialize`:
               &'a T
               &'a mut T
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
               (T0, T1, T2, T3, T4)
             and 135 others
note: required for `Post<User>` to implement `Serialize`
    --> src/bin/too_strict.rs:16:8
     |
  15 | #[derive(Serialize)]
     |          --------- type parameter would need to implement `Serialize`
  16 | struct Post<T> {
     |        ^^^^^^^
     = help: consider manually implementing `Serialize` to avoid undesired bounds caused by "imperfect derives"
     = note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `serde_json::to_string`
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.154/src/ser.rs:2247:17
     |
2245 | pub fn to_string<T>(value: &T) -> Result<String>
     |        --------- required by a bound in this function
2246 | where
2247 |     T: ?Sized + Serialize,
     |                 ^^^^^^^^^ required by this bound in `to_string`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `exp-serde-enum1` (bin "too_strict") due to 1 previous error

这次错误不在 derive 处,而在使用处:impl 本身是对的,只是条件比需要的严。rustc 的提示里直接点了名:「imperfect derives」,链接的 rust-lang/rust#26925 是标准库 derive 的同一个问题。给 Post 加 #[serde(bound = "")],impl 头变成 impl<T> _serde::Serialize for Post<T>,输出 {"author":7,"title":"hi"}(src/bin/too_strict_fixed.rs)。

❗IMPORTANT
动脑筋:写成 <I as Iterator>::Item 会怎样?

src/bin/quiz_qself.rs 里两个结构体只差一种写法:

#[derive(Serialize)]
struct Short<I: Iterator> {
  item: I::Item,
}

#[derive(Serialize)]
struct Long<I: Iterator> {
  item: <I as Iterator>::Item,
}

main 里对 Short::<std::vec::IntoIter<u8>> 和 Long::<std::vec::IntoIter<u8>> 各调一次 serde_json::to_string。Short 的 impl 头是 where I::Item: _serde::Serialize。Long 的是什么?这个程序能编译吗?

属性 bound:三个位置,两个方向

#[serde(bound = "...")] 可以写在容器、变体、字段上,也可以分方向写成 bound(serialize = "...", deserialize = "...")。三个位置的效果不同:

写在哪效果
容器替换掉全部推断(包括 Self: Default 和 T: Default),只用你写的
变体这个变体里的字段都不参与推断,你写的那几条追加进 where
字段这个字段不参与推断,你写的那几条追加进 where

src/bin/field_bound.rs:

#[derive(Serialize, Deserialize)]
struct Tagged<T, U> {
  #[serde(bound = "")]
  author: Id<T>,
  extra: U,
}

#[derive(Deserialize)]
#[serde(bound(deserialize = "T: Deserialize<'de> + Default"))]
struct Custom<T> {
  x: T,
}

展开后 Tagged 两个 impl 的 where 都只有 U:U: _serde::Serialize 和 U: _serde::Deserialize<'de>,author 写了空的 bound,不再给 T 加任何东西。Custom 的 impl 头是:

    impl<'de, T> _serde::Deserialize<'de> for Custom<T>
    where
        T: Deserialize<'de> + Default,
    {

手写的 bound 里可以直接用 'de:生成的 impl 恰好把这个生命周期参数叫 'de,字符串被原样解析成 WherePredicate 放进去。也因此结构体自己不能有叫 'de 的生命周期参数,precondition_no_de_lifetime 会拦下(src/bin/de_named.rs):

error: cannot deserialize when there is a lifetime parameter called 'de
 --> src/bin/de_named.rs:4:12
  |
4 | struct Doc<'de> {
  |            ^^^

error: could not compile `exp-serde-enum1` (bin "de_named") due to 1 previous error

Self: Default 与 '__a

另外两个小函数也在 bound.rs 里。with_self_bound 给整个类型加一条 bound,只用在容器的 #[serde(default)] 上(src/bin/self_bound.rs):

#[derive(Deserialize, Default)]
#[serde(default)]
struct Config<T> {
  level: T,
  name: String,
}
    impl<'de, T> _serde::Deserialize<'de> for Config<T>
    where
        Config<T>: _serde::__private229::Default,
        T: _serde::Deserialize<'de>,
    {

容器级的 default 让生成的 visit_map 和 visit_seq 一开始就执行 let __default: Self::Value = _serde::__private229::Default::default();,缺的字段从 __default.level 这样的位置取,要求的就是 Config<T>: Default。写成 T: Default 是猜:#[derive(Default)] 恰好要 T: Default,手写的 impl<T> Default for Config<T> 可能不要。这里写类型本身不会绕圈:要证明的是 Config<T>: Default,和正在实现的 Deserialize 不是同一个 trait。类型名由 type_of_item 拼出来,遇到 const 泛型参数会 panic!("Serde does not support const generics yet");with_bound 推断时只看 type_params(),const 参数不参与。

with_lifetime_bound 在参数表最前面插一个新的生命周期,并让原有的每个生命周期和类型参数都活得比它长。序列化一侧用它给几个「借用字段的包装类型」加 '__a,比如 serialize_with 的 __SerializeWith:

fn as_len<S: Serializer, T: AsRef<[u8]>>(v: &T, s: S) -> Result<S::Ok, S::Error> {
  s.serialize_u64(v.as_ref().len() as u64)
}

#[derive(Serialize)]
enum Msg<'a, T: AsRef<[u8]>> {
  Data(#[serde(serialize_with = "as_len")] T),
  Text(&'a str),
}
                            struct __SerializeWith<
                                '__a,
                                'a: '__a,
                                T: AsRef<[u8]> + '__a,
                            > {
                                values: (&'__a T,),
                                phantom: _serde::__private229::PhantomData<Msg<'a, T>>,

包装类型里存的是 &'__a T,T: '__a 是这个引用合法的前提。Msg 本身的 impl 是 impl<'a, T: AsRef<[u8]>> _serde::Serialize for Msg<'a, T>,没有 where:Data 的字段交给了 serialize_with,Text 的字段里没有类型参数。

'de 与借用的生命周期

反序列化的 impl 比序列化多一个生命周期参数。它由 borrowed_lifetimes 决定:

// The union of lifetimes borrowed by each field of the container.
//
// These turn into bounds on the `'de` lifetime of the Deserialize impl. If
// lifetimes `'a` and `'b` are borrowed but `'c` is not, the impl is:
//
//     impl<'de: 'a + 'b, 'a, 'b, 'c> Deserialize<'de> for S<'a, 'b, 'c>
//
// If any borrowed lifetime is `'static`, then `'de: 'static` would be redundant
// and we use plain `'static` instead of `'de`.
fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
    let mut lifetimes = BTreeSet::new();
    for field in cont.data.all_fields() {
        if !field.attrs.skip_deserializing() {
            lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned());
        }
    }
    if lifetimes.iter().any(|b| b.to_string() == "'static") {
        BorrowedLifetimes::Static
    } else {
        BorrowedLifetimes::Borrowed(lifetimes)
    }
}

每个字段借哪些生命周期(&'a str 隐式借,Cow<'a, str> 要写 #[serde(borrow)])是 attr.rs 算的,规则在生命周期 'de 与借用。这里只做并集,结果是 BorrowedLifetimes 的两种之一:Borrowed(集合) 生成 'de: 'a + 'b,Static 不生成 'de,整个 impl 改用 'static。

插到哪里由两个 ToTokens 包装决定。DeImplGenerics 用在 impl<...> 的位置,把带约束的 'de: 'a 放在参数表最前面;de_type_generics_to_tokens 用在生成的 __Visitor<...> 的类型位置,只放一个光秃秃的 'de。bounds.rs 里两个对照:

#[derive(Deserialize)]
struct Borrowed<'a> {
  s: &'a str,
}

#[derive(Deserialize)]
struct Static {
  s: &'static str,
}
    impl<'de: 'a, 'a> _serde::Deserialize<'de> for Borrowed<'a> {
            // ...
            struct __Visitor<'de: 'a, 'a> {
                marker: _serde::__private229::PhantomData<Borrowed<'a>>,
                lifetime: _serde::__private229::PhantomData<&'de ()>,
            }
            #[automatically_derived]
            impl<'de: 'a, 'a> _serde::de::Visitor<'de> for __Visitor<'de, 'a> {
    impl _serde::Deserialize<'static> for Static {
            // ...
            struct __Visitor {
                marker: _serde::__private229::PhantomData<Static>,
                lifetime: _serde::__private229::PhantomData<&'static ()>,
            }
            #[automatically_derived]
            impl _serde::de::Visitor<'static> for __Visitor {

没有借用字段的类型也有 'de:borrowed_lifetimes 返回一个空集合的 BorrowedLifetimes::Borrowed,照样生成不带约束的 'de(前面的 impl<'de, T> ... for Plain<T>),__Visitor<'de> 靠 PhantomData<&'de ()> 用上这个参数。

Static 只实现了 Deserialize<'static>,所以输入也必须活到 'static。src/bin/static_str.rs 用一个字面量能通过,换成 String 的借用就不行:

error[E0597]: `owned` does not live long enough
    --> src/bin/static_str.rs:12:40
     |
  11 |   let owned = String::from(r#"{"s":"from a String"}"#);
     |       ----- binding `owned` declared here
  12 |   let b: Static = serde_json::from_str(&owned).unwrap();
     |                   ---------------------^^^^^^-
     |                   |                    |
     |                   |                    borrowed value does not live long enough
     |                   argument requires that `owned` is borrowed for `'static`
  13 |   println!("{b:?}");
  14 | }
     | - `owned` dropped here while still borrowed
     |
note: requirement that the value outlives `'static` introduced here
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.154/src/de.rs:2711:8
     |
2711 |     T: de::Deserialize<'a>,
     |        ^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0597`.
error: could not compile `exp-serde-enum1` (bin "static_str") due to 1 previous error

'static 单独处理是 1.0.14 的事(PR #1052,修 issue #975):之前展开成 impl<'de: 'static> Deserialize<'de>,rustc 警告「unnecessary lifetime parameter 'de」;发布说明是「Generate code without warnings when a struct contains a &'static field (#1052)」。

Self:先换成类型名

字段类型里可以写 Self:tail: Option<Box<Self>>、[u8; Self::LEN]。生成的代码把字段类型抄进 __Visitor 的方法里,比如 next_value::<Option<Box<Self>>>(),而那里的 Self 是 __Visitor,不是 List。所以 expand_derive_deserialize 和 expand_derive_serialize 的第一行都是 replace_receiver(input):

pub fn replace_receiver(input: &mut DeriveInput) {
    let self_ty = {
        let ident = &input.ident;
        let ty_generics = input.generics.split_for_impl().1;
        parse_quote!(#ident #ty_generics)
    };
    let mut visitor = ReplaceReceiver(&self_ty);
    visitor.visit_generics_mut(&mut input.generics);
    visitor.visit_data_mut(&mut input.data);
}

它在 derive 拿到的那份输入上原地改写:类型位置的 Self 换成 List(带上泛型参数),Self::LEN 这样的路径换成 <Fixed>::LEN。改的是 derive 自己的副本,用户写的结构体定义不变。src/bin/receiver.rs:

#[derive(Serialize, Deserialize, Debug)]
struct Fixed([u8; Self::LEN]);

impl Fixed {
  const LEN: usize = 2;
}

#[derive(Serialize, Deserialize, Debug)]
struct List {
  head: u8,
  tail: Option<Box<Self>>,
}

展开后的 Deserialize 里是 [u8; <Fixed>::LEN] 和 Option<Box<List>>:

                    let __field0: [u8; <Fixed>::LEN] = <[u8; <Fixed>::LEN] as _serde::Deserialize>::deserialize(
                    // ...
                    let mut __field1: _serde::__private229::Option<Option<Box<List>>> = _serde::__private229::None;

两个都能正确反序列化,输出 Fixed([1, 2]) 和 List { head: 1, tail: Some(List { head: 2, tail: None }) }。

📝NOTE
冷知识:Self 字段是 1.0.123 才能 derive 的

issue #1506(「Deserilize derive does not work with Self in type definition」)的例子正是 struct Foo([u8; Self::MAX_LENGTH]);,报错是 no associated item named `MAX_LENGTH` found,找的类型是生成的 __Visitor<'de>:Self 落进了 impl Visitor for __Visitor 里面。issue 里还说「If we change Self::MAX_LENGTH to Foo::MAX_LENGTH it works fine. Serialization also works fine.」PR #1830(「Fix handling of Self keyword in type definition」)加了 receiver.rs,说明里写它的访问逻辑「is the same as for async-trait's ReplaceReceiver」,同时修了 #1565(「Derive Deserialize: Self in recursive type is not supported」)。序列化一侧的问题出在 serialize_with:它生成的 __SerializeWith 结构体也抄字段类型,结构体定义里的 Self 指的是 __SerializeWith。PR #1970(「Substitute Self in output of Serialize derive」)让 Serialize 也先调 replace_receiver。两者都在 1.0.123,发布说明是「Support Self keywords in fields of types that derive Deserialize (#1830)」和「Support Self inside fields that use serialize_with (#1970)」。

this.rs 是同一类问题的另一面:生成代码要写出「这个类型」时用 this_type(类型位置)和 this_value(表达式位置,泛型参数前要 ::)。本地类型就是结构体名;#[serde(remote = "...")] 时换成远程类型的路径,this_value 给尖括号补上 turbofish。前面展开里的 PhantomData<Borrowed<'a>>、type Value = Fixed; 都出自这里。

动脑筋答案

Long 的 impl 头是:

    impl<I: Iterator> _serde::Serialize for Long<I>
    where
        I: _serde::Serialize,
    {

程序不能编译,cargo build 的完整输出:

error[E0277]: the trait bound `<I as Iterator>::Item: serde::Serialize` is not satisfied
    --> src/bin/quiz_qself.rs:8:10
     |
   8 | #[derive(Serialize)]
     |          ^^^^^^^^^ the trait `Serialize` is not implemented for `<I as Iterator>::Item`
   9 | struct Long<I: Iterator> {
  10 |   item: <I as Iterator>::Item,
     |   ---- required by a bound introduced by this call
     |
     = note: for local types consider adding `#[derive(serde::Serialize)]` to your `<I as Iterator>::Item` type
     = note: for types from other crates check whether the crate offers a `serde` feature flag
note: required by a bound in `_::_serde::ser::SerializeStruct::serialize_field`
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_core-1.0.229/src/ser/mod.rs:1917:21
     |
1915 |     fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
     |        --------------- required by a bound in this associated function
1916 |     where
1917 |         T: ?Sized + Serialize;
     |                     ^^^^^^^^^ required by this bound in `SerializeStruct::serialize_field`
     = note: this error originates in the derive macro `Serialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `std::vec::IntoIter<u8>: serde::Serialize` is not satisfied
    --> src/bin/quiz_qself.rs:17:40
     |
  17 |   println!("{}", serde_json::to_string(&b).unwrap());
     |                  --------------------- ^^ the trait `Serialize` is not implemented for `std::vec::IntoIter<u8>`
     |                  |
     |                  required by a bound introduced by this call
     |
     = note: for local types consider adding `#[derive(serde::Serialize)]` to your `std::vec::IntoIter<u8>` type
     = note: for types from other crates check whether the crate offers a `serde` feature flag
     = help: the following other types implement trait `Serialize`:
               &'a T
               &'a mut T
               ()
               (T,)
               (T0, T1)
               (T0, T1, T2)
               (T0, T1, T2, T3)
               (T0, T1, T2, T3, T4)
             and 135 others
note: required for `Long<std::vec::IntoIter<u8>>` to implement `Serialize`
    --> src/bin/quiz_qself.rs:9:8
     |
   8 | #[derive(Serialize)]
     |          --------- type parameter would need to implement `Serialize`
   9 | struct Long<I: Iterator> {
     |        ^^^^^^^^^^^^^^^^^
     = help: consider manually implementing `Serialize` to avoid undesired bounds caused by "imperfect derives"
     = note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `serde_json::to_string`
    --> ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/serde_json-1.0.154/src/ser.rs:2247:17
     |
2245 | pub fn to_string<T>(value: &T) -> Result<String>
     |        --------- required by a bound in this function
2246 | where
2247 |     T: ?Sized + Serialize,
     |                 ^^^^^^^^^ required by this bound in `to_string`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `exp-serde-enum1` (bin "quiz_qself") due to 2 previous errors

原因在 FindTyParams:<I as Iterator>::Item 在 syn 里是 qself = I、path = Iterator::Item。visit_field 看的是 path 的第一段 Iterator,不是类型参数,于是不记关联类型;visit_type 遇到 qself 会先 visit_type(&qself.ty),走到一段的路径 I,记下 I。结果给错了对象:要的是 Item: Serialize,加的是 I: Serialize。函数体里的 serialize_field 仍然要 Item: Serialize,所以 derive 处报一次;I 是 IntoIter<u8>,它不实现 Serialize,使用处再报一次。Short 那一行本身没有错误。

小结

  • impl 头由 build_generics 决定:去掉默认值,追加字段和变体上写的 bound;容器写了 bound 就只用它,否则按字段推断。
  • 推断按语法:一段的路径 T 才算用到;PhantomData 整个跳过;skip、serialize_with / deserialize_with 的字段不参与;关联类型只认字段类型本身是 T::Assoc 的情况。bound 加在类型参数上而不是字段类型上,这样递归类型不会让 trait 求解绕圈。
  • 推断会少(Vec<I::Item>,derive 处报错)也会多(Id<T>,使用处报错),都用 #[serde(bound = "...")] 修;手写的 bound 里可以用 'de,类型也因此不能有叫 'de 的生命周期参数。
  • 反序列化一侧多三样:T: Deserialize<'de>、容器 default 的 Self: Default、要填默认值的字段的 T: Default。
  • 借用的生命周期取并集,成为 'de: 'a + 'b;有 'static 就整个换成 Deserialize<'static>。
  • 字段里的 Self 在生成代码之前就被 replace_receiver 换成了具体的类型名,否则会指向生成的 __Visitor。

💬 关联讨论

发起讨论
加载中...