泛型与 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),
),
}
}
四步:
without_defaults去掉类型参数的默认值。struct WithDefault<T = u8>展开成impl<T> _serde::Serialize for WithDefault<T>,注释说留着默认值会报「associated type bindings are not allowed here」。- 字段上的
#[serde(bound = "...")]和变体上的#[serde(bound = "...")]原样追加到where里(with_where_predicates_from_fields、with_where_predicates_from_variants)。 - 容器上写了
bound,就把它原样放进where(with_where_predicates),不再推断。 - 没写,就调
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::Serialize | impl<'de, T> ... where T: _serde::Deserialize<'de> |
Marker<T> | impl<T> _serde::Serialize for Marker<T>,没有 where | impl<'de, T> _serde::Deserialize<'de> for Marker<T>,没有 where |
Skipped<T> | 没有 where | where T: _serde::__private229::Default |
Boxed<T> | where T: _serde::Serialize | where 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。
问: 既然 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]}。
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)。
<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 }) }。
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。