// Figure out what impl needs to be generated for the Debug impl of Field<T>.
// This will involve adding a trait bound to the T type parameter of the
// generated impl.
//
// Callers should be free to instantiate Field<T> with a type parameter T which
// does not implement Debug, but such a Field<T> will not fulfill the trait
// bounds of the generated Debug impl and so will not be printable via Debug.
//
//
// Resources:
//
// - Representation of generics in the Syn syntax tree:
// https://docs.rs/syn/1.0/syn/struct.Generics.html
//
// - A helper for placing generics into an impl signature:
// https://docs.rs/syn/1.0/syn/struct.Generics.html#method.split_for_impl
//
// - Example code from Syn which deals with type parameters:
// https://github.com/dtolnay/syn/tree/master/examples/heapsize
use derive_debug::CustomDebug;
#[derive(CustomDebug)]
pub struct Field<T> {
value: T,
#[debug = "0b{:08b}"]
bitmask: u8,
}
fn main() {
let f = Field {
value: "F",
bitmask: 0b00011100,
};
let debug = format!("{:?}", f);
let expected = r#"Field { value: "F", bitmask: 0b00011100 }"#;
assert_eq!(debug, expected);
}
這道題涉及到了泛型,主要問題是:如何限定泛型。
// common.rs
pub(crate) fn parse_generic_type(ast: &syn::DeriveInput) -> syn::Generics {
return ast.generics.clone();
}
這次的關鍵點並不是抽取
// solution4.rs
pub(super) fn solution(
fields: &crate::common::FieldsType,
origin_ident: &syn::Ident,
ast: &syn::DeriveInput,
) -> syn::Result<proc_macro2::TokenStream> {
let mut generic = crate::common::parse_generic_type(ast);
// 相關泛型要限定
for g in generic.params.iter_mut() {
if let syn::GenericParam::Type(t) = g {
t.bounds.push(syn::parse_quote!(std::fmt::Debug));
}
}
// 提取關鍵三處的泛型宣告
let (impl_generics, type_generics, where_clause) = generic.split_for_impl();
let origin_ident_string = origin_ident.to_string();
let debug_field_vec = debug_body_vec(fields)?;
syn::Result::Ok(quote::quote! {
// 填充泛型宣告
impl #impl_generics std::fmt::Debug for #origin_ident #type_generics #where_clause {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct(#origin_ident_string)
#(
#debug_field_vec
)*
.finish()
}
}
})
}
// 因為body不變,為了突出邏輯變更,這裡就抽取一下
fn debug_body_vec(
fields: &crate::common::FieldsType,
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
fields
.iter()
.map(|f| {
let ident = &f.ident;
let ident_string = ident.as_ref().unwrap().to_string();
let mut format = "{:?}".to_string();
if let std::option::Option::Some(customer_format) = crate::common::parse_format(f)? {
format = customer_format;
}
syn::Result::Ok(quote::quote! {
.field(#ident_string, &format_args!(#format, &self.#ident))
})
})
.collect()
}
大體上並沒有更多變化,主要是兩點
- 繫結泛型宣告
- 提取泛型宣告
關注一下split_for_impl
使用即可
fn solution1(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let origin_ident = &ast.ident;
let fields = crate::common::parse_fields(&ast)?;
// soluton2
let _ = solution2::solution(fields, origin_ident)?;
// solution3
let _ = solution3::solution(fields, origin_ident)?;
// solution4
let token_stream = solution4::solution(fields, origin_ident, ast)?;
syn::Result::Ok(token_stream)
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結